@saboit-dev/thomas 1.0.0
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.
- package/LICENSE +23 -0
- package/README.md +138 -0
- package/index.cjs.js +1 -0
- package/index.d.ts +2 -0
- package/index.js +6 -0
- package/package.json +54 -0
- package/text_engine_2d/MSDFTextGeometry/TextLayout.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/TextLayout.d.ts +48 -0
- package/text_engine_2d/MSDFTextGeometry/TextLayout.js +357 -0
- package/text_engine_2d/MSDFTextGeometry/index.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/index.d.ts +51 -0
- package/text_engine_2d/MSDFTextGeometry/index.js +159 -0
- package/text_engine_2d/MSDFTextGeometry/utils.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/utils.d.ts +3 -0
- package/text_engine_2d/MSDFTextGeometry/utils.js +46 -0
- package/text_engine_2d/MSDFTextGeometry/vertices.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/vertices.d.ts +28 -0
- package/text_engine_2d/MSDFTextGeometry/vertices.js +84 -0
- package/text_engine_2d/MSDFTextMaterial/fragment.glsl.js +3 -0
- package/text_engine_2d/MSDFTextMaterial/index.cjs.js +1 -0
- package/text_engine_2d/MSDFTextMaterial/index.d.ts +4 -0
- package/text_engine_2d/MSDFTextMaterial/index.js +38 -0
- package/text_engine_2d/MSDFTextMaterial/vertex.glsl.js +3 -0
- package/text_engine_2d/index.cjs.js +1 -0
- package/text_engine_2d/index.d.ts +6 -0
- package/text_engine_2d/index.js +18 -0
- package/text_engine_2d/react/SDFText.cjs.js +1 -0
- package/text_engine_2d/react/SDFText.d.ts +9 -0
- package/text_engine_2d/react/SDFText.js +63 -0
- package/text_engine_2d/react/SDFTextProvider.cjs.js +1 -0
- package/text_engine_2d/react/SDFTextProvider.d.ts +6 -0
- package/text_engine_2d/react/SDFTextProvider.js +191 -0
- package/text_engine_2d/react/context.cjs.js +1 -0
- package/text_engine_2d/react/context.d.ts +3 -0
- package/text_engine_2d/react/context.js +5 -0
- package/text_engine_2d/react/types.cjs.js +1 -0
- package/text_engine_2d/react/types.d.ts +33 -0
- package/text_engine_2d/react/types.js +1 -0
- package/text_engine_2d/react/utils.cjs.js +1 -0
- package/text_engine_2d/react/utils.d.ts +2 -0
- package/text_engine_2d/react/utils.js +6 -0
- package/text_engine_2d/register.cjs.js +1 -0
- package/text_engine_2d/register.d.ts +12 -0
- package/text_engine_2d/register.js +12 -0
- package/text_engine_3d/InstancedText.cjs.js +1 -0
- package/text_engine_3d/InstancedText.d.ts +13 -0
- package/text_engine_3d/InstancedText.js +127 -0
- package/text_engine_3d/InstancedTextPlaceholder.cjs.js +1 -0
- package/text_engine_3d/InstancedTextPlaceholder.d.ts +23 -0
- package/text_engine_3d/InstancedTextPlaceholder.js +50 -0
- package/text_engine_3d/InstancedTextProvider.cjs.js +1 -0
- package/text_engine_3d/InstancedTextProvider.d.ts +15 -0
- package/text_engine_3d/InstancedTextProvider.js +124 -0
- package/text_engine_3d/index.cjs.js +1 -0
- package/text_engine_3d/index.d.ts +3 -0
- package/text_engine_3d/index.js +11 -0
- package/text_engine_3d/instancedTextBuffers.cjs.js +1 -0
- package/text_engine_3d/instancedTextBuffers.d.ts +28 -0
- package/text_engine_3d/instancedTextBuffers.js +105 -0
- package/text_engine_3d/types.cjs.js +1 -0
- package/text_engine_3d/types.d.ts +37 -0
- package/text_engine_3d/types.js +8 -0
- package/text_engine_3d/useInstancedTextMaterial.cjs.js +1 -0
- package/text_engine_3d/useInstancedTextMaterial.d.ts +4 -0
- package/text_engine_3d/useInstancedTextMaterial.js +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Flux
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2026 SABO Mobile IT GmbH
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# thomas
|
|
2
|
+
|
|
3
|
+
This project is a fork of Thomas by Flux (original author Giulio Zausa), originally released under the MIT License.
|
|
4
|
+
|
|
5
|
+
<img src="./docs/logo.png" width="175px" />
|
|
6
|
+
|
|
7
|
+
thomas is an high-performance 2D/3D text rendering engine for [react-three-fiber](https://github.com/pmndrs/react-three-fiber). It can leverage instancing, vertex pulling and multichannel signed distance fields to render hundreds of thousands of characters with a single draw call, without compromising performance. It offers an easy-to-use React-friendly API.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @saboit-dev/thomas
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Try our [demo here](https://thomas-the-text-engine.netlify.app)!
|
|
14
|
+
|
|
15
|
+
## API for 2D Text
|
|
16
|
+
|
|
17
|
+
thomas provides high quality instanced rendering for 2D quads with text, using multichannel signed distance fields.
|
|
18
|
+
|
|
19
|
+
<div>
|
|
20
|
+
<img src="docs/lots-2d.png" width="250px" />
|
|
21
|
+
<img src="docs/colors-2d.png" width="250px" />
|
|
22
|
+
<img src="docs/align-2d.png" width="250px" />
|
|
23
|
+
</div>
|
|
24
|
+
<br />
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
// Provides the paths to the font to the provider, should be served by your webserver
|
|
28
|
+
const fontPathRegular = {
|
|
29
|
+
sdfPath: '/robotoRegular2D/roboto-regular.png',
|
|
30
|
+
fontPath: '/robotoRegular2D/roboto-regular.fnt',
|
|
31
|
+
}
|
|
32
|
+
// Optional
|
|
33
|
+
const fontPathBold = {
|
|
34
|
+
sdfPath: '/robotoBold2D/roboto-bold.png',
|
|
35
|
+
fontPath: '/robotoBold2D/roboto-bold.fnt',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Text2D() {
|
|
39
|
+
// Optional, for imperative mutations
|
|
40
|
+
const textRef = useRef<SDFTextRefType>(null)
|
|
41
|
+
// You can then call textRef?.setTransform(matrix)
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
// Wrap your text components with this provider
|
|
45
|
+
<SDFTextProvider
|
|
46
|
+
fontPathRegular={fontPathRegular}
|
|
47
|
+
// Optional
|
|
48
|
+
fontPathBold={fontPathBold}
|
|
49
|
+
>
|
|
50
|
+
{/* SDFText will get the transforms from the parent */}
|
|
51
|
+
<group position={[10, 0, 0]}>
|
|
52
|
+
<SDFText
|
|
53
|
+
// The text to be displayed
|
|
54
|
+
text="Example text"
|
|
55
|
+
// An unique key for this instance
|
|
56
|
+
instanceKey="unique_key"
|
|
57
|
+
|
|
58
|
+
// Optional styling props
|
|
59
|
+
bold={false}
|
|
60
|
+
color={new Color('#ff0000')}
|
|
61
|
+
opacity={0.5}
|
|
62
|
+
outlineWidth={0.13}
|
|
63
|
+
outlineOpacity={0.4}
|
|
64
|
+
outlineColor={new Color('#0000ff')}
|
|
65
|
+
|
|
66
|
+
// Optional layouting props
|
|
67
|
+
alignX="left" // 'left' | 'center' | 'right'
|
|
68
|
+
alignY="top" // 'top' | 'middle' | 'bottom'
|
|
69
|
+
letterSpacing={32}
|
|
70
|
+
width={500}
|
|
71
|
+
lineHeight={10}
|
|
72
|
+
yShift={0}
|
|
73
|
+
|
|
74
|
+
// Gets called before updating, useful for applying imperative updates
|
|
75
|
+
onBeforeUpdate={updateFunction}
|
|
76
|
+
|
|
77
|
+
// You can also get a ref to apply imperative mutations
|
|
78
|
+
ref={textRef}
|
|
79
|
+
/>
|
|
80
|
+
</group>
|
|
81
|
+
</SDFTextProvider>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## API for 3D Text
|
|
87
|
+
|
|
88
|
+
Like for the 2D text, wrap your scene with a provider and spawn instances using the `<InstancedText />` component.
|
|
89
|
+
|
|
90
|
+
<div>
|
|
91
|
+
<img src="docs/persp-3d.png" width="250px" />
|
|
92
|
+
<img src="docs/demo-3d.png" width="250px" />
|
|
93
|
+
</div>
|
|
94
|
+
<br />
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import robotoRegular3DInfo from '../public/robotoRegular3D/info.json'
|
|
98
|
+
|
|
99
|
+
// Provides the paths to the font to the provider, should be served by your webserver
|
|
100
|
+
const styles = [
|
|
101
|
+
{
|
|
102
|
+
fontMetadata: robotoRegular3DInfo,
|
|
103
|
+
offsetsPath: '/robotoRegular3D/font.png',
|
|
104
|
+
normalsPath: '/robotoRegular3D/normals.png',
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
function Text3DScene() {
|
|
109
|
+
return (
|
|
110
|
+
<InstancedTextProvider styles={styles}>
|
|
111
|
+
{/* InstancedText will get the transforms from the parent */}
|
|
112
|
+
<group position={[10, 0, 0]}>
|
|
113
|
+
<InstancedText
|
|
114
|
+
text="Example text"
|
|
115
|
+
font={styles[0]}
|
|
116
|
+
|
|
117
|
+
// Depth of the text geometry
|
|
118
|
+
thickness={1}
|
|
119
|
+
// Size of the text geometry
|
|
120
|
+
fontSize={1}
|
|
121
|
+
|
|
122
|
+
// Optional props
|
|
123
|
+
textAlign={TextAlignment.left}
|
|
124
|
+
color={new Color("#ff0000")}
|
|
125
|
+
opacity={0.8}
|
|
126
|
+
|
|
127
|
+
// Also accepts any Object3D props
|
|
128
|
+
/>
|
|
129
|
+
</group>
|
|
130
|
+
</InstancedTextProvider>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Acknowledgements
|
|
136
|
+
|
|
137
|
+
- [drei](https://github.com/pmndrs/drei) for the library setup
|
|
138
|
+
- [three-msdf-text](https://github.com/leochocolat/three-msdf-text) for the MSDF implementation
|
package/index.cjs.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("three"),t=require("quad-indices"),n=require("word-wrapper"),r=require("react"),i=require("@babel/runtime/helpers/extends"),o=require("@react-three/drei"),s=require("@react-three/fiber"),a=require("three/examples/jsm/loaders/FontLoader");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function u(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var c=l(t),h=l(n),d=u(r),f=l(i);const p=["x","e","a","o","n","s","r","c","u","m","v","w","z"],g=["m","w"],x=["H","I","N","E","F","K","L","T","U","V","W","X","Y","Z"],m="\t".charCodeAt(0),y=" ".charCodeAt(0),v=1,b=2;class S{_options={};_width=0;_height=0;_descender=0;_ascender=0;_xHeight=0;_baseline=0;_capHeight=0;_lineHeight=0;_linesTotal=0;_lettersTotal=0;_wordsTotal=0;_fallbackSpaceGlyph=null;_fallbackTabGlyph=null;constructor(e,t={}){this.glyphs=[],this._text=e,this._measure=this.computeMetrics.bind(this),this.update(t)}get width(){return this._width}get height(){return this._height}get descender(){return this._descender}get ascender(){return this._ascender}get xHeight(){return this._xHeight}get baseline(){return this._baseline}get capHeight(){return this._capHeight}get lineHeight(){return this._lineHeight}get linesTotal(){return this._linesTotal}get lettersTotal(){return this._lettersTotal}get wordsTotal(){return this._wordsTotal}update(e){if(e=Object.assign({measure:this._measure},e),this._options=e,this._options.tabSize=_(this._options.tabSize,4),!e.font)throw new Error("must provide a valid bitmap font");const t=this.glyphs,n=this._text||"",r=e.font;this._setupSpaceGlyphs(r);const i=h.default.lines(n,e),o=e.width||0,s=n.split(" ").filter((e=>"\n"!==e)).length,a=n.split("").filter((e=>"\n"!==e&&" "!==e)).length;t.length=0;const l=i.reduce((function(e,t){return Math.max(e,t.width,o)}),0);let u=0,c=0;const d=_(e.lineHeight,r.common.lineHeight),f=r.common.base,g=d-f,m=e.letterSpacing||0,S=d*i.length-g,w=function(e){if("center"===e)return v;if("right"===e)return b;return 0}(this._options.alignX);c-=S,this._width=l,this._height=S,this._descender=d-f,this._baseline=f,this._xHeight=function(e){for(const t of p){const n=M(e.chars,t.charCodeAt(0));if(n>=0)return e.chars[n].height}return 0}(r),this._capHeight=function(e){for(const t of x){const n=M(e.chars,t.charCodeAt(0));if(n>=0)return e.chars[n].height}return 0}(r),this._lineHeight=d,this._ascender=d-g-this._xHeight;let B=0,C=0;i.forEach(((e,o)=>{const h=e.start,f=e.end,p=e.width,g=n.slice(h,f).split(" ").filter((e=>""!==e)).length,x=n.slice(h,f).split(" ").join("").length;let S,M=0,_=0;for(let e=h;e<f;e++){const h=n.charCodeAt(e),d=this.getGlyph(r,h);if(d){S&&(u+=A(r,S.id,d.id));let n=u;w===v?n+=(l-p)/2:w===b&&(n+=l-p),t.push({position:[n,c],data:d,index:e,linesTotal:i.length,lineIndex:o,lineLettersTotal:x,lineLetterIndex:M,lineWordsTotal:g,lineWordIndex:_,wordsTotal:s,wordIndex:B,lettersTotal:a,letterIndex:C}),d.id===y&&S.id!==y&&(_++,B++),d.id!==y&&(M++,C++),u+=d.xadvance+m,S=d}}c+=d,u=0})),this._lettersTotal=a,this._wordsTotal=s,this._linesTotal=i.length}getGlyph(e,t){const n=w(e,t);return n||(t===m?this._fallbackTabGlyph:t===y?this._fallbackSpaceGlyph:null)}computeMetrics(e,t,n,r){const i=this._options.letterSpacing||0,o=this._options.font;let s,a,l=0,u=0,c=0;if(!o.chars||0===o.chars.length)return{start:t,end:t,width:0};n=Math.min(e.length,n);for(let h=t;h<n;h++){const t=e.charCodeAt(h);if(s=this.getGlyph(o,t),s){s.char=e[h];l+=a?A(o,a.id,s.id):0;const t=l+s.xadvance+i,n=l+s.width;if(n>=r||t>=r)break;l=t,u=n,a=s}c++}return a&&(u+=a.xoffset),{start:t,end:t+c,width:u}}_setupSpaceGlyphs(e){var t;if(this._fallbackSpaceGlyph=null,this._fallbackTabGlyph=null,!e.chars||0===e.chars.length)return;const n=w(e,y)||function(e){for(const t of g){const n=M(e.chars,t.charCodeAt(0));if(n>=0)return e.chars[n]}return 0}(e)||e.chars[0],r=(null!==(t=this._options.tabSize)&&void 0!==t?t:1)*n.xadvance;this._fallbackSpaceGlyph=n;const i=Object.assign({},n);this._fallbackTabGlyph=Object.assign(i,{x:0,y:0,xadvance:r,id:m,xoffset:0,yoffset:0,width:0,height:0})}}function w(e,t){if(!e.chars||0===e.chars.length)return null;const n=M(e.chars,t);if(n>=0){return e.chars[n]}return null}function A(e,t,n){if(!e.kernings||0===e.kernings.length)return 0;const r=e.kernings;for(const e of r)if(e.first===t&&e.second===n)return e.amount;return 0}function M(e,t,n){for(let r=n=n||0;r<e.length;r++)if(e[r].id===t)return r;return-1}function _(e,t){return"number"==typeof e?e:"number"==typeof t?t:0}const B=3,C={min:[0,0,0],max:[0,0,0]};function I(e){const t=e.length/B;C.min[0]=e[0],C.min[1]=e[1],C.max[0]=e[0],C.max[1]=e[1];for(let n=0;n<t;n++){const t=e[n*B+0],r=e[n*B+1],i=e[n*B+2];C.min[0]=Math.min(t,C.min[0]),C.min[1]=Math.min(r,C.min[1]),C.min[2]=Math.min(i,C.min[2]),C.max[0]=Math.max(t,C.max[0]),C.max[1]=Math.max(r,C.max[1]),C.max[2]=Math.max(i,C.max[2])}}function T(e,t){return I(e),t.min.set(C.min[0],C.min[1],C.min[2]),t.max.set(C.max[0],C.max[1],C.max[2]),t}const G=new e.Vector3,P=new e.Vector3,F=new e.Vector3,O=new e.Vector3;class E extends e.BufferGeometry{originalPositionsArray=new Float32Array;positionsArray=new Float32Array;positionsAttribute=null;instancesOffsetsCache=[];instancesLengthsCache=[];constructor(e){super(),this._layouts=[],this.update(e)}setTransform(e,t=0){const n=this.instancesOffsetsCache[t],r=this.instancesLengthsCache[t];for(let t=0;t<r;t++){const r=12*n+12*t,i=this.originalPositionsArray[r],o=this.originalPositionsArray[r+1],s=this.originalPositionsArray[r+2],a=this.originalPositionsArray[r+3],l=this.originalPositionsArray[r+4],u=this.originalPositionsArray[r+5],c=this.originalPositionsArray[r+6],h=this.originalPositionsArray[r+7],d=this.originalPositionsArray[r+8],f=this.originalPositionsArray[r+9],p=this.originalPositionsArray[r+10],g=this.originalPositionsArray[r+11];G.set(i,o,s).applyMatrix4(e),P.set(a,l,u).applyMatrix4(e),F.set(c,h,d).applyMatrix4(e),O.set(f,p,g).applyMatrix4(e),this.positionsArray[r]=G.x,this.positionsArray[r+1]=G.y,this.positionsArray[r+2]=G.z,this.positionsArray[r+3]=P.x,this.positionsArray[r+4]=P.y,this.positionsArray[r+5]=P.z,this.positionsArray[r+6]=F.x,this.positionsArray[r+7]=F.y,this.positionsArray[r+8]=F.z,this.positionsArray[r+9]=O.x,this.positionsArray[r+10]=O.y,this.positionsArray[r+11]=O.z}this.positionsAttribute&&(this.positionsAttribute.needsUpdate=!0)}update(t){const{texts:n,...r}=t;this._layouts=n.map((e=>function(e,t){return new S(e,t)}(e.text,{...r,...e})));const i=!1!==t.flipY,o=t.font,s=o.common.scaleW,a=o.common.scaleH,l=this._layouts.map((e=>e.glyphs.filter((e=>{const t=e.data;return t.width*t.height>0}))));this.instancesOffsetsCache=[],this.instancesLengthsCache=[];let u=0;l.forEach(((e,t)=>{this.instancesLengthsCache[t]=e.length,this.instancesOffsetsCache[t]=u,u+=e.length}));const h=u,d=c.default([],{clockwise:!0,type:"uint16",count:h});this.setIndex(d);const f=l.map((e=>function(e,t,n,r){const i=new Float32Array(4*e.length*2),o=new Float32Array(4*e.length*3),s=new Float32Array(4*e.length*2);let a=0,l=0,u=0;return e.forEach((function(e){const c=e.data,h=c.x+c.width,d=c.y+c.height,f=c.x/t;let p=c.y/n;const g=h/t;let x=d/n;r&&(p=(n-c.y)/n,x=(n-d)/n),i[a++]=f,i[a++]=p,i[a++]=f,i[a++]=x,i[a++]=g,i[a++]=x,i[a++]=g,i[a++]=p;const m=e.position[0]+c.xoffset,y=e.position[1]+c.yoffset,v=c.width,b=c.height;o[l++]=m,o[l++]=y,o[l++]=0,o[l++]=m,o[l++]=y+b,o[l++]=0,o[l++]=m+v,o[l++]=y+b,o[l++]=0,o[l++]=m+v,o[l++]=y,o[l++]=0,s[u++]=m+v/2,s[u++]=y+b/2,s[u++]=m+v/2,s[u++]=y+b/2,s[u++]=m+v/2,s[u++]=y+b/2,s[u++]=m+v/2,s[u++]=y+b/2})),{uvs:i,positions:o,centers:s}}(e,s,a,i))),p=f.reduce(((e,t)=>e+t.positions.length),0),g=f.reduce(((e,t)=>e+t.centers.length),0),x=f.reduce(((e,t)=>e+t.uvs.length),0),m=new Float32Array(p),y=new Float32Array(g),v=new Float32Array(x);let b=0,w=0,A=0;f.forEach((e=>{m.set(e.positions,b),b+=e.positions.length,y.set(e.centers,w),w+=e.centers.length,v.set(e.uvs,A),A+=e.uvs.length})),this.positionsArray=m,this.originalPositionsArray=new Float32Array(p),this.originalPositionsArray.set(m),this.positionsAttribute=new e.BufferAttribute(m,3),this.setAttribute("position",this.positionsAttribute),this.setAttribute("center",new e.BufferAttribute(y,2)),this.setAttribute("uv",new e.BufferAttribute(v,2))}computeBoundingSphere(){null===this.boundingSphere&&(this.boundingSphere=new e.Sphere);const t=this.attributes.position.array,n=this.attributes.position.itemSize;if(!t||!n||t.length<2)return this.boundingSphere.radius=0,void this.boundingSphere.center.set(0,0,0);!function(e,t){I(e);const n=C.min[0],r=C.min[1],i=C.min[2],o=C.max[0]-n,s=C.max[1]-r,a=C.max[2]-i,l=Math.sqrt(o*o+s*s+a*a);t.center.set(n+o/2,r+s/2,i+a/2),t.radius=l/2}(t,this.boundingSphere),isNaN(this.boundingSphere.radius)&&console.error('BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.')}computeBoundingBox(){null===this.boundingBox&&(this.boundingBox=new e.Box3);const t=this.boundingBox,n=this.attributes.position.array,r=this.attributes.position.itemSize;if(!n||!r||n.length<2)return void t.makeEmpty();return T(n,t)}}class k extends e.ShaderMaterial{constructor(t={}){super(t={...{side:e.FrontSide,transparent:!0,extensions:{derivatives:!0},uniforms:{uOpacity:{value:1},uOffset:{value:0},uColor:{value:new e.Color("#ffffff")},uMap:{value:null}},vertexShader:"#define GLSLIFY 1\nvarying vec2 vUv;void main(){vec4 mvPosition=vec4(position,1.0);mvPosition=modelViewMatrix*mvPosition;gl_Position=projectionMatrix*mvPosition;vUv=uv;}",fragmentShader:"#define GLSLIFY 1\nvarying vec2 vUv;uniform float uOpacity;uniform float uOffset;uniform vec3 uColor;uniform sampler2D uMap;float median(float r,float g,float b){return max(min(r,g),min(max(r,g),b));}void main(){vec3 s=texture2D(uMap,vUv).rgb;float sigDist=median(s.r,s.g,s.b)-0.5+uOffset;float alpha=clamp(sigDist/fwidth(sigDist)+0.5,0.0,1.0);if(alpha<0.01)discard;gl_FragColor=vec4(uColor,uOpacity*alpha);}"},...t})}}const H=r.createContext(null),V=e=>{var t,n;return[e.bold,null==(t=e.color)?void 0:t.getHexString(),e.opacity,e.outlineWidth,e.outlineOpacity,null==(n=e.outlineColor)?void 0:n.getHexString()].join("_")},L=r.forwardRef((function({instanceKey:e,bold:t,color:n,opacity:i,outlineWidth:o,outlineOpacity:s,outlineColor:a,text:l,alignX:u,alignY:c,letterSpacing:h,yShift:f,width:p,lineHeight:g,onBeforeUpdate:x},m){const y=r.useContext(H);if(!y)throw new Error("No SDFTextProvider");const v=r.useRef(null),b=r.useRef(null);return r.useImperativeHandle(m,(()=>({setTransform(e){b.current&&(b.current.transform=e)}})),[]),r.useLayoutEffect((()=>{var r;const d={bold:t,color:n,opacity:i,outlineWidth:o,outlineOpacity:s,outlineColor:a},m={text:l,alignX:u,alignY:c,letterSpacing:h,yShift:f,width:p,lineHeight:g,onBeforeUpdate:x},S=V(d);return b.current=y.insertInstance(S,d,e,m,null!==(r=v.current)&&void 0!==r?r:void 0),()=>y.removeInstance(S,e)}),[y,e,l,u,c,h,f,p,g,t,n,i,o,s,a,x]),d.createElement("group",{ref:v})}));function D(){s.extend({MSDFTextMaterial:k,MSDFTextGeometry:E})}const j=99999,U=99998,W=new e.Matrix4,z=new e.Matrix4,N=new e.Color("#ffffff"),R=new e.Color("#000000"),q=.01;function Y({instances:e,textStyle:t,atlas:n,font:i,atlasBold:o,fontBold:a,...l}){var u;const c=o&&a&&t.bold,h=c?a.data:i.data,p=r.useState((()=>new E({font:h,texts:[]})))[0];r.useLayoutEffect((()=>()=>p.dispose()),[p]),r.useLayoutEffect((()=>{const t=e.map((e=>e.config));p.update({font:h,texts:t})}),[p,e,h]),s.useFrame((()=>{e.forEach(((t,n)=>{null==t.config.onBeforeUpdate||t.config.onBeforeUpdate(),z.identity();const r=e[n].object;r&&(r.updateMatrixWorld(),z.multiply(r.matrixWorld)),z.multiply(t.transform),W.makeScale(q,q,q),z.multiply(W);const i=p._layouts[n];if(i){var o,s,a,l;const e=null!==(o=i.width)&&void 0!==o?o:0,t=null!==(s=i.height)&&void 0!==s?s:0,n=null!==(a=i.lineHeight)&&void 0!==a?a:0,r=null!==(l=i._options.yShift)&&void 0!==l?l:0,u=n/4-t,c=n/2;let h=0;"center"===i._options.alignX&&(h=-e/2),"right"===i._options.alignX&&(h=-e);let d=u+r;"middle"===i._options.alignY&&(d=(u+c)/2-i.ascender+r),"bottom"===i._options.alignY&&(d=c+r),W.makeTranslation(h,d,0),z.multiply(W)}W.makeRotationX(Math.PI),z.multiply(W),p.setTransform(z,n)}))}));const g=r.useMemo((()=>{var e,r;return{uMap:{value:c?o:n},uColor:{value:null!==(e=t.color)&&void 0!==e?e:N},uOpacity:{value:null!==(r=t.opacity)&&void 0!==r?r:1},uOffset:{value:0}}}),[c,t.opacity,t.color,n,o]),x=r.useMemo((()=>{var e,r;return{uMap:{value:c?o:n},uColor:{value:null!==(e=t.outlineColor)&&void 0!==e?e:R},uOpacity:{value:null!==(r=t.outlineOpacity)&&void 0!==r?r:.4},uOffset:{value:t.outlineWidth}}}),[c,t.outlineOpacity,t.outlineColor,t.outlineWidth,n,o]);return d.createElement(d.Fragment,null,d.createElement("mesh",f.default({geometry:p,frustumCulled:!1,renderOrder:j},l),d.createElement("mSDFTextMaterial",{uniforms:g})),(null!==(u=t.outlineWidth)&&void 0!==u?u:0)>0&&d.createElement("mesh",f.default({geometry:p,frustumCulled:!1,renderOrder:U},l),d.createElement("mSDFTextMaterial",{uniforms:x,depthWrite:!1})))}const X=new e.Matrix4,K=new e.Matrix4;function Z(){s.extend({InstancedTextPlaceholder:ne})}class J{boundingBox=null;boundingSphere=null;computeBoundingBox(){this.boundingBox||(this.boundingBox=new e.Box3)}computeBoundingSphere(){this.boundingSphere||(this.boundingSphere=new e.Sphere)}}const Q=new e.Matrix4,$=new e.Ray,ee=new e.Sphere,te=new e.Vector3;class ne extends e.Group{textAPI=void 0;geometry=new J;raycast(e,t){const n=this.geometry,r=this.matrixWorld;null===n.boundingSphere&&n.computeBoundingSphere(),ee.copy(n.boundingSphere),ee.applyMatrix4(r),!1!==e.ray.intersectsSphere(ee)&&(Q.copy(r).invert(),$.copy(e.ray).applyMatrix4(Q),null!==n.boundingBox&&null===$.intersectBox(n.boundingBox,te)||t.push({distance:te.distanceTo(e.ray.origin),point:te.clone(),object:this}))}}const re=()=>new e.MeshPhongMaterial;function ie(t,n,i,o=re){return r.useMemo((()=>{const r=o();return r.onBeforeCompile=r=>{r.uniforms={...r.uniforms,offsets:{value:t},normals:{value:n},shift:{value:new e.Vector3(i.xShift,i.yShift,i.zShift)},scale:{value:new e.Vector3(i.xScale,i.yScale,i.zScale)}},r.vertexShader=r.vertexShader.replace(/#include <morphtarget_pars_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <morphtarget_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <morphcolor_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <morphnormal_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <skinning_pars_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <skinbase_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <skinning_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <skinnormal_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <displacementmap_pars_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/#include <displacementmap_vertex>/gi,""),r.vertexShader=r.vertexShader.replace(/>[\n]*void/,">\n uniform sampler2D offsets;\n uniform sampler2D normals;\n uniform vec3 shift;\n uniform vec3 scale;\n in float instanceGIndex;\n\n void"),r.vertexShader=r.vertexShader.replace(/void main\(\) {/,"void main() { vec3 offset = texelFetch(offsets, ivec2(gl_VertexID, instanceGIndex), 0).rgb * scale + shift;"),r.vertexShader=r.vertexShader.replace(/#include <beginnormal_vertex>/,"vec3 objectNormal = (texelFetch(normals, ivec2(gl_VertexID, instanceGIndex), 0).rgb) - 0.5;"),r.vertexShader=r.vertexShader.replace(/#include <begin_vertex>/,"vec3 transformed = offset;")},r}),[t,n,i,o])}function oe(t){const n=o.useTexture(t);return n.flipY=!1,n.magFilter=e.NearestFilter,n.minFilter=e.NearestFilter,n}const se=r.createContext(null);const ae=r.forwardRef((function({font:t},n){const i=ie(oe(t.offsetsPath),oe(t.normalsPath),t.fontMetadata),[{instanceAllocated:o,geometry:a},l]=r.useState({instanceAllocated:0,geometry:null}),u=r.useRef(null),c=r.useCallback((n=>(u.current=function(t,n,r){var i,o,s,a;null==t||t.geometry.dispose();const l=new e.InstancedBufferGeometry;l.instanceCount=n;const u=new Float32Array(3*r),c=new e.Float32BufferAttribute(u,3,!1);l.setAttribute("position",c);const h=new Float32Array(16*l.instanceCount),d=new e.InstancedBufferAttribute(h,16,!1,1);l.setAttribute("instanceMatrix",d);const f=new Float32Array(l.instanceCount);t&&f.set(t.currentGlyphs);const p=new e.InstancedBufferAttribute(f,1,!1,1);l.setAttribute("instanceGIndex",p);const g=new Float32Array(4*l.instanceCount);t&&g.set(t.currentColors);const x=new e.InstancedBufferAttribute(g,4,!0,1);return l.setAttribute("instanceColor",x),{instanceAllocated:n,geometry:l,currentInstances:null!==(i=null==t?void 0:t.currentInstances)&&void 0!==i?i:[],currentGlyphs:null!==(o=null==t?void 0:t.currentGlyphs)&&void 0!==o?o:[],currentShifts:null!==(s=null==t?void 0:t.currentShifts)&&void 0!==s?s:[],currentColors:null!==(a=null==t?void 0:t.currentColors)&&void 0!==a?a:[],instanceMatrixBuffer:h,instanceMatrixBufferAttribute:d,instanceGIndexBuffer:f,instanceGIndexBufferAttribute:p,instanceColorBuffer:g,instanceColorBufferAttribute:x}}(u.current,n,t.fontMetadata.maxVertices),l({instanceAllocated:n,geometry:u.current.geometry}),u.current)),[t.fontMetadata.maxVertices]);return r.useImperativeHandle(n,(()=>({font:t,addInstances:(e,t,n,r,i)=>{var o;const s=null!==(o=u.current)&&void 0!==o?o:c(2*e.length),a=function(e,t,n,r,i,o,s){const a=e.currentGlyphs.length,l=n.length,u=Array.from(n).map((e=>t[e])),c=Array.from(n).flatMap((()=>[i.r,i.g,i.b,1]));e.currentGlyphs.push(...u),e.currentShifts.push(...o),e.currentColors.push(...c);const h={start:a,length:l,text:n,parentGroup:s,font:r};return e.currentInstances.push(h),h}(s,t.fontMetadata.charToIndex,e,t,n,r,i);return s.currentGlyphs.length>s.instanceAllocated?c(2*s.currentGlyphs.length):function(e){e.instanceGIndexBuffer.set(e.currentGlyphs),e.instanceGIndexBufferAttribute.needsUpdate=!0,e.instanceColorBuffer.set(e.currentColors),e.instanceColorBufferAttribute.needsUpdate=!0}(s),a},removeInstances:e=>{const t=u.current;t&&function(e,t){const n=e.currentInstances.indexOf(t);if(-1!==n){for(let r=0;r<e.currentInstances.length;r++)r>n&&(e.currentInstances[r].start-=t.length);e.currentInstances.splice(n,1),e.currentGlyphs.splice(t.start,t.length),e.currentShifts.splice(2*t.start,2*t.length),e.currentColors.splice(4*t.start,4*t.length),e.instanceGIndexBuffer.fill(0),e.instanceGIndexBuffer.set(e.currentGlyphs),e.instanceGIndexBufferAttribute.needsUpdate=!0,e.instanceColorBuffer.fill(0),e.instanceColorBuffer.set(e.currentColors),e.instanceColorBufferAttribute.needsUpdate=!0,e.instanceMatrixBuffer.fill(0),e.instanceMatrixBufferAttribute.needsUpdate=!0}}(t,e)},setOpacity:(e,t)=>{const n=u.current;if(n&&n.instanceColorBuffer&&n.instanceColorBufferAttribute){for(let r=0;r<e.length;r++)n.currentColors[4*(e.start+r)+3]=t,n.instanceColorBuffer[4*(e.start+r)+3]=t;n.instanceColorBufferAttribute.needsUpdate=!0}},getBuffers:()=>u.current})),[t,c]),s.useFrame((()=>{const e=u.current;e&&function(e){e.currentInstances.forEach((t=>{for(let n=0;n<t.length;n++){const r=e.currentShifts[2*(t.start+n)],i=e.currentShifts[2*(t.start+n)+1];K.copy(t.parentGroup.matrixWorld),X.identity().setPosition(r,i,0),K.multiply(X),K.toArray(e.instanceMatrixBuffer,16*(t.start+n))}})),e.instanceMatrixBufferAttribute.needsUpdate=!0}(e)})),a?d.createElement("instancedMesh",{frustumCulled:!1,args:[a,i,o]}):null}));let le=function(e){return e.left="left",e.right="right",e.center="center",e}({});const ue=new e.Color("white");exports.InstancedText=function({text:t,thickness:n,fontSize:i,textAlign:o,scale:s,color:a,opacity:l,font:u,...c}){var h,p,g;const x=u.fontMetadata.charToIndex["?"],m=function(){const e=r.useContext(se);if(!e)throw new Error("No instanced text context");return e}(),y=r.useRef(null),v=r.useRef(null);r.useLayoutEffect((()=>{const r=y.current;if(!r)return;const i=u.fontMetadata.fontScale,s=(u.fontMetadata.boundingBox.yMax-u.fontMetadata.boundingBox.yMin+u.fontMetadata.underlineThickness)*i,l=new Float32Array(t.length),c=new Float32Array(2*t.length);let h=0,d=0;const f=[];Array.from(t).forEach(((e,t)=>{if("\n"===e)f.push(h),h=0,d-=s;else{var n;const r=u.fontMetadata.charToIndex[e],o=null!==(n=u.fontMetadata.advances[r])&&void 0!==n?n:u.fontMetadata.advances[x];l[t]=i,c[2*t]=h,c[2*t+1]=d,h+=o*i}})),f.push(h);const p=Math.max(...f),g=-((u.fontMetadata.boundingBox.yMax-u.fontMetadata.boundingBox.yMin)/2+u.fontMetadata.boundingBox.yMin)*i;Array.from(t).forEach(((e,t)=>{l[t]>0&&(o===le.left&&(c[2*t+1]+=g),o===le.right&&(c[2*t]-=p,c[2*t+1]+=g),o===le.center&&(c[2*t]-=p/2,c[2*t+1]+=g))})),r.geometry.boundingBox||(r.geometry.boundingBox=new e.Box3),r.geometry.boundingSphere||(r.geometry.boundingSphere=new e.Sphere),r.geometry.boundingBox.setFromPoints([new e.Vector3(0,u.fontMetadata.boundingBox.yMin*i,0),new e.Vector3(p,u.fontMetadata.boundingBox.yMax*i,n)]),r.geometry.boundingSphere.setFromPoints([new e.Vector3(0,u.fontMetadata.boundingBox.yMin*i,0),new e.Vector3(p,u.fontMetadata.boundingBox.yMax*i,n)]),o===le.left&&(r.geometry.boundingBox.translate(new e.Vector3(0,g,0)),r.geometry.boundingSphere.translate(new e.Vector3(0,g,0))),o===le.right&&(r.geometry.boundingBox.translate(new e.Vector3(-p,g,0)),r.geometry.boundingSphere.translate(new e.Vector3(-p,g,0))),o===le.center&&(r.geometry.boundingBox.translate(new e.Vector3(-p/2,g,0)),r.geometry.boundingSphere.translate(new e.Vector3(-p/2,g,0)));const b=m.addInstances(t,u,null!=a?a:ue,c,r);return b?(v.current=b,()=>m.removeInstances(b)):void 0}),[t,a,u.fontMetadata,x,n,o,m]),r.useEffect((()=>{v.current&&m.setOpacity(v.current,null!=l?l:1)}),[m,l]);const b=r.useMemo((()=>({getBuffers:()=>v.current&&m.getBuffers(v.current),getPtr:()=>v.current,setGlyphs:e=>{if(!v.current)return;const t=m.getBuffers(v.current);if(!t)return;const n=Array.from(e).map((e=>u.fontMetadata.charToIndex[e]));t.instanceGIndexBuffer.set(n,v.current.start),t.instanceGIndexBufferAttribute.needsUpdate=!0}})),[u.fontMetadata.charToIndex]);return d.createElement("instancedTextPlaceholder",f.default({scale:[i*(null!==(h=null==s?void 0:s.x)&&void 0!==h?h:1),i*(null!==(p=null==s?void 0:s.y)&&void 0!==p?p:1),n*(null!==(g=null==s?void 0:s.z)&&void 0!==g?g:1)]},c,{textAPI:b,ref:y,frustumCulled:!1}))},exports.InstancedTextProvider=function({children:e,styles:t}){r.useMemo(Z,[]);const n=r.useRef(new WeakMap),i=r.useCallback((e=>{e&&n.current.set(e.font,e)}),[]),o=r.useMemo((()=>({addInstances:(e,t,r,i,o)=>{const s=n.current.get(t);return null==s?void 0:s.addInstances(e,t,r,i,o)},removeInstances:e=>{const t=n.current.get(e.font);null==t||t.removeInstances(e)},setOpacity:(e,t)=>{const r=n.current.get(e.font);null==r||r.setOpacity(e,t)},getBuffers:e=>{var t;const r=n.current.get(e.font);return null!==(t=null==r?void 0:r.getBuffers(e))&&void 0!==t?t:null}})),[]);return d.createElement(se.Provider,{value:o},t.map((e=>d.createElement(ae,{key:e.offsetsPath,font:e,ref:i}))),e)},exports.MSDFTextGeometry=E,exports.MSDFTextMaterial=k,exports.SDFText=L,exports.SDFTextProvider=function({children:t,fontPathRegular:n,fontPathBold:i}){r.useMemo(D,[]);const l=o.useTexture(n.sdfPath),u=s.useLoader(a.FontLoader,n.fontPath),c=o.useTexture(i?i.sdfPath:[]),h=s.useLoader(a.FontLoader,i?i.fontPath:[]),f=r.useState((()=>new Map))[0],p=r.useState(0)[1],g=r.useMemo((()=>({insertInstance(t,n,r,i,o){var s;const a={instanceKey:r,config:i,object:o,transform:new e.Matrix4},l=f.get(t);return f.set(t,{textStyle:n,instances:[...null!==(s=null==l?void 0:l.instances)&&void 0!==s?s:[],a]}),p((e=>e+1)),a},removeInstance(e,t){const n=f.get(e);if(n){const r=n.instances.filter((e=>e.instanceKey!==t));0===r.length?f.delete(e):f.set(e,{textStyle:n.textStyle,instances:r}),p((e=>e+1))}}})),[]);return d.createElement(H.Provider,{value:g},d.createElement(d.Fragment,null,t,Array.from(f.values()).map((e=>d.createElement(Y,{key:V(e.textStyle),textStyle:e.textStyle,instances:e.instances,atlas:l,font:u,atlasBold:Array.isArray(c)?void 0:c,fontBold:Array.isArray(h)?void 0:h})))))};
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as MSDFTextMaterial } from './text_engine_2d/MSDFTextMaterial/index.js';
|
|
2
|
+
export { default as MSDFTextGeometry } from './text_engine_2d/MSDFTextGeometry/index.js';
|
|
3
|
+
export { SDFText } from './text_engine_2d/react/SDFText.js';
|
|
4
|
+
export { SDFTextProvider } from './text_engine_2d/react/SDFTextProvider.js';
|
|
5
|
+
export { InstancedTextProvider } from './text_engine_3d/InstancedTextProvider.js';
|
|
6
|
+
export { InstancedText } from './text_engine_3d/InstancedText.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@saboit-dev/thomas",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "",
|
|
6
|
+
"keywords": [],
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/saboit/thomas.git"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/saboit/thomas/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/saboit/thomas",
|
|
16
|
+
"author": "Giulio Zausa (https://github.com/giulioz)",
|
|
17
|
+
"maintainers": [
|
|
18
|
+
"Nikita Evstigneev (https://github.com/evstinik)"
|
|
19
|
+
],
|
|
20
|
+
"main": "index.cjs.js",
|
|
21
|
+
"module": "index.js",
|
|
22
|
+
"types": "index.d.ts",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"commitlint": {
|
|
28
|
+
"extends": [
|
|
29
|
+
"@commitlint/config-conventional"
|
|
30
|
+
],
|
|
31
|
+
"rules": {
|
|
32
|
+
"body-max-line-length": [
|
|
33
|
+
0
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@babel/runtime": "^7.11.2",
|
|
39
|
+
"quad-indices": "^2.0.1",
|
|
40
|
+
"word-wrapper": "^1.0.7"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@react-three/drei": ">=8.0",
|
|
44
|
+
"@react-three/fiber": ">=8.0",
|
|
45
|
+
"react": ">=18.0",
|
|
46
|
+
"react-dom": ">=18.0",
|
|
47
|
+
"three": ">=0.137"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"react-dom": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}Object.defineProperty(exports,"__esModule",{value:!0});var e=t(require("word-wrapper"));const n=["x","e","a","o","n","s","r","c","u","m","v","w","z"],i=["m","w"],r=["H","I","N","E","F","K","L","T","U","V","W","X","Y","Z"],s="\t".charCodeAt(0),h=" ".charCodeAt(0),l=1,o=2;class a{_options={};_width=0;_height=0;_descender=0;_ascender=0;_xHeight=0;_baseline=0;_capHeight=0;_lineHeight=0;_linesTotal=0;_lettersTotal=0;_wordsTotal=0;_fallbackSpaceGlyph=null;_fallbackTabGlyph=null;constructor(t,e={}){this.glyphs=[],this._text=t,this._measure=this.computeMetrics.bind(this),this.update(e)}get width(){return this._width}get height(){return this._height}get descender(){return this._descender}get ascender(){return this._ascender}get xHeight(){return this._xHeight}get baseline(){return this._baseline}get capHeight(){return this._capHeight}get lineHeight(){return this._lineHeight}get linesTotal(){return this._linesTotal}get lettersTotal(){return this._lettersTotal}get wordsTotal(){return this._wordsTotal}update(t){if(t=Object.assign({measure:this._measure},t),this._options=t,this._options.tabSize=f(this._options.tabSize,4),!t.font)throw new Error("must provide a valid bitmap font");const i=this.glyphs,s=this._text||"",a=t.font;this._setupSpaceGlyphs(a);const c=e.default.lines(s,t),g=t.width||0,p=s.split(" ").filter((t=>"\n"!==t)).length,_=s.split("").filter((t=>"\n"!==t&&" "!==t)).length;i.length=0;const b=c.reduce((function(t,e){return Math.max(t,e.width,g)}),0);let x=0,w=0;const y=f(t.lineHeight,a.common.lineHeight),T=a.common.base,m=y-T,H=t.letterSpacing||0,G=y*c.length-m,k=function(t){if("center"===t)return l;if("right"===t)return o;return 0}(this._options.alignX);w-=G,this._width=b,this._height=G,this._descender=y-T,this._baseline=T,this._xHeight=function(t){for(const e of n){const n=d(t.chars,e.charCodeAt(0));if(n>=0)return t.chars[n].height}return 0}(a),this._capHeight=function(t){for(const e of r){const n=d(t.chars,e.charCodeAt(0));if(n>=0)return t.chars[n].height}return 0}(a),this._lineHeight=y,this._ascender=y-m-this._xHeight;let v=0,S=0;c.forEach(((t,e)=>{const n=t.start,r=t.end,d=t.width,f=s.slice(n,r).split(" ").filter((t=>""!==t)).length,g=s.slice(n,r).split(" ").join("").length;let T,m=0,G=0;for(let t=n;t<r;t++){const n=s.charCodeAt(t),r=this.getGlyph(a,n);if(r){T&&(x+=u(a,T.id,r.id));let n=x;k===l?n+=(b-d)/2:k===o&&(n+=b-d),i.push({position:[n,w],data:r,index:t,linesTotal:c.length,lineIndex:e,lineLettersTotal:g,lineLetterIndex:m,lineWordsTotal:f,lineWordIndex:G,wordsTotal:p,wordIndex:v,lettersTotal:_,letterIndex:S}),r.id===h&&T.id!==h&&(G++,v++),r.id!==h&&(m++,S++),x+=r.xadvance+H,T=r}}w+=y,x=0})),this._lettersTotal=_,this._wordsTotal=p,this._linesTotal=c.length}getGlyph(t,e){const n=c(t,e);return n||(e===s?this._fallbackTabGlyph:e===h?this._fallbackSpaceGlyph:null)}computeMetrics(t,e,n,i){const r=this._options.letterSpacing||0,s=this._options.font;let h,l,o=0,a=0,c=0;if(!s.chars||0===s.chars.length)return{start:e,end:e,width:0};n=Math.min(t.length,n);for(let d=e;d<n;d++){const e=t.charCodeAt(d);if(h=this.getGlyph(s,e),h){h.char=t[d];o+=l?u(s,l.id,h.id):0;const e=o+h.xadvance+r,n=o+h.width;if(n>=i||e>=i)break;o=e,a=n,l=h}c++}return l&&(a+=l.xoffset),{start:e,end:e+c,width:a}}_setupSpaceGlyphs(t){var e;if(this._fallbackSpaceGlyph=null,this._fallbackTabGlyph=null,!t.chars||0===t.chars.length)return;const n=c(t,h)||function(t){for(const e of i){const n=d(t.chars,e.charCodeAt(0));if(n>=0)return t.chars[n]}return 0}(t)||t.chars[0],r=(null!==(e=this._options.tabSize)&&void 0!==e?e:1)*n.xadvance;this._fallbackSpaceGlyph=n;const l=Object.assign({},n);this._fallbackTabGlyph=Object.assign(l,{x:0,y:0,xadvance:r,id:s,xoffset:0,yoffset:0,width:0,height:0})}}function c(t,e){if(!t.chars||0===t.chars.length)return null;const n=d(t.chars,e);if(n>=0){return t.chars[n]}return null}function u(t,e,n){if(!t.kernings||0===t.kernings.length)return 0;const i=t.kernings;for(const t of i)if(t.first===e&&t.second===n)return t.amount;return 0}function d(t,e,n){for(let i=n=n||0;i<t.length;i++)if(t[i].id===e)return i;return-1}function f(t,e){return"number"==typeof t?t:"number"==typeof e?e:0}exports.createLayout=function(t,e){return new a(t,e)},exports.default=a;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { IOptions, IGlyph } from '.';
|
|
2
|
+
type OptionsWithoutText = Omit<IOptions, 'text' | 'texts'>;
|
|
3
|
+
declare class TextLayout {
|
|
4
|
+
glyphs: IGlyph[];
|
|
5
|
+
_text: string;
|
|
6
|
+
_options: OptionsWithoutText;
|
|
7
|
+
_measure: (text: string, start: number, end: number, width: number) => {
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
width: number;
|
|
11
|
+
};
|
|
12
|
+
_width: number;
|
|
13
|
+
_height: number;
|
|
14
|
+
_descender: number;
|
|
15
|
+
_ascender: number;
|
|
16
|
+
_xHeight: number;
|
|
17
|
+
_baseline: number;
|
|
18
|
+
_capHeight: number;
|
|
19
|
+
_lineHeight: number;
|
|
20
|
+
_linesTotal: number;
|
|
21
|
+
_lettersTotal: number;
|
|
22
|
+
_wordsTotal: number;
|
|
23
|
+
_fallbackSpaceGlyph: string | null;
|
|
24
|
+
_fallbackTabGlyph: string | null;
|
|
25
|
+
constructor(text: string, options?: OptionsWithoutText);
|
|
26
|
+
get width(): number;
|
|
27
|
+
get height(): number;
|
|
28
|
+
get descender(): number;
|
|
29
|
+
get ascender(): number;
|
|
30
|
+
get xHeight(): number;
|
|
31
|
+
get baseline(): number;
|
|
32
|
+
get capHeight(): number;
|
|
33
|
+
get lineHeight(): number;
|
|
34
|
+
get linesTotal(): number;
|
|
35
|
+
get lettersTotal(): number;
|
|
36
|
+
get wordsTotal(): number;
|
|
37
|
+
update(options: OptionsWithoutText): void;
|
|
38
|
+
getGlyph(font: any, id: number): any;
|
|
39
|
+
computeMetrics(text: string, start: number, end: number, width: number): {
|
|
40
|
+
start: number;
|
|
41
|
+
end: number;
|
|
42
|
+
width: number;
|
|
43
|
+
};
|
|
44
|
+
_setupSpaceGlyphs(font: any): void;
|
|
45
|
+
}
|
|
46
|
+
declare function createLayout(text: string, options: OptionsWithoutText): TextLayout;
|
|
47
|
+
export { createLayout };
|
|
48
|
+
export default TextLayout;
|