Skip to content

Commit bd6d50d

Browse files
committed
Add support for bevelled fractions, and clean up formatting and comments.
1 parent 0c3342a commit bd6d50d

File tree

1 file changed

+148
-52
lines changed
  • mathjax3-ts/output/chtml/Wrappers

1 file changed

+148
-52
lines changed

mathjax3-ts/output/chtml/Wrappers/mfrac.ts

Lines changed: 148 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
*/
2323

2424
import {CHTMLWrapper} from '../Wrapper.js';
25+
import {CHTMLWrapperFactory} from '../WrapperFactory.js';
26+
import {CHTMLmo} from './mo.js';
2527
import {MmlMfrac} from '../../../core/MmlTree/MmlNodes/mfrac.js';
2628
import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
2729
import {BBox} from '../BBox.js';
@@ -122,32 +124,75 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
122124

123125
};
124126

127+
protected bevel: CHTMLmo<N, T, D> = null;
128+
129+
/************************************************/
130+
131+
/*
132+
* @override
133+
* @constructor
134+
*/
135+
constructor(factory: CHTMLWrapperFactory<N, T, D>, node: MmlNode, parent: CHTMLWrapper<N, T, D> = null) {
136+
super(factory, node, parent);
137+
//
138+
// create internal bevel mo element
139+
//
140+
if (this.node.attributes.get('bevelled')) {
141+
const {H} = this.getBevelData(this.isDisplay());
142+
const bevel = this.bevel = this.createMo('/');
143+
bevel.canStretch(DIRECTION.Vertical);
144+
bevel.getStretchedVariant([H], true);
145+
}
146+
}
147+
125148
/*
126149
* @override
127150
*/
128151
public toCHTML(parent: N) {
129152
this.standardCHTMLnode(parent);
130153
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
154+
const display = this.isDisplay();
155+
if (bevelled) {
156+
this.makeBevelled(display);
157+
} else {
158+
const thickness = this.length2em(String(linethickness));
159+
if (thickness === 0) {
160+
this.makeAtop(display);
161+
} else {
162+
this.makeFraction(display, thickness);
163+
}
164+
}
165+
}
166+
167+
/*
168+
* @override
169+
*/
170+
public computeBBox(bbox: BBox) {
171+
bbox.empty();
172+
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
173+
const display = this.isDisplay();
131174
if (bevelled) {
132-
this.makeBevelled();
175+
this.getBevelledBBox(bbox, display);
133176
} else {
134177
const thickness = this.length2em(String(linethickness));
135178
if (thickness === 0) {
136-
this.makeAtop();
179+
this.getAtopBBox(bbox, display);
137180
} else {
138-
this.makeFraction(thickness);
181+
this.getFractionBBox(bbox, display, thickness);
139182
}
140183
}
184+
bbox.clean();
141185
}
142186

