@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.
Files changed (65) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +138 -0
  3. package/index.cjs.js +1 -0
  4. package/index.d.ts +2 -0
  5. package/index.js +6 -0
  6. package/package.json +54 -0
  7. package/text_engine_2d/MSDFTextGeometry/TextLayout.cjs.js +1 -0
  8. package/text_engine_2d/MSDFTextGeometry/TextLayout.d.ts +48 -0
  9. package/text_engine_2d/MSDFTextGeometry/TextLayout.js +357 -0
  10. package/text_engine_2d/MSDFTextGeometry/index.cjs.js +1 -0
  11. package/text_engine_2d/MSDFTextGeometry/index.d.ts +51 -0
  12. package/text_engine_2d/MSDFTextGeometry/index.js +159 -0
  13. package/text_engine_2d/MSDFTextGeometry/utils.cjs.js +1 -0
  14. package/text_engine_2d/MSDFTextGeometry/utils.d.ts +3 -0
  15. package/text_engine_2d/MSDFTextGeometry/utils.js +46 -0
  16. package/text_engine_2d/MSDFTextGeometry/vertices.cjs.js +1 -0
  17. package/text_engine_2d/MSDFTextGeometry/vertices.d.ts +28 -0
  18. package/text_engine_2d/MSDFTextGeometry/vertices.js +84 -0
  19. package/text_engine_2d/MSDFTextMaterial/fragment.glsl.js +3 -0
  20. package/text_engine_2d/MSDFTextMaterial/index.cjs.js +1 -0
  21. package/text_engine_2d/MSDFTextMaterial/index.d.ts +4 -0
  22. package/text_engine_2d/MSDFTextMaterial/index.js +38 -0
  23. package/text_engine_2d/MSDFTextMaterial/vertex.glsl.js +3 -0
  24. package/text_engine_2d/index.cjs.js +1 -0
  25. package/text_engine_2d/index.d.ts +6 -0
  26. package/text_engine_2d/index.js +18 -0
  27. package/text_engine_2d/react/SDFText.cjs.js +1 -0
  28. package/text_engine_2d/react/SDFText.d.ts +9 -0
  29. package/text_engine_2d/react/SDFText.js +63 -0
  30. package/text_engine_2d/react/SDFTextProvider.cjs.js +1 -0
  31. package/text_engine_2d/react/SDFTextProvider.d.ts +6 -0
  32. package/text_engine_2d/react/SDFTextProvider.js +191 -0
  33. package/text_engine_2d/react/context.cjs.js +1 -0
  34. package/text_engine_2d/react/context.d.ts +3 -0
  35. package/text_engine_2d/react/context.js +5 -0
  36. package/text_engine_2d/react/types.cjs.js +1 -0
  37. package/text_engine_2d/react/types.d.ts +33 -0
  38. package/text_engine_2d/react/types.js +1 -0
  39. package/text_engine_2d/react/utils.cjs.js +1 -0
  40. package/text_engine_2d/react/utils.d.ts +2 -0
  41. package/text_engine_2d/react/utils.js +6 -0
  42. package/text_engine_2d/register.cjs.js +1 -0
  43. package/text_engine_2d/register.d.ts +12 -0
  44. package/text_engine_2d/register.js +12 -0
  45. package/text_engine_3d/InstancedText.cjs.js +1 -0
  46. package/text_engine_3d/InstancedText.d.ts +13 -0
  47. package/text_engine_3d/InstancedText.js +127 -0
  48. package/text_engine_3d/InstancedTextPlaceholder.cjs.js +1 -0
  49. package/text_engine_3d/InstancedTextPlaceholder.d.ts +23 -0
  50. package/text_engine_3d/InstancedTextPlaceholder.js +50 -0
  51. package/text_engine_3d/InstancedTextProvider.cjs.js +1 -0
  52. package/text_engine_3d/InstancedTextProvider.d.ts +15 -0
  53. package/text_engine_3d/InstancedTextProvider.js +124 -0
  54. package/text_engine_3d/index.cjs.js +1 -0
  55. package/text_engine_3d/index.d.ts +3 -0
  56. package/text_engine_3d/index.js +11 -0
  57. package/text_engine_3d/instancedTextBuffers.cjs.js +1 -0
  58. package/text_engine_3d/instancedTextBuffers.d.ts +28 -0
  59. package/text_engine_3d/instancedTextBuffers.js +105 -0
  60. package/text_engine_3d/types.cjs.js +1 -0
  61. package/text_engine_3d/types.d.ts +37 -0
  62. package/text_engine_3d/types.js +8 -0
  63. package/text_engine_3d/useInstancedTextMaterial.cjs.js +1 -0
  64. package/text_engine_3d/useInstancedTextMaterial.d.ts +4 -0
  65. 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
@@ -0,0 +1,2 @@
1
+ export * from './text_engine_2d';
2
+ export * from './text_engine_3d';
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;