Add square roots (\sqrt)
Summary: Follow the TeXbook instructions on how to construct square roots. Using makeCustomSizedDelim, this becomes nearly trivial. Test Plan: - Make sure normal tests work - Make sure the new huxley test looks good, and other huxley tests haven't changed. Reviewers: alpert Reviewed By: alpert Differential Revision: http://phabricator.khanacademy.org/D12918
This commit is contained in:
parent
5a94faac9e
commit
925c96dbe2
13
Parser.js
13
Parser.js
|
@ -535,6 +535,19 @@ Parser.prototype.parseNucleus = function(pos, mode) {
|
|||
this.lexer, nucleus.position
|
||||
);
|
||||
}
|
||||
} else if (mode === "math" && nucleus.type === "\\sqrt") {
|
||||
// If this is a square root, parse its argument and return
|
||||
var group = this.parseGroup(nucleus.position, mode);
|
||||
if (group) {
|
||||
return new ParseResult(
|
||||
new ParseNode("sqrt", group, mode),
|
||||
group.position);
|
||||
} else {
|
||||
throw new ParseError("Expected group after '" +
|
||||
nucleus.type + "'",
|
||||
this.lexer, nucleus.position
|
||||
);
|
||||
}
|
||||
} else if (mode === "math" && nucleus.type === "\\rule") {
|
||||
// Parse the width of the rule
|
||||
var widthGroup = this.parseSizeGroup(nucleus.position, mode);
|
||||
|
|
65
buildTree.js
65
buildTree.js
|
@ -38,7 +38,8 @@ var groupToType = {
|
|||
katex: "mord",
|
||||
overline: "mord",
|
||||
rule: "mord",
|
||||
leftright: "minner"
|
||||
leftright: "minner",
|
||||
sqrt: "mord"
|
||||
};
|
||||
|
||||
var getTypeOfGroup = function(group) {
|
||||
|
@ -429,6 +430,68 @@ var groupTypes = {
|
|||
["katex-logo"], [k, a, t, e, x], options.getColor());
|
||||
},
|
||||
|
||||
sqrt: function(group, options, prev) {
|
||||
var innerGroup = buildGroup(group.value.result,
|
||||
options.withStyle(options.style.cramp()));
|
||||
|
||||
var fontSizer = buildCommon.makeFontSizer(
|
||||
options, Math.max(innerGroup.maxFontSize, 1.0));
|
||||
|
||||
// The theta variable in the TeXbook
|
||||
var lineWidth = fontMetrics.metrics.defaultRuleThickness;
|
||||
|
||||
var lineInner =
|
||||
makeSpan([options.style.reset(), Style.TEXT.cls(), "line"]);
|
||||
lineInner.maxFontSize = 1.0;
|
||||
var line = makeSpan(["sqrt-line"], [fontSizer, lineInner]);
|
||||
|
||||
var inner = makeSpan(["sqrt-inner"], [fontSizer, innerGroup]);
|
||||
var fixIE = makeSpan(
|
||||
["fix-ie"], [fontSizer, new domTree.textNode("\u00a0")]);
|
||||
|
||||
var theta = fontMetrics.metrics.defaultRuleThickness /
|
||||
options.style.sizeMultiplier;
|
||||
var phi = theta;
|
||||
if (options.style.id < Style.TEXT.id) {
|
||||
phi = fontMetrics.metrics.xHeight;
|
||||
}
|
||||
|
||||
var psi = theta + phi / 4;
|
||||
|
||||
var innerHeight =
|
||||
(inner.height + inner.depth) * options.style.sizeMultiplier;
|
||||
var minDelimiterHeight = innerHeight + psi + theta;
|
||||
|
||||
var delim = makeSpan(["sqrt-sign"], [
|
||||
delimiter.customSizedDelim("\\surd", minDelimiterHeight,
|
||||
false, options, group.mode)]);
|
||||
|
||||
var delimDepth = delim.height + delim.depth;
|
||||
|
||||
if (delimDepth > inner.height + inner.depth + psi) {
|
||||
psi = (psi + delimDepth - inner.height - inner.depth) / 2;
|
||||
}
|
||||
|
||||
delim.style.top = (-inner.height - psi + delim.height - theta) + "em";
|
||||
|
||||
line.style.top = (-inner.height - psi) + "em";
|
||||
line.height = inner.height + psi + 2 * theta;
|
||||
|
||||
// We add a special case here, because even when `inner` is empty, we
|
||||
// still get a line. So, we use a simple heuristic to decide if we
|
||||
// should omit the body entirely. (note this doesn't work for something
|
||||
// like `\sqrt{\rlap{x}}`, but if someone is doing that they deserve for
|
||||
// it not to work.
|
||||
var body;
|
||||
if (inner.height === 0 && inner.depth === 0) {
|
||||
body = makeSpan();
|
||||
} else {
|
||||
body = makeSpan(["sqrt-body"], [line, inner, fixIE]);
|
||||
}
|
||||
|
||||
return makeSpan(["sqrt", "mord"], [delim, body]);
|
||||
},
|
||||
|
||||
overline: function(group, options, prev) {
|
||||
var innerGroup = buildGroup(group.value.result,
|
||||
options.withStyle(options.style.cramp()));
|
||||
|
|
|
@ -191,6 +191,12 @@ var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
|
|||
bottom = "\u23ad";
|
||||
repeat = "\u23aa";
|
||||
font = "Size4-Regular";
|
||||
} else if (delim === "\\surd") {
|
||||
top = "\ue001";
|
||||
bottom = "\u23b7";
|
||||
repeat = "\ue000";
|
||||
font = "Size4-Regular";
|
||||
overlap = true;
|
||||
}
|
||||
|
||||
// Get the metrics of the three sections
|
||||
|
@ -312,7 +318,8 @@ var normalDelimiters = [
|
|||
"(", ")", "[", "\\lbrack", "]", "\\rbrack",
|
||||
"\\{", "\\lbrace", "\\}", "\\rbrace",
|
||||
"\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
|
||||
"<", ">", "\\langle", "\\rangle", "/", "\\backslash"
|
||||
"<", ">", "\\langle", "\\rangle", "/", "\\backslash",
|
||||
"\\surd"
|
||||
];
|
||||
|
||||
var stackDelimiters = [
|
||||
|
|
|
@ -350,6 +350,46 @@ big parens
|
|||
}
|
||||
}
|
||||
|
||||
.sqrt {
|
||||
> .sqrt-sign {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> .sqrt-body {
|
||||
.baseline-align-hack-outer;
|
||||
|
||||
> .sqrt-line,
|
||||
> .sqrt-inner,
|
||||
> .fix-ie {
|
||||
.baseline-align-hack-middle;
|
||||
position: relative;
|
||||
|
||||
> span {
|
||||
.baseline-align-hack-inner;
|
||||
}
|
||||
}
|
||||
|
||||
> .sqrt-line > .line {
|
||||
width: 100%;
|
||||
|
||||
&:before {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
content: "";
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 0.04em;
|
||||
content: "";
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sizing, .fontsize-ensurer {
|
||||
display: inline-block;
|
||||
|
||||
|
|
|
@ -298,6 +298,11 @@ var symbols = {
|
|||
group: "bin",
|
||||
replace: "\u00d7"
|
||||
},
|
||||
"\\surd": {
|
||||
font: "main",
|
||||
group: "textord",
|
||||
replace: "\u221a"
|
||||
},
|
||||
"(": {
|
||||
font: "main",
|
||||
group: "open"
|
||||
|
|
|
@ -153,5 +153,11 @@
|
|||
"name": "NullDelimiterInteraction",
|
||||
"screenSize": [1024, 768],
|
||||
"url": "http://localhost:7936/test/huxley/test.html?m=a \\bigl. + 2 \\quad \\left. + a \\right)"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Sqrt",
|
||||
"screenSize": [1024, 768],
|
||||
"url": "http://localhost:7936/test/huxley/test.html?m=\\sqrt{\\sqrt{\\sqrt{x}}}_{\\sqrt{\\sqrt{x}}}^{\\sqrt{\\sqrt{\\sqrt{x}}}^{\\sqrt{\\sqrt{\\sqrt{x}}}}}"
|
||||
}
|
||||
]
|
||||
|
|
BIN
test/huxley/Sqrt.hux/firefox-1.png
Normal file
BIN
test/huxley/Sqrt.hux/firefox-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
5
test/huxley/Sqrt.hux/record.json
Normal file
5
test/huxley/Sqrt.hux/record.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
[
|
||||
{
|
||||
"action": "screenshot"
|
||||
}
|
||||
]
|
|
@ -798,3 +798,26 @@ describe("A left/right parser", function() {
|
|||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("A sqrt parser", function() {
|
||||
var sqrt = "\\sqrt{x}";
|
||||
var missingGroup = "\\sqrt";
|
||||
|
||||
it("should parse square roots", function() {
|
||||
expect(function() {
|
||||
parseTree(sqrt);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it("should error when there is no group", function() {
|
||||
expect(function() {
|
||||
parseTree(missingGroup);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it("should produce sqrts", function() {
|
||||
var parse = parseTree(sqrt)[0];
|
||||
|
||||
expect(parse.type).toMatch("sqrt");
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user