187+
/************************************************/
188+
143189
/*
144-
* @param{number} t The rule line thickness
190+
* @param{boolean} display True when fraction is in display mode
191+
* @param{number} t The rule line thickness
145192
*/
146-
protected makeFraction(t: number) {
147-
const {displaystyle, scriptlevel, numalign, denomalign} =
148-
this.node.attributes.getList('displaystyle', 'scriptlevel', 'numalign', 'denomalign');
193+
protected makeFraction(display: boolean, t: number) {
194+
const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
149195
const withDelims = this.node.getProperty('texWithDelims');
150-
const display = (displaystyle && scriptlevel === 0);
151196
//
152197
// Attributes to set for the different elements making up the fraction
153198
//
@@ -189,11 +234,31 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
189234
this.childNodes[1].toCHTML(den);
190235
}
191236

192-
protected makeAtop() {
193-
const {displaystyle, scriptlevel, numalign, denomalign} =
194-
this.node.attributes.getList('displaystyle', 'scriptlevel', 'numalign', 'denomalign');
237+
/*
238+
* @param{BBox} bbox The buonding box to modify
239+
* @param{boolean} display True for display-mode fractions
240+
* @param{number} t The thickness of the line
241+
*/
242+
protected getFractionBBox(bbox: BBox, display: boolean, t: number) {
243+
const nbox = this.childNodes[0].getBBox();
244+
const dbox = this.childNodes[1].getBBox();
245+
const fparam = this.font.params;
246+
const pad = (this.node.getProperty('texWithDelims') as boolean ? 0 : fparam.nulldelimiterspace);
247+
const a = fparam.axis_height;
248+
const T = (display ? 3.5 : 1.5) * t;
249+
bbox.combine(nbox, 0, a + T + Math.max(nbox.d * nbox.rscale, (display ? fparam.num1 : fparam.num2) - a - T));
250+
bbox.combine(dbox, 0, a - T - Math.max(dbox.h * dbox.rscale, (display ? fparam.denom1 : fparam.denom2) + a - T));
251+
bbox.w += 2 * pad + .2;
252+
}
253+
254+
/************************************************/
255+
256+
/*
257+
* @param{boolean} display True when fraction is in display mode
258+
*/
259+
protected makeAtop(display: boolean) {
260+
const {numalign, denomalign} = this.node.attributes.getList('numalign', 'denomalign');
195261
const withDelims = this.node.getProperty('texWithDelims');
196-
const display = (displaystyle && scriptlevel === 0);
197262
//
198263
// Attributes to set for the different elements making up the fraction
199264
//
@@ -217,7 +282,19 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
217282
]));
218283
this.childNodes[0].toCHTML(num);
219284
this.childNodes[1].toCHTML(den);
220-
this.drawBBox();
285+
}
286+
287+
/*
288+
* @param{BBox} bbox The bounding box to modify
289+
* @param{boolean} display True for display-mode fractions
290+
*/
291+
protected getAtopBBox(bbox: BBox, display: boolean) {
292+
const fparam = this.font.params;
293+
const pad = (this.node.getProperty('texWithDelims') as boolean ? 0 : fparam.nulldelimiterspace);
294+
const {u, v, nbox, dbox} = this.getUVQ(display);
295+
bbox.combine(nbox, 0, u);
296+
bbox.combine(dbox, 0, -v);
297+
bbox.w += 2 * pad;
221298
}
222299

223300
/*
@@ -249,61 +326,80 @@ export class CHTMLmfrac<N, T, D> extends CHTMLWrapper<N, T, D> {
249326
return {u, v, q, nbox, dbox};
250327
}
251328

252-
protected makeBevelled() {
253-
}
254-
329+
/************************************************/
255330

