mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-13 06:58:40 +00:00
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:
@@ -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 {
|
||||
|
@@ -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",
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user