feat: Support \phase (#2406)

* Support \phase

* Update screenshots

* Update documentation

* Add a11y test

* Edit MathML notation to phasorangle

* Move code to enclose.js

Co-authored-by: Kevin Barabash <kevinb@khanacademy.org>
This commit is contained in:
Ron Kok
2020-08-12 21:08:36 -07:00
committed by GitHub
parent 26e0a05221
commit 42cc5e507e
11 changed files with 73 additions and 7 deletions

View File

@@ -533,7 +533,7 @@ export class PathNode implements VirtualNode {
constructor(pathName: string, alternate?: string) {
this.pathName = pathName;
this.alternate = alternate; // Used only for \sqrt
this.alternate = alternate; // Used only for \sqrt and \phase
}
toNode(): Node {

View File

@@ -4,6 +4,9 @@ import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import stretchy from "../stretchy";
import {phasePath} from "../svgGeometry";
import {PathNode, SvgNode} from "../domTree";
import {calculateSize} from "../units";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
@@ -11,14 +14,14 @@ import * as mml from "../buildMathML";
const htmlBuilder = (group, options) => {
// \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox
// \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox, \phase
// Some groups can return document fragments. Handle those by wrapping
// them in a span.
const inner = buildCommon.wrapFragment(
html.buildGroup(group.body, options), options);
const label = group.label.substr(1);
const scale = options.sizeMultiplier;
let scale = options.sizeMultiplier;
let img;
let imgShift = 0;
@@ -34,6 +37,33 @@ const htmlBuilder = (group, options) => {
img.height = options.fontMetrics().defaultRuleThickness / scale;
imgShift = -0.5 * options.fontMetrics().xHeight;
} else if (label === "phase") {
// Set a couple of dimensions from the steinmetz package.
const lineWeight = calculateSize({number: 0.6, unit: "pt"}, options);
const clearance = calculateSize({number: 0.35, unit: "ex"}, options);
// Prevent size changes like \Huge from affecting line thickness
const newOptions = options.havingBaseSizing();
scale = scale / newOptions.sizeMultiplier;
const angleHeight = inner.height + inner.depth + lineWeight + clearance;
// Reserve a left pad for the angle.
inner.style.paddingLeft = (angleHeight / 2 + lineWeight) + "em";
// Create an SVG
const viewBoxHeight = Math.floor(1000 * angleHeight * scale);
const path = phasePath(viewBoxHeight);
const svgNode = new SvgNode([new PathNode("phase", path)], {
"width": "400em",
"height": `${viewBoxHeight / 1000}em`,
"viewBox": `0 0 400000 ${viewBoxHeight}`,
"preserveAspectRatio": "xMinYMin slice",
});
// Wrap it in a span with overflow: hidden.
img = buildCommon.makeSvgSpan(["hide-tail"], [svgNode], options);
img.style.height = angleHeight + "em";
imgShift = inner.depth + lineWeight + clearance;
} else {
// Add horizontal padding
if (/cancel/.test(label)) {
@@ -100,6 +130,7 @@ const htmlBuilder = (group, options) => {
],
}, options);
} else {
const classes = /cancel|phase/.test(label) ? ["svg-align"] : [];
vlist = buildCommon.makeVList({
positionType: "individualShift",
children: [
@@ -113,7 +144,7 @@ const htmlBuilder = (group, options) => {
type: "elem",
elem: img,
shift: imgShift,
wrapperClasses: /cancel/.test(label) ? ["svg-align"] : [],
wrapperClasses: classes,
},
],
}, options);
@@ -147,6 +178,9 @@ const mathmlBuilder = (group, options) => {
case "\\bcancel":
node.setAttribute("notation", "downdiagonalstrike");
break;
case "\\phase":
node.setAttribute("notation", "phasorangle");
break;
case "\\sout":
node.setAttribute("notation", "horizontalstrike");
break;
@@ -255,11 +289,11 @@ defineFunction({
defineFunction({
type: "enclose",
names: ["\\cancel", "\\bcancel", "\\xcancel", "\\sout"],
names: ["\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\phase"],
props: {
numArgs: 1,
},
handler({parser, funcName}, args, optArgs) {
handler({parser, funcName}, args) {
const body = args[0];
return {
type: "enclose",

View File

@@ -98,6 +98,11 @@ s76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,1
606zM${1001 + extraViniculum} ${hLinePad}h400000v${40 + extraViniculum}H1017.7z`;
};
export const phasePath = function(y: number): string {
const x = y / 2; // x coordinate at top of angle
return `M400000 ${y} H0 L${x} 0 l65 45 L145 ${y - 80} H400000z`;
};
const sqrtTall = function(
extraViniculum: number,
hLinePad: number,