256331
/*
257-
* @override
332+
* @param{boolean} display True when fraction is in display mode
258333
*/
259-
public computeBBox(bbox: BBox) {
260-
bbox.empty();
261-
const {linethickness, bevelled} = this.node.attributes.getList('linethickness', 'bevelled');
262-
if (bevelled) {
263-
this.getBevelledBBox(bbox);
264-
} else {
265-
const thickness = this.length2em(String(linethickness));
266-
if (thickness === 0) {
267-
this.getAtopBBox(bbox);
268-
} else {
269-
this.getFractionBBox(bbox, thickness);
270-
}
334+
protected makeBevelled(display: boolean) {
335+
const adaptor = this.adaptor;
336+
//
337+
// Create HTML tree
338+
//
339+
this.childNodes[0].toCHTML(this.chtml);
340+
this.bevel.toCHTML(this.chtml);
341+
this.childNodes[1].toCHTML(this.chtml);
342+
//
343+
// Place the parts
344+
//
345+
const {u, v, delta, nbox, dbox} = this.getBevelData(display);
346+
if (u) {
347+
const num = this.childNodes[0].chtml;
348+
adaptor.setStyle(num, 'verticalAlign', this.em(u / nbox.scale));
271349
}
272-
bbox.clean();
350+
if (v) {
351+
const denom = this.childNodes[1].chtml;
352+
adaptor.setStyle(denom, 'verticalAlign', this.em(v / dbox.scale));
353+
}
354+
const dx = this.em(-delta / 2);
355+
adaptor.setStyle(this.bevel.chtml, 'marginLeft', dx);
356+
adaptor.setStyle(this.bevel.chtml, 'marginRight', dx);
273357
}
274358

275-
protected getFractionBBox(bbox: BBox, t: number) {
276-
const {displaystyle, scriptlevel} = this.node.attributes.getList('displaystyle', 'scriptlevel');
277-
const display = displaystyle && scriptlevel === 0;
359+
/*
360+
* @param{BBox} bbox The boundng box to modify
361+
* @param{boolean} display True for display-mode fractions
362+
*/
363+
protected getBevelledBBox(bbox: BBox, display: boolean) {
364+
const {u, v, delta, nbox, dbox} = this.getBevelData(display);
365+
const lbox = this.bevel.getBBox();
366+
bbox.combine(nbox, 0, u);
367+
bbox.combine(lbox, bbox.w - delta / 2, 0);
368+
bbox.combine(dbox, bbox.w - delta / 2, v);
369+
}
370+
371+
/*
372+
* @param{boolean} display True for display-style fractions
373+
* @return{Object} The height (H) of the bevel, horizontal offest (delta)
374+
* vertical offsets (u and v) of the parts, and
375+
* bounding boxes of the parts.
376+
*/
377+
protected getBevelData(display: boolean) {
278378
const nbox = this.childNodes[0].getBBox();
279379
const dbox = this.childNodes[1].getBBox();
280-
const fparam = this.font.params;
281-
const pad = (this.node.getProperty('texWithDelims') as boolean ? 0 : fparam.nulldelimiterspace);
282-
const a = fparam.axis_height;
283-
const T = (display ? 3.5 : 1.5) * t;
284-
bbox.combine(nbox, 0, a + T + Math.max(nbox.d * nbox.rscale, (display ? fparam.num1 : fparam.num2) - a - T));
285-
bbox.combine(dbox, 0, a - T - Math.max(dbox.h * dbox.rscale, (display ? fparam.denom1 : fparam.denom2) + a - T));
286-
bbox.w += 2 * pad + .2;
380+
const delta = (display ? .4 : .15);
381+
const H = Math.max(nbox.scale * (nbox.h + nbox.d), dbox.scale * (dbox.h + dbox.d)) + 2 * delta;
382+
const a = this.font.params.axis_height;
383+
const u = nbox.scale * (nbox.d - nbox.h) / 2 + a + delta;
384+
const v = dbox.scale * (dbox.d - dbox.h) / 2 + a - delta;
385+
return {H, delta, u, v, nbox, dbox};
287386
}
288387

289-
protected getAtopBBox(bbox: BBox) {
290-
const {displaystyle, scriptlevel} = this.node.attributes.getList('displaystyle', 'scriptlevel');
291-
const display = displaystyle && scriptlevel === 0;
292-
const fparam = this.font.params;
293-
const pad = (this.node.getProperty('texWithDelims') as boolean ? 0 : fparam.nulldelimiterspace);
294-
const {u, v, nbox, dbox} = this.getUVQ(display);
295-
bbox.combine(nbox, 0, u);
296-
bbox.combine(dbox, 0, -v);
297-
bbox.w += 2 * pad;
298-
}
299388

300-
protected getBevelledBBox(bbox: BBox) {
301-
}
389+
/************************************************/
302390

303391
/*
304392
* @override
305393
*/
306394
public canStretch(direction: DIRECTION) {
307395
return false;
308396
}
397+
398+
/*
399+
* @return{boolean} True if in display mode, false otherwise
400+
*/
401+
protected isDisplay() {
402+
const {displaystyle, scriptlevel} = this.node.attributes.getList('displaystyle', 'scriptlevel');
403+
return displaystyle && scriptlevel === 0;
404+
}
309405
}

0 commit comments

Comments
 (0)