rapier-math 0.0.0-stage → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,111 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Compose missing mathematical symbols from this font's own outlines and rules.
3
+ function mathFallbackGlyph(value,c,extra={}) {
4
+ if(!'ϝ▴▾◇◆ⓈℲ⅁╱╲⩞⪷⪸⫅⫆⋔◂▸⪵⪶⪹⪺⫋⫌⇠⇢⨋⎰⎱'.includes(value))return null;
5
+ const s=mathScale(c),axis=mathConstant('AxisHeight',c,.25),line=mathConstant('FractionRuleThickness',c,.04),
6
+ draw=(ch,factor=1)=>mathGlyphIndex(MATH_FONT.chars[ch.codePointAt(0)],{...c,size:c.size*factor}),
7
+ centered=b=>mathCompose([[b,0,-axis-(b.inkTop+b.inkBottom)/2]],b.w,extra);
8
+ const contour=(width,low,high,path,factor=1)=>{
9
+ const size=s*factor,unit=size/MATH_FONT.units,stroke=mathScreenStroke({...c,size:c.size*factor}),radius=stroke/2;
10
+ return mathBox(width*unit,high*unit+radius,Math.max(0,-low*unit+radius),
11
+ '<path d="'+path+'" transform="scale('+mathNumber(unit)+' '+mathNumber(-unit)+')"'+
12
+ (c.screen?' data-math-ink="'+mathNumber(size)+'"':'')+
13
+ (stroke?' stroke="currentColor" stroke-width="'+mathNumber(stroke/unit)+'" stroke-linejoin="round" paint-order="stroke fill"':'')+'/>',
14
+ {left:-radius,right:width*unit+radius,inkTop:-high*unit-radius,inkBottom:-low*unit+radius,...extra});
15
+ };
16
+ if(value==='ϝ') {
17
+ const b=draw('𝑓'),g=MATH_FONT.glyph(b.glyph),path=g[7],bars=[...path.matchAll(/H(\d+)/g)],
18
+ start=bars[1],end=bars[2],cut=Number(path.slice(0,start.index).match(/-?\d+/g).at(-1)),
19
+ left=Number(start[1]),right=Number(path.slice(start.index,end.index).match(/-?\d+/g).at(-2)),
20
+ body='M'+left+' '+cut+path.slice(start.index+start[0].length,end.index)+'Z',unit=b.unitScale,
21
+ barEnd=g[0],top=MATH_FONT.xHeight,thickness=top-cut,
22
+ upper=contour(g[0],cut,top,'M'+left+' '+cut+'H'+barEnd+'V'+top+'H'+left+'Z'),
23
+ middle=contour(g[0],170,170+thickness,'M'+(left-50)+' 170H'+(barEnd-45)+'V'+(170+thickness)+'H'+(left-50)+'Z');
24
+ const stem={...b,h:cut*unit+b.stroke/2,inkTop:-cut*unit-b.stroke/2,right:Math.max(b.w,right*unit),
25
+ svg:b.svg.replace('d="'+path+'"','d="'+body+'"'),glyph:undefined};
26
+ return mathCompose([[stem,0,0],[upper,0,0],[middle,0,0]],b.w,{...extra,italic:0});
27
+ }
28
+ if('Ⅎ⅁'.includes(value)) {
29
+ const b=draw(value==='Ⅎ'?'F':String.fromCodePoint(mathStyledCodePoint(71,'sans-serif'))),svg='<g transform="translate('+mathNumber(b.w)+' '+mathNumber(b.inkTop+b.inkBottom)+') scale(-1 -1)">'+b.svg+'</g>';
30
+ return {...b,svg,left:b.w-b.right,right:b.w-b.left,accent:b.w/2,italic:0,glyph:undefined,...extra};
31
+ }
32
+ if(value==='Ⓢ') {
33
+ const circle=draw('○'),letter=draw('S',.62),y=(circle.inkTop+circle.inkBottom-letter.inkTop-letter.inkBottom)/2;
34
+ return mathCompose([[circle,0,0],[letter,(circle.w-letter.w)/2,y]],circle.w,extra);
35
+ }
36
+ if('▴▾◂▸'.includes(value))return centered(draw({'▴':'▲','▾':'▼','◂':'◀','▸':'▶'}[value],.8));
37
+ if(value==='◇') {
38
+ const diamond=draw('⋄'),square=draw('□'),factor=(square.inkBottom-square.inkTop)/(diamond.inkBottom-diamond.inkTop);
39
+ return centered(draw('⋄',factor));
40
+ }
41
+ if(value==='◆') {
42
+ const b=draw('◊'),path=MATH_FONT.glyph(b.glyph)[7],contours=path.match(/M[^M]+/g);
43
+ const range=part=>{const numbers=part.match(/-?\d+(?:\.\d+)?/g).map(Number);return Math.max(...numbers)-Math.min(...numbers);};
44
+ const outer=contours.reduce((a,b)=>range(a)>range(b)?a:b);
45
+ return {...b,svg:b.svg.replace('d="'+path+'"','d="'+outer+'"'),glyph:undefined,...extra};
46
+ }
47
+ if(value==='╱'||value==='╲') {
48
+ const t=MATH_FONT.constants.FractionRuleThickness/Math.SQRT2;
49
+ const points=[[75,-75],[725,575],[725+t,575-t],[75+t,-75-t]];
50
+ if(value==='╲')for(const point of points)point[0]=800-point[0];
51
+ return contour(800,-75-t,575,'M'+points.map(point=>point.map(mathNumber).join(' ')).join('L')+'Z');
52
+ }
53
+ if(value==='⋔') {
54
+ const b=draw('∩',.86),g=MATH_FONT.glyph(b.glyph),x=(g[1]+g[3])/2,t=MATH_FONT.constants.FractionRuleThickness,
55
+ stem=contour(g[0],g[2],g[4]+100,'M'+mathNumber(x-t/2)+' '+g[2]+'V'+(g[4]+100)+'H'+mathNumber(x+t/2)+'V'+g[2]+'Z',.86);
56
+ return centered(mathCompose([[b,0,0],[stem,0,0]],b.w));
57
+ }
58
+ const relations={'⩞':['=','∧'],'⪷':['≺','≈'],'⪸':['≻','≈'],'⫅':['⊂','='],'⫆':['⊃','='],
59
+ '⪵':['≺','=',1],'⪶':['≻','=',1],'⪹':['≺','≈',1],'⪺':['≻','≈',1],'⫋':['⊂','=',1],'⫌':['⊃','=',1]};
60
+ if(relations[value]) {
61
+ const [upper,lower,negated]=relations[value],a=draw(upper,.9),b=draw(lower,.85),gap=.065*s,
62
+ width=Math.max(draw(upper).w,draw(lower).w),extent=a.inkBottom-a.inkTop+b.inkBottom-b.inkTop+gap,
63
+ top=-axis-extent/2,ay=top-a.inkTop,by=top+a.inkBottom-a.inkTop+gap-b.inkTop,
64
+ items=[[a,(width-a.w)/2,ay],[b,(width-b.w)/2,by]];
65
+ if(negated) {
66
+ const height=b.inkBottom-b.inkTop+.12*s,half=.13*s,thickness=line*Math.SQRT2/2,
67
+ y=by+(b.inkTop+b.inkBottom)/2,x=width/2;
68
+ const path='M'+[x-half,y+height/2].map(mathNumber).join(' ')+'L'+[x+half,y-height/2].map(mathNumber).join(' ')+
69
+ 'l'+[thickness,thickness].map(mathNumber).join(' ')+'L'+[x-half+thickness,y+height/2+thickness].map(mathNumber).join(' ')+'Z';
70
+ items.push([mathBox(0,0,0,'<path d="'+path+'"/>',{left:x-half,right:x+half+thickness,inkTop:y-height/2,inkBottom:y+height/2+thickness}),0,0]);
71
+ }
72
+ return mathCompose(items,width,extra);
73
+ }
74
+ if(value==='⇠'||value==='⇢') {
75
+ const right=value==='⇢',b=draw(right?'→':'←'),g=MATH_FONT.glyph(b.glyph),unit=b.unitScale,
76
+ width=Math.max(b.w,extra.stretchAxis==='horizontal'?extra.stretchTarget || 0:0),added=width-b.w,
77
+ split=g[0]*(right?.65:.35);let command='',coordinate=0;
78
+ const path=g[7].replace(/[MLHVCZ]|-?\d+(?:\.\d+)?/g,token=>{
79
+ if(/[MLHVCZ]/.test(token)){command=token;coordinate=0;return token;}
80
+ const isX=command!=='V'&&(command==='H'||coordinate%2===0);coordinate++;
81
+ return isX?mathNumber(right?Math.max(split,Number(token)):Math.min(split,Number(token))):token;
82
+ });
83
+ const head={...b,svg:b.svg.replace('d="'+g[7]+'"','d="'+path+'"'),glyph:undefined},
84
+ start=right?g[1]*unit:split*unit,end=right?split*unit+added:g[3]*unit+added,
85
+ length=end-start,count=Math.max(2,Math.ceil(length/(.19*s)));
86
+ if(count>2048)throw Object.assign(new Error('Mathematical arrow is too large'),{code:'resource-limit'});
87
+ const dash=length/(count*1.7-.7),items=[[head,right?added:0,0]];
88
+ for(let i=0;i<count;i++)items.push([mathRule(dash,line,-axis-line/2),start+i*dash*1.7,0]);
89
+ return mathCompose(items,width,{stretchAxis:'horizontal',...extra});
90
+ }
91
+ if(value==='⨋') {
92
+ const target=extra.stretchAxis==='vertical'?extra.stretchTarget:0,
93
+ sum=target?mathStretch('∑',target,c):draw('∑'),integral=target?mathStretch('∫',target,c):draw('∫'),
94
+ x=(sum.w-integral.w)/2,sy=-axis-(sum.inkTop+sum.inkBottom)/2,iy=-axis-(integral.inkTop+integral.inkBottom)/2;
95
+ return mathCompose([[sum,0,sy],[integral,x,iy]],Math.max(sum.w,x+integral.w),{italic:integral.italic,extended:true,...extra});
96
+ }
97
+ if(value==='⎰'||value==='⎱') {
98
+ const target=Math.max(s,extra.stretchAxis==='vertical'?extra.stretchTarget || 0:s),factor=Math.min(1,target/(1.5*s)),
99
+ top=draw(value==='⎰'?'⎧':'⎫',factor),bottom=draw(value==='⎰'?'⎭':'⎩',factor),
100
+ total=Math.max(target,top.h+bottom.h),gap=Math.max(0,total-top.h-bottom.h),width=top.w,
101
+ ty=-axis-total/2-top.inkTop,by=ty+top.inkBottom+gap-bottom.inkTop,items=[[top,0,ty],[bottom,0,by]];
102
+ if(gap>0) {
103
+ const part=MATH_FONT.glyph(MATH_FONT.chars['⎪'.codePointAt(0)]),unit=s*factor/MATH_FONT.units,
104
+ overlap=Math.max(MATH_FONT.minConnectorOverlap,40),height=gap/unit+2*overlap,
105
+ stem=contour(part[0],0,height,'M'+part[1]+' 0V'+mathNumber(height)+'H'+part[3]+'V0Z',factor);
106
+ items.push([stem,0,ty+top.inkBottom+gap+overlap*unit]);
107
+ }
108
+ return mathCompose(items,width,{extended:true,...extra});
109
+ }
110
+ return null;
111
+ }