mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-15 15:58:40 +00:00
Add support for \{,d,t}binom
Test Plan: `\binom xy^{\binom xy^{\binom xy}}` looks like something. `\dbinom` and `\tbinom` also seem to work. Reviewers: emily Reviewed By: emily Subscribers: jessie Differential Revision: http://phabricator.khanacademy.org/D13315
This commit is contained in:
106
src/buildTree.js
106
src/buildTree.js
@@ -364,14 +364,14 @@ var groupTypes = {
|
|||||||
[base, supsub]);
|
[base, supsub]);
|
||||||
},
|
},
|
||||||
|
|
||||||
frac: function(group, options, prev) {
|
genfrac: function(group, options, prev) {
|
||||||
// Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
|
// Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
|
||||||
// Figure out what style this fraction should be in based on the
|
// Figure out what style this fraction should be in based on the
|
||||||
// function used
|
// function used
|
||||||
var fstyle = options.style;
|
var fstyle = options.style;
|
||||||
if (group.value.size === "dfrac") {
|
if (group.value.size === "display") {
|
||||||
fstyle = Style.DISPLAY;
|
fstyle = Style.DISPLAY;
|
||||||
} else if (group.value.size === "tfrac") {
|
} else if (group.value.size === "text") {
|
||||||
fstyle = Style.TEXT;
|
fstyle = Style.TEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,30 +384,55 @@ var groupTypes = {
|
|||||||
var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
|
var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
|
||||||
var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]);
|
var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]);
|
||||||
|
|
||||||
var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
|
var ruleWidth;
|
||||||
|
if (group.value.hasBarLine) {
|
||||||
|
ruleWidth = fontMetrics.metrics.defaultRuleThickness /
|
||||||
options.style.sizeMultiplier;
|
options.style.sizeMultiplier;
|
||||||
|
|
||||||
var mid = makeSpan(
|
|
||||||
[options.style.reset(), Style.TEXT.cls(), "frac-line"]);
|
|
||||||
// Manually set the height of the line because its height is created in
|
|
||||||
// CSS
|
|
||||||
mid.height = ruleWidth;
|
|
||||||
|
|
||||||
// Rule 15b, 15d
|
|
||||||
var numShift, denomShift, clearance;
|
|
||||||
if (fstyle.size === Style.DISPLAY.size) {
|
|
||||||
numShift = fontMetrics.metrics.num1;
|
|
||||||
denomShift = fontMetrics.metrics.denom1;
|
|
||||||
clearance = 3 * ruleWidth;
|
|
||||||
} else {
|
} else {
|
||||||
numShift = fontMetrics.metrics.num2;
|
ruleWidth = 0;
|
||||||
denomShift = fontMetrics.metrics.denom2;
|
|
||||||
clearance = ruleWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rule 15b
|
||||||
|
var numShift;
|
||||||
|
var clearance;
|
||||||
|
var denomShift;
|
||||||
|
if (fstyle.size === Style.DISPLAY.size) {
|
||||||
|
numShift = fontMetrics.metrics.num1;
|
||||||
|
if (ruleWidth > 0) {
|
||||||
|
clearance = 3 * ruleWidth;
|
||||||
|
} else {
|
||||||
|
clearance = 7 * fontMetrics.metrics.defaultRuleThickness;
|
||||||
|
}
|
||||||
|
denomShift = fontMetrics.metrics.denom1;
|
||||||
|
} else {
|
||||||
|
if (ruleWidth > 0) {
|
||||||
|
numShift = fontMetrics.metrics.num2;
|
||||||
|
clearance = ruleWidth;
|
||||||
|
} else {
|
||||||
|
numShift = fontMetrics.metrics.num3;
|
||||||
|
clearance = 3 * fontMetrics.metrics.defaultRuleThickness;
|
||||||
|
}
|
||||||
|
denomShift = fontMetrics.metrics.denom2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var frac;
|
||||||
|
if (ruleWidth === 0) {
|
||||||
|
// Rule 15c
|
||||||
|
var candiateClearance =
|
||||||
|
(numShift - numer.depth) - (denom.height - denomShift);
|
||||||
|
if (candiateClearance < clearance) {
|
||||||
|
numShift += 0.5 * (clearance - candiateClearance);
|
||||||
|
denomShift += 0.5 * (clearance - candiateClearance);
|
||||||
|
}
|
||||||
|
|
||||||
|
frac = buildCommon.makeVList([
|
||||||
|
{type: "elem", elem: denomreset, shift: denomShift},
|
||||||
|
{type: "elem", elem: numerreset, shift: -numShift}
|
||||||
|
], "individualShift", null, options);
|
||||||
|
} else {
|
||||||
|
// Rule 15d
|
||||||
var axisHeight = fontMetrics.metrics.axisHeight;
|
var axisHeight = fontMetrics.metrics.axisHeight;
|
||||||
|
|
||||||
// Rule 15d
|
|
||||||
if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth)
|
if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth)
|
||||||
< clearance) {
|
< clearance) {
|
||||||
numShift +=
|
numShift +=
|
||||||
@@ -422,22 +447,55 @@ var groupTypes = {
|
|||||||
(denom.height - denomShift));
|
(denom.height - denomShift));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mid = makeSpan(
|
||||||
|
[options.style.reset(), Style.TEXT.cls(), "frac-line"]);
|
||||||
|
// Manually set the height of the line because its height is
|
||||||
|
// created in CSS
|
||||||
|
mid.height = ruleWidth;
|
||||||
|
|
||||||
var midShift = -(axisHeight - 0.5 * ruleWidth);
|
var midShift = -(axisHeight - 0.5 * ruleWidth);
|
||||||
|
|
||||||
var frac = buildCommon.makeVList([
|
frac = buildCommon.makeVList([
|
||||||
{type: "elem", elem: denomreset, shift: denomShift},
|
{type: "elem", elem: denomreset, shift: denomShift},
|
||||||
{type: "elem", elem: mid, shift: midShift},
|
{type: "elem", elem: mid, shift: midShift},
|
||||||
{type: "elem", elem: numerreset, shift: -numShift}
|
{type: "elem", elem: numerreset, shift: -numShift}
|
||||||
], "individualShift", null, options);
|
], "individualShift", null, options);
|
||||||
|
}
|
||||||
|
|
||||||
// Since we manually change the style sometimes (with \dfrac or \tfrac),
|
// Since we manually change the style sometimes (with \dfrac or \tfrac),
|
||||||
// account for the possible size change here.
|
// account for the possible size change here.
|
||||||
frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
||||||
frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
|
||||||
|
|
||||||
|
// Rule 15e
|
||||||
|
var innerChildren = [makeSpan(["mfrac"], [frac])];
|
||||||
|
|
||||||
|
var delimSize;
|
||||||
|
if (fstyle.size === Style.DISPLAY.size) {
|
||||||
|
delimSize = fontMetrics.metrics.delim1;
|
||||||
|
} else {
|
||||||
|
delimSize = fontMetrics.metrics.getDelim2(fstyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.value.leftDelim != null) {
|
||||||
|
innerChildren.unshift(
|
||||||
|
delimiter.customSizedDelim(
|
||||||
|
group.value.leftDelim, delimSize, true,
|
||||||
|
options.withStyle(fstyle), group.mode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (group.value.rightDelim != null) {
|
||||||
|
innerChildren.push(
|
||||||
|
delimiter.customSizedDelim(
|
||||||
|
group.value.rightDelim, delimSize, true,
|
||||||
|
options.withStyle(fstyle), group.mode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["minner", "mfrac", options.style.reset(), fstyle.cls()],
|
["minner", options.style.reset(), fstyle.cls()],
|
||||||
[frac], options.getColor());
|
innerChildren,
|
||||||
|
options.getColor());
|
||||||
},
|
},
|
||||||
|
|
||||||
spacing: function(group, options, prev) {
|
spacing: function(group, options, prev) {
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
/* jshint unused:false */
|
/* jshint unused:false */
|
||||||
|
|
||||||
|
var Style = require("./Style");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file contains metrics regarding fonts and individual symbols. The sigma
|
* This file contains metrics regarding fonts and individual symbols. The sigma
|
||||||
* and xi variables, as well as the metricMap map contain data extracted from
|
* and xi variables, as well as the metricMap map contain data extracted from
|
||||||
@@ -35,7 +37,9 @@ var sigma17 = 0.247;
|
|||||||
var sigma18 = 0.386;
|
var sigma18 = 0.386;
|
||||||
var sigma19 = 0.050;
|
var sigma19 = 0.050;
|
||||||
var sigma20 = 2.390;
|
var sigma20 = 2.390;
|
||||||
var sigma21 = 0.101;
|
var sigma21 = 1.01;
|
||||||
|
var sigma21Script = 0.81;
|
||||||
|
var sigma21ScriptScript = 0.71;
|
||||||
var sigma22 = 0.250;
|
var sigma22 = 0.250;
|
||||||
|
|
||||||
// These font metrics are extracted from TeX by using
|
// These font metrics are extracted from TeX by using
|
||||||
@@ -81,8 +85,6 @@ var metrics = {
|
|||||||
sub2: sigma17,
|
sub2: sigma17,
|
||||||
supDrop: sigma18,
|
supDrop: sigma18,
|
||||||
subDrop: sigma19,
|
subDrop: sigma19,
|
||||||
delim1: sigma20,
|
|
||||||
delim2: sigma21,
|
|
||||||
axisHeight: sigma22,
|
axisHeight: sigma22,
|
||||||
defaultRuleThickness: xi8,
|
defaultRuleThickness: xi8,
|
||||||
bigOpSpacing1: xi9,
|
bigOpSpacing1: xi9,
|
||||||
@@ -90,7 +92,21 @@ var metrics = {
|
|||||||
bigOpSpacing3: xi11,
|
bigOpSpacing3: xi11,
|
||||||
bigOpSpacing4: xi12,
|
bigOpSpacing4: xi12,
|
||||||
bigOpSpacing5: xi13,
|
bigOpSpacing5: xi13,
|
||||||
ptPerEm: ptPerEm
|
ptPerEm: ptPerEm,
|
||||||
|
|
||||||
|
// TODO(alpert): Missing parallel structure here. We should probably add
|
||||||
|
// style-specific metrics for all of these.
|
||||||
|
delim1: sigma20,
|
||||||
|
getDelim2: function(style) {
|
||||||
|
if (style.size === Style.TEXT.size) {
|
||||||
|
return sigma21;
|
||||||
|
} else if (style.size === Style.SCRIPT.size) {
|
||||||
|
return sigma21Script;
|
||||||
|
} else if (style.size === Style.SCRIPTSCRIPT.size) {
|
||||||
|
return sigma21ScriptScript;
|
||||||
|
}
|
||||||
|
throw new Error("Unexpected style size: " + style.size);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This map contains a mapping from font name and character code to character
|
// This map contains a mapping from font name and character code to character
|
||||||
|
@@ -329,16 +329,55 @@ var duplicatedFunctions = [
|
|||||||
|
|
||||||
// Fractions
|
// Fractions
|
||||||
{
|
{
|
||||||
funcs: ["\\dfrac", "\\frac", "\\tfrac"],
|
funcs: [
|
||||||
|
"\\dfrac", "\\frac", "\\tfrac",
|
||||||
|
"\\dbinom", "\\binom", "\\tbinom"
|
||||||
|
],
|
||||||
data: {
|
data: {
|
||||||
numArgs: 2,
|
numArgs: 2,
|
||||||
greediness: 2,
|
greediness: 2,
|
||||||
handler: function(func, numer, denom) {
|
handler: function(func, numer, denom) {
|
||||||
|
var hasBarLine;
|
||||||
|
var leftDelim = null;
|
||||||
|
var rightDelim = null;
|
||||||
|
var size = "auto";
|
||||||
|
|
||||||
|
switch (func) {
|
||||||
|
case "\\dfrac":
|
||||||
|
case "\\frac":
|
||||||
|
case "\\tfrac":
|
||||||
|
hasBarLine = true;
|
||||||
|
break;
|
||||||
|
case "\\dbinom":
|
||||||
|
case "\\binom":
|
||||||
|
case "\\tbinom":
|
||||||
|
hasBarLine = false;
|
||||||
|
leftDelim = "(";
|
||||||
|
rightDelim = ")";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unrecognized genfrac command");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (func) {
|
||||||
|
case "\\dfrac":
|
||||||
|
case "\\dbinom":
|
||||||
|
size = "display";
|
||||||
|
break;
|
||||||
|
case "\\tfrac":
|
||||||
|
case "\\tbinom":
|
||||||
|
size = "text";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "frac",
|
type: "genfrac",
|
||||||
numer: numer,
|
numer: numer,
|
||||||
denom: denom,
|
denom: denom,
|
||||||
size: func.slice(1)
|
hasBarLine: hasBarLine,
|
||||||
|
leftDelim: leftDelim,
|
||||||
|
rightDelim: rightDelim,
|
||||||
|
size: size
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,12 @@
|
|||||||
"url": "http://localhost:7936/test/huxley/test.html?m=\\dfrac{a}{b}\\frac{a}{b}\\tfrac{a}{b}"
|
"url": "http://localhost:7936/test/huxley/test.html?m=\\dfrac{a}{b}\\frac{a}{b}\\tfrac{a}{b}"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "BinomTest",
|
||||||
|
"screenSize": [1024, 768],
|
||||||
|
"url": "http://localhost:7936/test/huxley/test.html?m=\\dbinom{a}{b}\\tbinom{a}{b}^{\\binom{a}{b}+17}"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "NestedFractions",
|
"name": "NestedFractions",
|
||||||
"screenSize": [1024, 768],
|
"screenSize": [1024, 768],
|
||||||
|
BIN
test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png
Normal file
BIN
test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
5
test/huxley/Huxleyfolder/BinomTest.record.json
Normal file
5
test/huxley/Huxleyfolder/BinomTest.record.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"action": "screenshot"
|
||||||
|
}
|
||||||
|
]
|
Reference in New Issue
Block a user