commit 006a0a761c59a99acbfc2ce48ce717dae1e76a6b
parent 10e9b4ec125d4b0cd05053c71b12c40c7bb8cf14
Author: Ben Alpert <alpert@khanacademy.org>
Date: Tue, 14 Oct 2014 17:01:19 -0700
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
Diffstat:
6 files changed, 169 insertions(+), 45 deletions(-)
diff --git a/src/buildTree.js b/src/buildTree.js
@@ -364,14 +364,14 @@ var groupTypes = {
[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).
// Figure out what style this fraction should be in based on the
// function used
var fstyle = options.style;
- if (group.value.size === "dfrac") {
+ if (group.value.size === "display") {
fstyle = Style.DISPLAY;
- } else if (group.value.size === "tfrac") {
+ } else if (group.value.size === "text") {
fstyle = Style.TEXT;
}
@@ -384,60 +384,118 @@ var groupTypes = {
var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]);
- var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
- 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;
+ var ruleWidth;
+ if (group.value.hasBarLine) {
+ ruleWidth = fontMetrics.metrics.defaultRuleThickness /
+ options.style.sizeMultiplier;
+ } else {
+ ruleWidth = 0;
+ }
- // Rule 15b, 15d
- var numShift, denomShift, clearance;
+ // 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;
- clearance = 3 * ruleWidth;
} else {
- numShift = fontMetrics.metrics.num2;
+ if (ruleWidth > 0) {
+ numShift = fontMetrics.metrics.num2;
+ clearance = ruleWidth;
+ } else {
+ numShift = fontMetrics.metrics.num3;
+ clearance = 3 * fontMetrics.metrics.defaultRuleThickness;
+ }
denomShift = fontMetrics.metrics.denom2;
- clearance = ruleWidth;
}
- var axisHeight = fontMetrics.metrics.axisHeight;
+ 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);
+ }
- // Rule 15d
- if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth)
- < clearance) {
- numShift +=
- clearance - ((numShift - numer.depth) -
- (axisHeight + 0.5 * ruleWidth));
- }
+ 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;
+
+ if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth)
+ < clearance) {
+ numShift +=
+ clearance - ((numShift - numer.depth) -
+ (axisHeight + 0.5 * ruleWidth));
+ }
- if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift)
- < clearance) {
- denomShift +=
- clearance - ((axisHeight - 0.5 * ruleWidth) -
- (denom.height - denomShift));
- }
+ if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift)
+ < clearance) {
+ denomShift +=
+ clearance - ((axisHeight - 0.5 * ruleWidth) -
+ (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([
- {type: "elem", elem: denomreset, shift: denomShift},
- {type: "elem", elem: mid, shift: midShift},
- {type: "elem", elem: numerreset, shift: -numShift}
- ], "individualShift", null, options);
+ frac = buildCommon.makeVList([
+ {type: "elem", elem: denomreset, shift: denomShift},
+ {type: "elem", elem: mid, shift: midShift},
+ {type: "elem", elem: numerreset, shift: -numShift}
+ ], "individualShift", null, options);
+ }
// Since we manually change the style sometimes (with \dfrac or \tfrac),
// account for the possible size change here.
frac.height *= 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(
- ["minner", "mfrac", options.style.reset(), fstyle.cls()],
- [frac], options.getColor());
+ ["minner", options.style.reset(), fstyle.cls()],
+ innerChildren,
+ options.getColor());
},
spacing: function(group, options, prev) {
diff --git a/src/fontMetrics.js b/src/fontMetrics.js
@@ -1,5 +1,7 @@
/* jshint unused:false */
+var Style = require("./Style");
+
/**
* This file contains metrics regarding fonts and individual symbols. The sigma
* 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 sigma19 = 0.050;
var sigma20 = 2.390;
-var sigma21 = 0.101;
+var sigma21 = 1.01;
+var sigma21Script = 0.81;
+var sigma21ScriptScript = 0.71;
var sigma22 = 0.250;
// These font metrics are extracted from TeX by using
@@ -81,8 +85,6 @@ var metrics = {
sub2: sigma17,
supDrop: sigma18,
subDrop: sigma19,
- delim1: sigma20,
- delim2: sigma21,
axisHeight: sigma22,
defaultRuleThickness: xi8,
bigOpSpacing1: xi9,
@@ -90,7 +92,21 @@ var metrics = {
bigOpSpacing3: xi11,
bigOpSpacing4: xi12,
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
diff --git a/src/functions.js b/src/functions.js
@@ -329,16 +329,55 @@ var duplicatedFunctions = [
// Fractions
{
- funcs: ["\\dfrac", "\\frac", "\\tfrac"],
+ funcs: [
+ "\\dfrac", "\\frac", "\\tfrac",
+ "\\dbinom", "\\binom", "\\tbinom"
+ ],
data: {
numArgs: 2,
greediness: 2,
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 {
- type: "frac",
+ type: "genfrac",
numer: numer,
denom: denom,
- size: func.slice(1)
+ hasBarLine: hasBarLine,
+ leftDelim: leftDelim,
+ rightDelim: rightDelim,
+ size: size
};
}
}
diff --git a/test/huxley/Huxleyfile.json b/test/huxley/Huxleyfile.json
@@ -12,6 +12,12 @@
},
{
+ "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",
"screenSize": [1024, 768],
"url": "http://localhost:7936/test/huxley/test.html?m=\\dfrac{\\frac{a}{b}}{\\frac{c}{d}}\\dfrac{\\dfrac{a}{b}}{\\dfrac{c}{d}}\\frac{\\frac{a}{b}}{\\frac{c}{d}}"
diff --git a/test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png b/test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png
Binary files differ.
diff --git a/test/huxley/Huxleyfolder/BinomTest.record.json b/test/huxley/Huxleyfolder/BinomTest.record.json
@@ -0,0 +1,5 @@
+[
+ {
+ "action": "screenshot"
+ }
+]