diff --git a/docs/support_table.md b/docs/support_table.md index 93af131066..dce53dcf33 100644 --- a/docs/support_table.md +++ b/docs/support_table.md @@ -360,6 +360,7 @@ use `\ce` instead| |\downharpoonleft|$\downharpoonleft$|| |\downharpoonright|$\downharpoonright$|| |{drcases}|$\begin{drcases}a&\text{if }b\\c&\text{if }d\end{drcases}$|`\begin{drcases}`
   `a &\text{if } b \\`
   `c &\text{if } d`
`\end{drcases}`| +|\dv|$\dv{f}{t} \dv[2]{y}{x}$|`\dv{f}{t} \dv[2]{y}{x}`| ## E @@ -791,6 +792,7 @@ use `\ce` instead| |\O|$\text{\O}$|`\text{\O}`| |\o|$\text{\o}$|`\text{\o}`| |\odot|$\odot$|| +|\odv|$\odv{f}{t} \odv[2]{y}{x}$|`\odv{f}{t} \odv[2]{y}{x}`| |\OE|$\text{\OE}$|`\text{\OE}`| |\oe|$\text{\oe}$|`\text{\oe}`| |\officialeuro|Not supported|| @@ -837,6 +839,7 @@ use `\ce` instead| |\parallel|$\parallel$|| |\part|Not supported|[Deprecated](https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax)| |\partial|$\partial$|| +|\pdv|$\pdv{f}{t} \pdv[2,2][4]{\psi}{x, y}$|`\pdv{f}{t} \pdv[2,2][4]{\psi}{x, y}`| |\perp|$\perp$|| |\phantom|$\Gamma^{\phantom{i}j}_{i\phantom{j}k}$|`\Gamma^{\phantom{i}j}_{i\phantom{j}k}`| |\phase|$\phase{-78^\circ}$|`\phase{-78^\circ}`| diff --git a/docs/supported.md b/docs/supported.md index 6e2fbac68f..04f9bfd783 100644 --- a/docs/supported.md +++ b/docs/supported.md @@ -559,6 +559,40 @@ Direct Input: $← ↑ → ↓ ↔ ↕ ↖ ↗ ↘ ↙ ↚ ↛ ↞ ↠ ↢ ↣ Extensible arrows all can take an optional argument in the same manner
as `\xrightarrow[under]{over}`. +## Derivatives + +The syntax closely emulates popular $LaTeX$ packages that provide derivatives, +such as `physics`, `diffcoef`, and `derivative`. + +||| +|:----------|:----------| +|$\odv{}{t}$ `\odv{}{t}` |$\odv[2]{}{x}$ `\odv[2]{}{x}` | +|$\odv{f}{t}$ `\odv{f}{t}` |$\odv[2]{y}{x}$ `\odv[2]{y}{x}`| +|$\pdv{}{t}$ `\pdv{}{t}` |$\pdv[2]{}{x}$ `\pdv[2]{}{x}` | +|$\pdv{f}{t}$ `\pdv{f}{t}` |$\pdv[2]{y}{x}$ `\pdv[2]{y}{x}`| +|$\pdv[1,1][2]{}{x,y}$ `\pdv[1,1][2]{}{x,y}` |$\pdv[1,n][1+n]{}{x,y}$ `\pdv[1,n][1+n]{}{x,y}` | +|$\pdv[1,1][2]{z}{x,y}$ `\pdv[1,1][2]{z}{x,y}`|$\pdv[1,n][1+n]{z}{x,y}$ `\pdv[1,n][1+n]{z}{x,y}`| +|$\pdv[1,N-1][N]{z}{x_{\mu,\sigma},y_\xi}$ `\pdv[1,N-1][N]{z}{x_{\mu,\sigma},y_\xi}`|| + +Ordinary derivative `\odv[order]{f}{x}`: +* The first required brace `{f}` holds the function to take derivative of. This + brace can be left empty. +* The second required brace `{x}` holds the variable. +* `[order]` holds the derivative order. If the order is 1, you may omit this + bracket. + +Partial derivative `\pdv[var_orders][total_order]{f}{vars}`: +* The first required brace `{f}` holds the function to take derivative of. This + brace can be left empty. +* The second required brace `{vars}` holds the comma-separated variables, e.g. + `{x}`, `{x, y, z_k}`. +* The first optional braket `[var_orders]` holds the list of derivative orders + corresponding to the variables, e.g. `[2]`, `[1, n, m+1]`. If there is only + one variable and its order is 1, you may omit this bracket. +* The second optional braket `[total_order]` holds the total order of the + derivative. If there is only one variable order and that is the same as the + total order, you may omit this bracket. + ## Special Notation **Bra-ket Notation** diff --git a/src/macros.js b/src/macros.js index feb7400b8f..1e6075cc1f 100644 --- a/src/macros.js +++ b/src/macros.js @@ -16,6 +16,7 @@ import utils from "./utils"; import {makeEm} from "./units"; import ParseError from "./ParseError"; +import {assembleDerivativeExpr} from "./macros/derivatives"; ////////////////////////////////////////////////////////////////////// // macro tools @@ -895,6 +896,17 @@ defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"); defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"); defineMacro("\\plim", "\\DOTSB\\mathop{\\operatorname{plim}}\\limits"); +////////////////////////////////////////////////////////////////////// +// derivative.sty +// https://ctan.math.illinois.edu/macros/latex/contrib/derivative/derivative.pdf + +defineMacro( + "\\dv", (context) => assembleDerivativeExpr("\\textnormal{d}", context)); +defineMacro( + "\\odv", (context) => assembleDerivativeExpr("\\textnormal{d}", context)); +defineMacro( + "\\pdv", (context) => assembleDerivativeExpr("\\partial", context)); + ////////////////////////////////////////////////////////////////////// // braket.sty // http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf diff --git a/src/macros/derivatives.js b/src/macros/derivatives.js new file mode 100644 index 0000000000..9f873ce05c --- /dev/null +++ b/src/macros/derivatives.js @@ -0,0 +1,102 @@ +// @flow +/** + * Macro utilities for assembling derivatives. + */ + +import type {MacroContextInterface} from "../defineMacro"; + +function splitByTopLevelComma(s: string): string[] { + if (!s || s.length === 0) { + return []; + } + + // "1+a,n" => ["1+a", "n"] + // "1+a,{n,m}" => ["1+a", "{n,m}"] + // "1+a,{n,{p,q}}" => ["1+a", "{n,{p,q}}"] + // "1+a" => ["1+a"] + // "1+a,,n" => ["1+a", "", "n"] + // "1+a,," => ["1+a", ""] + // "1+a," => ["1+a"] + // "," => [""] + // "" => [] + const res = []; + let currentStr = ""; + let braceDepth = 0; + const isTopLevelDelimiter = (c) => c === "," && braceDepth <= 0; + for (let i = 0; i < s.length; ++i) { + const c = s[i]; + if (c === "{") { + ++braceDepth; + } else if (c === "}") { + --braceDepth; + } + if (!isTopLevelDelimiter(c)) { + currentStr += c; + } + if (isTopLevelDelimiter(c) || i === s.length - 1) { + res.push(currentStr); + currentStr = ""; + } + } + + return res; +} + +function addTokenProtector(s: string): string { + return s.length > 1 ? ` ${s} ` : s; +} + +function extractArgs(context: MacroContextInterface): { + func: string, + vars: string[], + totalOrders: string[], + varOrders: string[], +} { + const optionalArgGroups = []; + let arg = context.consumeArg().tokens; + while (arg.length === 1 && arg[0].text === "[") { + let bracketContent = ''; + let token = context.expandNextToken(); + while (token.text !== "]" && token.text !== "EOF") { + bracketContent += token.text; + token = context.expandNextToken(); + } + optionalArgGroups.push(splitByTopLevelComma(bracketContent).reverse()); + arg = context.consumeArg().tokens; + } + + const func = arg.reverse().map( + e => addTokenProtector(e.text)).join(""); + arg = context.consumeArg().tokens; + const vars = splitByTopLevelComma(arg.reverse().map( + e => addTokenProtector(e.text)).join("")); + const varOrders = + optionalArgGroups[0] ? optionalArgGroups[0].reverse() : ["1"]; + const totalOrders = + optionalArgGroups[1] ? optionalArgGroups[1].reverse() : varOrders; + + return {func, vars, totalOrders, varOrders}; +} + +const PLACEHOLDER = "\\square"; + +export function assembleDerivativeExpr( + d: string, context: MacroContextInterface): string { + const {func, vars, totalOrders, varOrders} = extractArgs(context); + const numVars = vars.length; + const numVarOrders = varOrders.length; + if (numVars > numVarOrders) { + varOrders.push(...Array(numVars - numVarOrders).fill(PLACEHOLDER)); + } else if (numVarOrders > numVars) { + vars.push(...Array(numVarOrders - numVars).fill(PLACEHOLDER)); + } + + const makeIndex = (s) => s === "1" ? "" : `^{${s}}`; + const numer = `{${d}}${makeIndex(totalOrders[0] ?? PLACEHOLDER)}{${func}}`; + const denom = vars.map((variable, i) => { + const order = varOrders[i]; + return `{${d}}{${variable}}${makeIndex(order)}`; + }).join(""); + + return `\\frac{${numer}}{${denom}}`; +} diff --git a/test/screenshotter/images/Derivatives-chrome.png b/test/screenshotter/images/Derivatives-chrome.png new file mode 100644 index 0000000000..5b279cc635 Binary files /dev/null and b/test/screenshotter/images/Derivatives-chrome.png differ diff --git a/test/screenshotter/images/Derivatives-firefox.png b/test/screenshotter/images/Derivatives-firefox.png new file mode 100644 index 0000000000..c8275c097b Binary files /dev/null and b/test/screenshotter/images/Derivatives-firefox.png differ diff --git a/test/screenshotter/images/DerivativesFallbacks-chrome.png b/test/screenshotter/images/DerivativesFallbacks-chrome.png new file mode 100644 index 0000000000..13ed2eca11 Binary files /dev/null and b/test/screenshotter/images/DerivativesFallbacks-chrome.png differ diff --git a/test/screenshotter/images/DerivativesFallbacks-firefox.png b/test/screenshotter/images/DerivativesFallbacks-firefox.png new file mode 100644 index 0000000000..9ed3264bf7 Binary files /dev/null and b/test/screenshotter/images/DerivativesFallbacks-firefox.png differ diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml index f0fb33a7e3..62f57c533a 100644 --- a/test/screenshotter/ss_data.yaml +++ b/test/screenshotter/ss_data.yaml @@ -125,6 +125,17 @@ DelimiterSizing: | a & b & c\\ a & b & c\\ \end{pmatrix}\; \Braket{ ϕ | \frac{∂^2}{∂ t^2} | ψ } \\ +Derivatives: | + \dv{}{x},\odv{y}{x},\odv[2]{}{x},\odv[n+1]{y}{x} \\ + \pdv{}{x},\pdv{y}{x},\pdv[2]{}{x},\pdv[n+1]{y}{x} \\ + \pdv[1,1][2]{z}{x,y},\pdv[2,2][4]{z}{x,y}, \pdv[1,a][1+a]{z}{x,y} \\ + \pdv[2][2]{\tiny\begin{pmatrix}\phi & 0 \\0 & 1\end{pmatrix}}{x}, + \pdv[1+2^n,{(1,2)},k,\Lambda][N_k+1]{\langle\psi_{1,ûnïcoδé}|\phi_\alpha\rangle} {x,y,z_{\mu,\sigma},\tau} \\ +DerivativesFallbacks: | + \square = \text{placeholder} \\ + \odv[]{y}{x}, \pdv[]{y}{x} \\ + \pdv[2][4]{z}{x,y}, \pdv[2,2][]{z}{x,y}, \pdv[2,2][4]{z}{x} \\ + \pdv[2][6]{\psi}{x,y,z}, \pdv[2,2][]{\psi}{x,y,z}, \pdv[2,2,2][6]{\psi}{x} \\ DisplayMode: tex: \sum_{i=0}^\infty \frac{1}{i} pre: pre