123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /*************************************************************
- *
- * Copyright (c) 2018-2022 The MathJax Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /**
- * @fileoverview Implements utilities for notations for menclose elements
- *
- * @author dpvc@mathjax.org (Davide Cervone)
- */
- import {AnyWrapper} from './Wrapper.js';
- import {CommonMenclose} from './Wrappers/menclose.js';
- /*****************************************************************/
- export const ARROWX = 4, ARROWDX = 1, ARROWY = 2; // default relative arrowhead values
- export const THICKNESS = .067; // default rule thickness
- export const PADDING = .2; // default padding
- export const SOLID = THICKNESS + 'em solid'; // a solid border
- /*****************************************************************/
- /**
- * Shorthand for CommonMenclose
- */
- export type Menclose = CommonMenclose<any, any, any>;
- /**
- * Top, right, bottom, left padding data
- */
- export type PaddingData = [number, number, number, number];
- /**
- * The functions used for notation definitions
- *
- * @templare N The DOM node class
- */
- export type Renderer<W extends AnyWrapper, N> = (node: W, child: N) => void;
- export type BBoxExtender<W extends AnyWrapper> = (node: W) => PaddingData;
- export type BBoxBorder<W extends AnyWrapper> = (node: W) => PaddingData;
- export type Initializer<W extends AnyWrapper> = (node: W) => void;
- /**
- * The definition of a notation
- *
- * @template W The menclose wrapper class
- * @templare N The DOM node class
- */
- export type NotationDef<W extends AnyWrapper, N> = {
- renderer: Renderer<W, N>; // renders the DOM nodes for the notation
- bbox: BBoxExtender<W>; // gives the offsets to the child bounding box: [top, right, bottom, left]
- border?: BBoxBorder<W>; // gives the amount of the bbox offset that is due to borders on the child
- renderChild?: boolean; // true if the notation is used to render the child directly (e.g., radical)
- init?: Initializer<W>; // function to be called during wrapper construction
- remove?: string; // list of notations that are suppressed by this one
- };
- /**
- * For defining notation maps
- *
- * @template W The menclose wrapper class
- * @templare N The DOM node class
- */
- export type DefPair<W extends AnyWrapper, N> = [string, NotationDef<W, N>];
- export type DefList<W extends AnyWrapper, N> = Map<string, NotationDef<W, N>>;
- export type DefPairF<T, W extends AnyWrapper, N> = (name: T) => DefPair<W, N>;
- /**
- * The list of notations for an menclose element
- *
- * @template W The menclose wrapper class
- * @templare N The DOM node class
- */
- export type List<W extends AnyWrapper, N> = {[notation: string]: NotationDef<W, N>};
- /*****************************************************************/
- /**
- * The names and indices of sides for borders, padding, etc.
- */
- export const sideIndex = {top: 0, right: 1, bottom: 2, left: 3};
- export type Side = keyof typeof sideIndex;
- export const sideNames = Object.keys(sideIndex) as Side[];
- /**
- * Common BBox and Border functions
- */
- export const fullBBox = ((node) => new Array(4).fill(node.thickness + node.padding)) as BBoxExtender<Menclose>;
- export const fullPadding = ((node) => new Array(4).fill(node.padding)) as BBoxExtender<Menclose>;
- export const fullBorder = ((node) => new Array(4).fill(node.thickness)) as BBoxBorder<Menclose>;
- /*****************************************************************/
- /**
- * The length of an arrowhead
- */
- export const arrowHead = (node: Menclose) => {
- return Math.max(node.padding, node.thickness * (node.arrowhead.x + node.arrowhead.dx + 1));
- };
- /**
- * Adjust short bbox for tall arrow heads
- */
- export const arrowBBoxHD = (node: Menclose, TRBL: PaddingData) => {
- if (node.childNodes[0]) {
- const {h, d} = node.childNodes[0].getBBox();
- TRBL[0] = TRBL[2] = Math.max(0, node.thickness * node.arrowhead.y - (h + d) / 2);
- }
- return TRBL;
- };
- /**
- * Adjust thin bbox for wide arrow heads
- */
- export const arrowBBoxW = (node: Menclose, TRBL: PaddingData) => {
- if (node.childNodes[0]) {
- const {w} = node.childNodes[0].getBBox();
- TRBL[1] = TRBL[3] = Math.max(0, node.thickness * node.arrowhead.y - w / 2);
- }
- return TRBL;
- };
- /**
- * The data for horizontal and vertical arrow notations
- * [angle, double, isVertical, remove]
- */
- export const arrowDef = {
- up: [-Math.PI / 2, false, true, 'verticalstrike'],
- down: [ Math.PI / 2, false, true, 'verticakstrike'],
- right: [ 0, false, false, 'horizontalstrike'],
- left: [ Math.PI, false, false, 'horizontalstrike'],
- updown: [ Math.PI / 2, true, true, 'verticalstrike uparrow downarrow'],
- leftright: [ 0, true, false, 'horizontalstrike leftarrow rightarrow']
- } as {[name: string]: [number, boolean, boolean, string]};
- /**
- * The data for diagonal arrow notations
- * [c, pi, double, remove]
- */
- export const diagonalArrowDef = {
- updiagonal: [-1, 0, false, 'updiagonalstrike northeastarrow'],
- northeast: [-1, 0, false, 'updiagonalstrike updiagonalarrow'],
- southeast: [ 1, 0, false, 'downdiagonalstrike'],
- northwest: [ 1, Math.PI, false, 'downdiagonalstrike'],
- southwest: [-1, Math.PI, false, 'updiagonalstrike'],
- northeastsouthwest: [-1, 0, true, 'updiagonalstrike northeastarrow updiagonalarrow southwestarrow'],
- northwestsoutheast: [ 1, 0, true, 'downdiagonalstrike northwestarrow southeastarrow']
- } as {[name: string]: [number, number, boolean, string]};
- /**
- * The BBox functions for horizontal and vertical arrows
- */
- export const arrowBBox = {
- up: (node) => arrowBBoxW(node, [arrowHead(node), 0, node.padding, 0]),
- down: (node) => arrowBBoxW(node, [node.padding, 0, arrowHead(node), 0]),
- right: (node) => arrowBBoxHD(node, [0, arrowHead(node), 0, node.padding]),
- left: (node) => arrowBBoxHD(node, [0, node.padding, 0, arrowHead(node)]),
- updown: (node) => arrowBBoxW(node, [arrowHead(node), 0, arrowHead(node), 0]),
- leftright: (node) => arrowBBoxHD(node, [0, arrowHead(node), 0, arrowHead(node)])
- } as {[name: string]: BBoxExtender<Menclose>};
- /*****************************************************************/
- /**
- * @param {Renderer} render The function for adding the border to the node
- * @return {string => DefPair} The function returingn the notation definition
- * for the notation having a line on the given side
- */
- export const CommonBorder = function<W extends Menclose, N>(render: Renderer<W, N>): DefPairF<Side, W, N> {
- /**
- * @param {string} side The side on which a border should appear
- * @return {DefPair} The notation definition for the notation having a line on the given side
- */
- return (side: Side) => {
- const i = sideIndex[side];
- return [side, {
- //
- // Add the border to the main child object
- //
- renderer: render,
- //
- // Indicate the extra space on the given side
- //
- bbox: (node) => {
- const bbox = [0, 0, 0, 0] as PaddingData;
- bbox[i] = node.thickness + node.padding;
- return bbox;
- },
- //
- // Indicate the border on the given side
- //
- border: (node) => {
- const bbox = [0, 0, 0, 0] as PaddingData;
- bbox[i] = node.thickness;
- return bbox;
- }
- }];
- };
- };
- /**
- * @param {Renderer} render The function for adding the borders to the node
- * @return {(sring, Side, Side) => DefPair} The function returning the notation definition
- * for the notation having lines on two sides
- */
- export const CommonBorder2 = function<W extends Menclose, N>(render: Renderer<W, N>):
- (name: string, side1: Side, side2: Side) => DefPair<W, N> {
- /**
- * @param {string} name The name of the notation to define
- * @param {Side} side1 The first side to get a border
- * @param {Side} side2 The second side to get a border
- * @return {DefPair} The notation definition for the notation having lines on two sides
- */
- return (name: string, side1: Side, side2: Side) => {
- const i1 = sideIndex[side1];
- const i2 = sideIndex[side2];
- return [name, {
- //
- // Add the border along the given sides
- //
- renderer: render,
- //
- // Mark the extra space along the two sides
- //
- bbox: (node) => {
- const t = node.thickness + node.padding;
- const bbox = [0, 0, 0, 0] as PaddingData;
- bbox[i1] = bbox[i2] = t;
- return bbox;
- },
- //
- // Indicate the border on the two sides
- //
- border: (node) => {
- const bbox = [0, 0, 0, 0] as PaddingData;
- bbox[i1] = bbox[i2] = node.thickness;
- return bbox;
- },
- //
- // Remove the single side notations, if present
- //
- remove: side1 + ' ' + side2
- }];
- };
- };
- /*****************************************************************/
- /**
- * @param {string => Renderer} render The function for adding the strike to the node
- * @return {string => DefPair} The function returning the notation definition for the diagonal strike
- */
- export const CommonDiagonalStrike = function<W extends Menclose, N>(render: (sname: string) => Renderer<W, N>):
- DefPairF<string, W, N> {
- /**
- * @param {string} name The name of the diagonal strike to define
- * @return {DefPair} The notation definition for the diagonal strike
- */
- return (name: string) => {
- const cname = 'mjx-' + name.charAt(0) + 'strike';
- return [name + 'diagonalstrike', {
- //
- // Find the angle and width from the bounding box size and create the diagonal line
- //
- renderer: render(cname),
- //
- // Add padding all around
- //
- bbox: fullBBox
- }];
- };
- };
- /*****************************************************************/
- /**
- * @param {Renderer} render The function to add the arrow to the node
- * @return {string => DefPair} The funciton returning the notation definition for the diagonal arrow
- */
- export const CommonDiagonalArrow = function<W extends Menclose, N>(render: Renderer<W, N>): DefPairF<string, W, N> {
- /**
- * @param {string} name The name of the diagonal arrow to define
- * @return {DefPair} The notation definition for the diagonal arrow
- */
- return (name: string) => {
- const [c, pi, double, remove] = diagonalArrowDef[name];
- return [name + 'arrow', {
- //
- // Find the angle and width from the bounding box size and create
- // the arrow from them and the other arrow data
- //
- renderer: (node, _child) => {
- const [a, W] = node.arrowAW();
- const arrow = node.arrow(W, c * (a - pi), double);
- render(node, arrow);
- },
- //
- // Add space for the arrowhead all around
- //
- bbox: (node) => {
- const {a, x, y} = node.arrowData();
- const [ax, ay, adx] = [node.arrowhead.x, node.arrowhead.y, node.arrowhead.dx];
- const [b, ar] = node.getArgMod(ax + adx, ay);
- const dy = y + (b > a ? node.thickness * ar * Math.sin(b - a) : 0);
- const dx = x + (b > Math.PI / 2 - a ? node.thickness * ar * Math.sin(b + a - Math.PI / 2) : 0);
- return [dy, dx, dy, dx];
- },
- //
- // Remove redundant notations
- //
- remove: remove
- }];
- };
- };
- /**
- * @param {Renderer} render The function to add the arrow to the node
- * @return {string => DefPair} The function returning the notation definition for the arrow
- */
- export const CommonArrow = function<W extends Menclose, N>(render: Renderer<W, N>): DefPairF<string, W, N> {
- /**
- * @param {string} name The name of the horizontal or vertical arrow to define
- * @return {DefPair} The notation definition for the arrow
- */
- return (name: string) => {
- const [angle, double, isVertical, remove] = arrowDef[name];
- return [name + 'arrow', {
- //
- // Get the arrow height and depth from the bounding box and the arrow direction
- // then create the arrow from that and the other data
- //
- renderer: (node, _child) => {
- const {w, h, d} = node.getBBox();
- const [W, offset] = (isVertical ? [h + d, 'X'] : [w, 'Y']);
- const dd = node.getOffset(offset);
- const arrow = node.arrow(W, angle, double, offset, dd);
- render(node, arrow);
- },
- //
- // Add the padding to the proper sides
- //
- bbox: arrowBBox[name],
- //
- // Remove redundant notations
- //
- remove: remove
- }];
- };
- };
|