troika-three-text
Advanced tools
Comparing version 0.36.1 to 0.37.0
@@ -6,2 +6,19 @@ # Change Log | ||
# [0.37.0](https://github.com/protectwise/troika/compare/v0.36.1...v0.37.0) (2021-01-18) | ||
### Features | ||
* **troika-three-text:** added inner stroke and outline blur capabilities ([e004b9d](https://github.com/protectwise/troika/commit/e004b9d2f7e2ef9e841e61156b68958076533a62)) | ||
### Performance Improvements | ||
* **troika-three-text:** swap tiny-inflate to fflate for minor speed boost on woff fonts ([2ae29fa](https://github.com/protectwise/troika/commit/2ae29faffcec2302453ce9dabac633ade8181127)) | ||
## [0.36.1](https://github.com/protectwise/troika/compare/v0.36.0...v0.36.1) (2020-12-16) | ||
@@ -8,0 +25,0 @@ |
@@ -0,0 +0,0 @@ #!/usr/bin/env node |
@@ -0,0 +0,0 @@ /*! |
/*! | ||
Custom bundle of woff2otf (https://github.com/arty-name/woff2otf) with tiny-inflate | ||
(https://github.com/foliojs/tiny-inflate) for use in Troika text rendering. | ||
Custom bundle of woff2otf (https://github.com/arty-name/woff2otf) with fflate | ||
(https://github.com/101arrowz/fflate) for use in Troika text rendering. | ||
Original licenses apply: | ||
- tiny-inflate: https://github.com/foliojs/tiny-inflate/blob/master/LICENSE (MIT) | ||
- fflate: https://github.com/101arrowz/fflate/blob/master/LICENSE (MIT) | ||
- woff2otf.js: https://github.com/arty-name/woff2otf/blob/master/woff2otf.js (Apache2) | ||
*/ | ||
export default function(){return function(t){"use strict";function e(){this.table=new Uint16Array(16),this.trans=new Uint16Array(288)}function r(t,r){this.source=t,this.sourceIndex=0,this.tag=0,this.bitcount=0,this.dest=r,this.destLen=0,this.ltree=new e,this.dtree=new e}var n=new e,o=new e,a=new Uint8Array(30),s=new Uint16Array(30),i=new Uint8Array(30),u=new Uint16Array(30),f=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),c=new e,g=new Uint8Array(320);function b(t,e,r,n){var o,a;for(o=0;o<r;++o)t[o]=0;for(o=0;o<30-r;++o)t[o+r]=o/r|0;for(a=n,o=0;o<30;++o)e[o]=a,a+=1<<t[o]}var d=new Uint16Array(16);function h(t,e,r,n){var o,a;for(o=0;o<16;++o)t.table[o]=0;for(o=0;o<n;++o)t.table[e[r+o]]++;for(t.table[0]=0,a=0,o=0;o<16;++o)d[o]=a,a+=t.table[o];for(o=0;o<n;++o)e[r+o]&&(t.trans[d[e[r+o]]++]=o)}function l(t){t.bitcount--||(t.tag=t.source[t.sourceIndex++],t.bitcount=7);var e=1&t.tag;return t.tag>>>=1,e}function v(t,e,r){if(!e)return r;for(;t.bitcount<24;)t.tag|=t.source[t.sourceIndex++]<<t.bitcount,t.bitcount+=8;var n=t.tag&65535>>>16-e;return t.tag>>>=e,t.bitcount-=e,n+r}function w(t,e){for(;t.bitcount<24;)t.tag|=t.source[t.sourceIndex++]<<t.bitcount,t.bitcount+=8;var r=0,n=0,o=0,a=t.tag;do{n=2*n+(1&a),a>>>=1,++o,r+=e.table[o],n-=e.table[o]}while(n>=0);return t.tag=a,t.bitcount-=o,e.trans[r+n]}function L(t,e,r){var n,o,a,s,i,u;for(n=v(t,5,257),o=v(t,5,1),a=v(t,4,4),s=0;s<19;++s)g[s]=0;for(s=0;s<a;++s){var b=v(t,3,0);g[f[s]]=b}for(h(c,g,0,19),i=0;i<n+o;){var d=w(t,c);switch(d){case 16:var l=g[i-1];for(u=v(t,2,3);u;--u)g[i++]=l;break;case 17:for(u=v(t,3,3);u;--u)g[i++]=0;break;case 18:for(u=v(t,7,11);u;--u)g[i++]=0;break;default:g[i++]=d}}h(e,g,0,n),h(r,g,n,o)}function U(t,e,r){for(;;){var n,o,f,c,g=w(t,e);if(256===g)return 0;if(g<256)t.dest[t.destLen++]=g;else for(n=v(t,a[g-=257],s[g]),o=w(t,r),c=f=t.destLen-v(t,i[o],u[o]);c<f+n;++c)t.dest[t.destLen++]=t.dest[c]}}function m(t){for(var e,r;t.bitcount>8;)t.sourceIndex--,t.bitcount-=8;if((e=256*(e=t.source[t.sourceIndex+1])+t.source[t.sourceIndex])!==(65535&~(256*t.source[t.sourceIndex+3]+t.source[t.sourceIndex+2])))return-3;for(t.sourceIndex+=4,r=e;r;--r)t.dest[t.destLen++]=t.source[t.sourceIndex++];return t.bitcount=0,0}!function(t,e){var r;for(r=0;r<7;++r)t.table[r]=0;for(t.table[7]=24,t.table[8]=152,t.table[9]=112,r=0;r<24;++r)t.trans[r]=256+r;for(r=0;r<144;++r)t.trans[24+r]=r;for(r=0;r<8;++r)t.trans[168+r]=280+r;for(r=0;r<112;++r)t.trans[176+r]=144+r;for(r=0;r<5;++r)e.table[r]=0;for(e.table[5]=32,r=0;r<32;++r)e.trans[r]=r}(n,o),b(a,s,4,3),b(i,u,2,1),a[28]=0,s[28]=258;var y=function(t,e){var a,s,i=new r(t,e);do{switch(a=l(i),v(i,2,0)){case 0:s=m(i);break;case 1:s=U(i,n,o);break;case 2:L(i,i.ltree,i.dtree),s=U(i,i.ltree,i.dtree);break;default:s=-3}if(0!==s)throw new Error("Data error")}while(!a);return i.destLen<i.dest.length?"function"==typeof i.dest.slice?i.dest.slice(0,i.destLen):i.dest.subarray(0,i.destLen):i.dest};return t.convert_streams=function(t){var e=new DataView(t),r=0;function n(){var t=e.getUint16(r);return r+=2,t}function o(){var t=e.getUint32(r);return r+=4,t}function a(t){w.setUint16(L,t),L+=2}function s(t){w.setUint32(L,t),L+=4}for(var i={signature:o(),flavor:o(),length:o(),numTables:n(),reserved:n(),totalSfntSize:o(),majorVersion:n(),minorVersion:n(),metaOffset:o(),metaLength:o(),metaOrigLength:o(),privOffset:o(),privLength:o()},u=0;Math.pow(2,u)<=i.numTables;)u++;u--;for(var f=16*Math.pow(2,u),c=16*i.numTables-f,g=12,b=[],d=0;d<i.numTables;d++)b.push({tag:o(),offset:o(),compLength:o(),origLength:o(),origChecksum:o()}),g+=16;var h,l=new Uint8Array(12+16*b.length+b.reduce((function(t,e){return t+e.origLength+4}),0)),v=l.buffer,w=new DataView(v),L=0;return s(i.flavor),a(i.numTables),a(f),a(u),a(c),b.forEach((function(t){s(t.tag),s(t.origChecksum),s(g),s(t.origLength),t.outOffset=g,(g+=t.origLength)%4!=0&&(g+=4-g%4)})),b.forEach((function(e){var r=t.slice(e.offset,e.offset+e.compLength);if(e.compLength!=e.origLength){var n=new Uint8Array(e.origLength);y(new Uint8Array(r,2),n)}else n=new Uint8Array(r);l.set(n,e.outOffset);var o=0;(g=e.outOffset+e.origLength)%4!=0&&(o=4-g%4),l.set(new Uint8Array(o).buffer,e.outOffset+e.origLength),h=g+o})),v.slice(0,h)},t}({}).convert_streams} | ||
export default function(){return function(r){"use strict";var e=Uint8Array,n=Uint16Array,t=Uint32Array,a=new e([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),f=new e([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),i=new e([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),o=function(r,e){for(var a=new n(31),f=0;f<31;++f)a[f]=e+=1<<r[f-1];var i=new t(a[30]);for(f=1;f<30;++f)for(var o=a[f];o<a[f+1];++o)i[o]=o-a[f]<<5|f;return[a,i]},u=o(a,2),v=u[0],s=u[1];v[28]=258,s[258]=28;for(var l=o(f,0)[0],c=new n(32768),g=0;g<32768;++g){var h=(43690&g)>>>1|(21845&g)<<1;h=(61680&(h=(52428&h)>>>2|(13107&h)<<2))>>>4|(3855&h)<<4,c[g]=((65280&h)>>>8|(255&h)<<8)>>>1}var w=function(r,e,t){for(var a=r.length,f=0,i=new n(e);f<a;++f)++i[r[f]-1];var o,u=new n(e);for(f=0;f<e;++f)u[f]=u[f-1]+i[f-1]<<1;if(t){o=new n(1<<e);var v=15-e;for(f=0;f<a;++f)if(r[f])for(var s=f<<4|r[f],l=e-r[f],g=u[r[f]-1]++<<l,h=g|(1<<l)-1;g<=h;++g)o[c[g]>>>v]=s}else for(o=new n(a),f=0;f<a;++f)o[f]=c[u[r[f]-1]++]>>>15-r[f];return o},m=new e(288);for(g=0;g<144;++g)m[g]=8;for(g=144;g<256;++g)m[g]=9;for(g=256;g<280;++g)m[g]=7;for(g=280;g<288;++g)m[g]=8;var b=new e(32);for(g=0;g<32;++g)b[g]=5;var p=w(m,9,1),d=w(b,5,1),L=function(r){for(var e=r[0],n=1;n<r.length;++n)r[n]>e&&(e=r[n]);return e},y=function(r,e,n){var t=e/8>>0;return(r[t]|r[t+1]<<8)>>>(7&e)&n},U=function(r,e){var n=e/8>>0;return(r[n]|r[n+1]<<8|r[n+2]<<16)>>>(7&e)},O=function(r,o,u){var s=r.length,c=!o||u,g=!u||u.i;u||(u={}),o||(o=new e(3*s));var h,m=function(r){var n=o.length;if(r>n){var t=new e(Math.max(2*n,r));t.set(o),o=t}},b=u.f||0,O=u.p||0,A=u.b||0,k=u.l,x=u.d,E=u.m,T=u.n,F=8*s;do{if(!k){u.f=b=y(r,O,1);var V=y(r,O+1,3);if(O+=3,!V){var M=r[(I=((h=O)/8>>0)+(7&h&&1)+4)-4]|r[I-3]<<8,C=I+M;if(C>s){if(g)throw"unexpected EOF";break}c&&m(A+M),o.set(r.subarray(I,C),A),u.b=A+=M,u.p=O=8*C;continue}if(1==V)k=p,x=d,E=9,T=5;else{if(2!=V)throw"invalid block type";var D=y(r,O,31)+257,S=y(r,O+10,15)+4,_=D+y(r,O+5,31)+1;O+=14;for(var j=new e(_),z=new e(19),q=0;q<S;++q)z[i[q]]=y(r,O+3*q,7);O+=3*S;var B=L(z),G=(1<<B)-1;if(!g&&O+_*(B+7)>F)break;var H=w(z,B,1);for(q=0;q<_;){var I,J=H[y(r,O,G)];if(O+=15&J,(I=J>>>4)<16)j[q++]=I;else{var K=0,N=0;for(16==I?(N=3+y(r,O,3),O+=2,K=j[q-1]):17==I?(N=3+y(r,O,7),O+=3):18==I&&(N=11+y(r,O,127),O+=7);N--;)j[q++]=K}}var P=j.subarray(0,D),Q=j.subarray(D);E=L(P),T=L(Q),k=w(P,E,1),x=w(Q,T,1)}if(O>F)throw"unexpected EOF"}c&&m(A+131072);for(var R=(1<<E)-1,W=(1<<T)-1,X=E+T+18;g||O+X<F;){var Y=(K=k[U(r,O)&R])>>>4;if((O+=15&K)>F)throw"unexpected EOF";if(!K)throw"invalid length/literal";if(Y<256)o[A++]=Y;else{if(256==Y){k=null;break}var Z=Y-254;if(Y>264){var $=a[q=Y-257];Z=y(r,O,(1<<$)-1)+v[q],O+=$}var rr=x[U(r,O)&W],er=rr>>>4;if(!rr)throw"invalid distance";O+=15&rr;Q=l[er];if(er>3){$=f[er];Q+=U(r,O)&(1<<$)-1,O+=$}if(O>F)throw"unexpected EOF";c&&m(A+131072);for(var nr=A+Z;A<nr;A+=4)o[A]=o[A-Q],o[A+1]=o[A+1-Q],o[A+2]=o[A+2-Q],o[A+3]=o[A+3-Q];A=nr}}u.l=k,u.p=O,u.b=A,k&&(b=1,u.m=E,u.d=x,u.n=T)}while(!b);return A==o.length?o:function(r,a,f){(null==a||a<0)&&(a=0),(null==f||f>r.length)&&(f=r.length);var i=new(r instanceof n?n:r instanceof t?t:e)(f-a);return i.set(r.subarray(a,f)),i}(o,0,A)};return r.convert_streams=function(r){var e=new DataView(r),n=0;function t(){var r=e.getUint16(n);return n+=2,r}function a(){var r=e.getUint32(n);return n+=4,r}function f(r){b.setUint16(p,r),p+=2}function i(r){b.setUint32(p,r),p+=4}for(var o={signature:a(),flavor:a(),length:a(),numTables:t(),reserved:t(),totalSfntSize:a(),majorVersion:t(),minorVersion:t(),metaOffset:a(),metaLength:a(),metaOrigLength:a(),privOffset:a(),privLength:a()},u=0;Math.pow(2,u)<=o.numTables;)u++;u--;for(var v=16*Math.pow(2,u),s=16*o.numTables-v,l=12,c=[],g=0;g<o.numTables;g++)c.push({tag:a(),offset:a(),compLength:a(),origLength:a(),origChecksum:a()}),l+=16;var h,w=new Uint8Array(12+16*c.length+c.reduce((function(r,e){return r+e.origLength+4}),0)),m=w.buffer,b=new DataView(m),p=0;return i(o.flavor),f(o.numTables),f(v),f(u),f(s),c.forEach((function(r){i(r.tag),i(r.origChecksum),i(l),i(r.origLength),r.outOffset=l,(l+=r.origLength)%4!=0&&(l+=4-l%4)})),c.forEach((function(e){var n,t=r.slice(e.offset,e.offset+e.compLength);if(e.compLength!=e.origLength){var a=new Uint8Array(e.origLength);n=new Uint8Array(t,2),O(n,a)}else a=new Uint8Array(t);w.set(a,e.outOffset);var f=0;(l=e.outOffset+e.origLength)%4!=0&&(f=4-l%4),w.set(new Uint8Array(f).buffer,e.outOffset+e.origLength),h=l+f})),m.slice(0,h)},r}({}).convert_streams} |
{ | ||
"name": "troika-three-text", | ||
"version": "0.36.1", | ||
"version": "0.37.0", | ||
"description": "SDF-based text rendering for Three.js", | ||
@@ -17,10 +17,10 @@ "author": "Jason Johnston <jason.johnston@protectwise.com>", | ||
"dependencies": { | ||
"troika-three-utils": "^0.36.0", | ||
"troika-worker-utils": "^0.36.0" | ||
"troika-three-utils": "^0.37.0", | ||
"troika-worker-utils": "^0.37.0" | ||
}, | ||
"devDependencies": { | ||
"@fredli74/typr": "0.3.3", | ||
"fflate": "0.4.8", | ||
"node-fetch": "^2.6.0", | ||
"opentype.js": "1.3.3", | ||
"tiny-inflate": "1.0.3" | ||
"opentype.js": "1.3.3" | ||
}, | ||
@@ -32,3 +32,3 @@ "scripts": { | ||
}, | ||
"gitHead": "1557947d1aa1f8362a3572039a983c679832cfee" | ||
"gitHead": "f2c9c725b22a88ca3275c39150d75f6894ea3503" | ||
} |
@@ -120,2 +120,8 @@ # `troika-three-text` | ||
#### `fillOpacity` | ||
Controls the opacity of just the glyph's fill area, separate from any configured `strokeOpacity`, `outlineOpacity`, and the material's `opacity`. A `fillOpacity` of `0` will make the fill invisible, leaving just the stroke and/or outline. | ||
Default: `1` | ||
#### `font` | ||
@@ -182,12 +188,36 @@ | ||
#### `outlineBlur` | ||
Specifies a blur radius applied to the outer edge of the text's `outlineWidth`. If the `outlineWidth` is zero, the blur will be applied at the glyph edge, like CSS's `text-shadow` blur radius. A blur plus a nonzero `outlineWidth` can give a solid outline with a fuzzy outer edge. | ||
The blur radius can be specified as either an absolute number in local units, or as a percentage string e.g. `"12%"` which is treated as a percentage of the `fontSize`. | ||
Default: `0` | ||
#### `outlineColor` | ||
The color to use for the text outline when `outlineWidth` is greater than `0`. | ||
The color to use for the text outline when `outlineWidth`, `outlineBlur`, and/or `outlineOffsetX/Y` are set. Accepts a ThreeJS `Color` object, or a number/string accepted by `Color#set`. | ||
Default: black | ||
#### `outlineOffsetX`, `outlineOffsetY` | ||
These define a horizontal and vertical offset of the text outline. Using an offset with `outlineWidth: 0` creates a drop-shadow effect like CSS's `text-shadow`; also see `outlineBlur`. | ||
The offsets can be specified as either an absolute number in local units, or as a percentage string e.g. `"12%"` which is treated as a percentage of the `fontSize`. | ||
Default: `0` | ||
#### `outlineOpacity` | ||
Sets the opacity of a configured text outline, in the range `0` to `1`. | ||
Default: `1` | ||
#### `outlineWidth` | ||
The width of an outline/halo to be drawn around each text glyph using the `outlineColor`. This can help improve readability when the text is displayed against a background of low or varying contrast. The width can be specified as either an absolute number in local units, or as a percentage string e.g. `"10%"` which is interpreted as a percentage of the `fontSize`. | ||
The width of an outline/halo to be drawn around each text glyph using the `outlineColor` and `outlineOpacity`. This can help improve readability when the text is displayed against a background of low or varying contrast. | ||
The width can be specified as either an absolute number in local units, or as a percentage string e.g. `"10%"` which is interpreted as a percentage of the `fontSize`. | ||
Default: `0` | ||
@@ -207,2 +237,22 @@ | ||
#### `strokeColor` | ||
The color of the text stroke, when `strokeWidth` is nonzero. Accepts a ThreeJS `Color` object, or a number/string accepted by `Color#set`. | ||
Default: grey | ||
#### `strokeOpacity` | ||
The opacity of the text stroke, when `strokeWidth` is nonzero. Accepts a number from `0` to `1`. | ||
Default: `1` | ||
#### `strokeWidth` | ||
Sets the width of a stroke drawn inside the edge of each text glyph, using the `strokeColor` and `strokeOpacity`. | ||
The width can be specified as either an absolute number in local units, or as a percentage string e.g. `"10%"` which is interpreted as a percentage of the `fontSize`. | ||
Default: `0` | ||
#### `textAlign` | ||
@@ -209,0 +259,0 @@ |
@@ -0,0 +0,0 @@ // This build file creates a static version of the OpenType.js library used for |
@@ -0,0 +0,0 @@ // This build file creates a static version of the Typr.ts library used for |
@@ -1,3 +0,3 @@ | ||
// This build file creates a static version of the Typr.ts library used for | ||
// processing fonts. It's isolated within a "factory" function wrapper so it can | ||
// This build file assembles a function for converting WOFF fonts to OTF fonts, so they | ||
// can be parsed by Typr. It's isolated within a "factory" function wrapper so it can | ||
// easily be marshalled into a web worker. | ||
@@ -19,6 +19,6 @@ | ||
/*! | ||
Custom bundle of woff2otf (https://github.com/arty-name/woff2otf) with tiny-inflate | ||
(https://github.com/foliojs/tiny-inflate) for use in Troika text rendering. | ||
Custom bundle of woff2otf (https://github.com/arty-name/woff2otf) with fflate | ||
(https://github.com/101arrowz/fflate) for use in Troika text rendering. | ||
Original licenses apply: | ||
- tiny-inflate: https://github.com/foliojs/tiny-inflate/blob/master/LICENSE (MIT) | ||
- fflate: https://github.com/101arrowz/fflate/blob/master/LICENSE (MIT) | ||
- woff2otf.js: https://github.com/arty-name/woff2otf/blob/master/woff2otf.js (Apache2) | ||
@@ -43,2 +43,12 @@ */ | ||
commonjs(), | ||
{ | ||
name: 'custom', | ||
transform(source, id) { | ||
if (/fflate/.test(id)) { | ||
// Remove stray `require` in try/catch block which doesn't get treeshaken | ||
return source.replace(/try\s*{\s*Worker\s*=\s*require\('worker_threads'\)\.Worker;\s*}\s*catch\s*\(.\)\s*{\s*}/, '') | ||
} | ||
return source | ||
} | ||
}, | ||
terser({ | ||
@@ -45,0 +55,0 @@ ecma: 5 |
@@ -0,0 +0,0 @@ import { |
@@ -0,0 +0,0 @@ // Exports for troika-three-text package: |
@@ -0,0 +0,0 @@ //=== Utility functions for dealing with carets and selection ranges ===// |
134
src/Text.js
@@ -8,3 +8,4 @@ import { | ||
PlaneBufferGeometry, | ||
Vector3 | ||
Vector3, | ||
Vector2, | ||
} from 'three' | ||
@@ -22,2 +23,3 @@ import { GlyphsGeometry } from './GlyphsGeometry.js' | ||
}) | ||
const defaultStrokeColor = 0x808080 | ||
@@ -221,5 +223,6 @@ const tempMat4 = new Matrix4() | ||
* WARNING: This API is experimental and may change. | ||
* The width of an outline drawn around each text glyph using the `outlineColor`. Can be | ||
* specified as either an absolute number in local units, or as a percentage string e.g. | ||
* `"12%"` which is treated as a percentage of the `fontSize`. Defaults to `0`. | ||
* The width of an outline/halo to be drawn around each text glyph using the `outlineColor` and `outlineOpacity`. | ||
* Can be specified as either an absolute number in local units, or as a percentage string e.g. | ||
* `"12%"` which is treated as a percentage of the `fontSize`. Defaults to `0`, which means | ||
* no outline will be drawn unless an `outlineOffsetX/Y` or `outlineBlur` is set. | ||
*/ | ||
@@ -231,7 +234,76 @@ this.outlineWidth = 0 | ||
* WARNING: This API is experimental and may change. | ||
* The color of the text outline, if `outlineWidth` is greater than zero. Defaults to black. | ||
* The color of the text outline, if `outlineWidth`/`outlineBlur`/`outlineOffsetX/Y` are set. | ||
* Defaults to black. | ||
*/ | ||
this.outlineColor = 0 | ||
this.outlineColor = 0x000000 | ||
/** | ||
* @member {number} outlineOpacity | ||
* WARNING: This API is experimental and may change. | ||
* The opacity of the outline, if `outlineWidth`/`outlineBlur`/`outlineOffsetX/Y` are set. | ||
* Defaults to `1`. | ||
*/ | ||
this.outlineOpacity = 1 | ||
/** | ||
* @member {number|string} outlineBlur | ||
* WARNING: This API is experimental and may change. | ||
* A blur radius applied to the outer edge of the text's outline. If the `outlineWidth` is | ||
* zero, the blur will be applied at the glyph edge, like CSS's `text-shadow` blur radius. | ||
* Can be specified as either an absolute number in local units, or as a percentage string e.g. | ||
* `"12%"` which is treated as a percentage of the `fontSize`. Defaults to `0`. | ||
*/ | ||
this.outlineBlur = 0 | ||
/** | ||
* @member {number|string} outlineOffsetX | ||
* WARNING: This API is experimental and may change. | ||
* A horizontal offset for the text outline. | ||
* Can be specified as either an absolute number in local units, or as a percentage string e.g. `"12%"` | ||
* which is treated as a percentage of the `fontSize`. Defaults to `0`. | ||
*/ | ||
this.outlineOffsetX = 0 | ||
/** | ||
* @member {number|string} outlineOffsetY | ||
* WARNING: This API is experimental and may change. | ||
* A vertical offset for the text outline. | ||
* Can be specified as either an absolute number in local units, or as a percentage string e.g. `"12%"` | ||
* which is treated as a percentage of the `fontSize`. Defaults to `0`. | ||
*/ | ||
this.outlineOffsetY = 0 | ||
/** | ||
* @member {number|string} strokeWidth | ||
* WARNING: This API is experimental and may change. | ||
* The width of an inner stroke drawn inside each text glyph using the `strokeColor` and `strokeOpacity`. | ||
* Can be specified as either an absolute number in local units, or as a percentage string e.g. `"12%"` | ||
* which is treated as a percentage of the `fontSize`. Defaults to `0`. | ||
*/ | ||
this.strokeWidth = 0 | ||
/** | ||
* @member {string|number|THREE.Color} strokeColor | ||
* WARNING: This API is experimental and may change. | ||
* The color of the text stroke, if `strokeWidth` is greater than zero. Defaults to gray. | ||
*/ | ||
this.strokeColor = defaultStrokeColor | ||
/** | ||
* @member {number} strokeOpacity | ||
* WARNING: This API is experimental and may change. | ||
* The opacity of the stroke, if `strokeWidth` is greater than zero. Defaults to `1`. | ||
*/ | ||
this.strokeOpacity = 1 | ||
/** | ||
* @member {number} fillOpacity | ||
* WARNING: This API is experimental and may change. | ||
* The opacity of the glyph's fill from 0 to 1. This behaves like the material's `opacity` but allows | ||
* giving the fill a different opacity than the `strokeOpacity`. A fillOpacity of `0` makes the | ||
* interior of the glyph invisible, leaving just the `strokeWidth`. Defaults to `1`. | ||
*/ | ||
this.fillOpacity = 1 | ||
/** | ||
* @member {number} depthOffset | ||
@@ -401,7 +473,7 @@ * This is a shortcut for setting the material's `polygonOffset` and related properties, | ||
} | ||
// If text outline is present, render it as a preliminary draw using Three's multi-material | ||
// If text outline is configured, render it as a preliminary draw using Three's multi-material | ||
// feature (see GlyphsGeometry which sets up `groups` for this purpose) Doing it with multi | ||
// materials ensures the layers are always rendered consecutively in a consistent order. | ||
// Each layer will trigger onBeforeRender with the appropriate material. | ||
if (this.outlineWidth) { | ||
if (this.outlineWidth || this.outlineBlur || this.outlineOffsetX || this.outlineOffsetY) { | ||
let outlineMaterial = derivedMaterial._outlineMtl | ||
@@ -463,15 +535,37 @@ if (!outlineMaterial) { | ||
uniforms.uTroikaTotalBounds.value.fromArray(blockBounds) | ||
uniforms.uTroikaUseGlyphColors.value = !!textInfo.glyphColors | ||
uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors | ||
let distanceOffset = 0 | ||
let blurRadius = 0 | ||
let strokeWidth = 0 | ||
let fillOpacity | ||
let strokeOpacity | ||
let strokeColor | ||
let offsetX = 0 | ||
let offsetY = 0 | ||
if (isOutline) { | ||
let {outlineWidth} = this | ||
if (typeof outlineWidth === 'string') { | ||
let match = outlineWidth.match(/^([\d.]+)%$/) | ||
let pct = match ? parseFloat(match[1]) : NaN | ||
outlineWidth = (isNaN(pct) ? 0 : pct / 100) * this.fontSize | ||
let {outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity} = this | ||
distanceOffset = this._parsePercent(outlineWidth) || 0 | ||
blurRadius = this._parsePercent(outlineBlur) || 0 | ||
fillOpacity = outlineOpacity | ||
offsetX = this._parsePercent(outlineOffsetX) || 0 | ||
offsetY = this._parsePercent(outlineOffsetY) || 0 | ||
} else { | ||
strokeWidth = this._parsePercent(this.strokeWidth) || 0 | ||
if (strokeWidth) { | ||
strokeColor = this.strokeColor | ||
uniforms.uTroikaStrokeColor.value.set(strokeColor == null ? defaultStrokeColor : strokeColor) | ||
strokeOpacity = this.strokeOpacity | ||
if (strokeOpacity == null) strokeOpacity = 1 | ||
} | ||
distanceOffset = outlineWidth | ||
fillOpacity = this.fillOpacity | ||
} | ||
uniforms.uTroikaDistanceOffset.value = distanceOffset | ||
uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY) | ||
uniforms.uTroikaBlurRadius.value = blurRadius | ||
uniforms.uTroikaStrokeWidth.value = strokeWidth | ||
uniforms.uTroikaStrokeOpacity.value = strokeOpacity | ||
uniforms.uTroikaFillOpacity.value = fillOpacity == null ? 1 : fillOpacity | ||
@@ -500,2 +594,3 @@ let clipRect = this.clipRect | ||
const color = isOutline ? (this.outlineColor || 0) : this.color | ||
if (color == null) { | ||
@@ -529,2 +624,11 @@ delete material.color //inherit from base | ||
_parsePercent(value) { | ||
if (typeof value === 'string') { | ||
let match = value.match(/^([\d.]+)%$/) | ||
let pct = match ? parseFloat(match[1]) : NaN | ||
value = (isNaN(pct) ? 0 : pct / 100) * this.fontSize | ||
} | ||
return value | ||
} | ||
/** | ||
@@ -531,0 +635,0 @@ * @override Custom raycasting to test against the whole text block's max rectangular bounds |
@@ -0,0 +0,0 @@ import { Color, DataTexture, LinearFilter, LuminanceFormat } from 'three' |
@@ -13,2 +13,4 @@ import { createDerivedMaterial, voidMainRegExp } from 'troika-three-utils' | ||
uniform float uTroikaDistanceOffset; | ||
uniform float uTroikaBlurRadius; | ||
uniform vec2 uTroikaPositionOffset; | ||
attribute vec4 aTroikaGlyphBounds; | ||
@@ -26,3 +28,9 @@ attribute float aTroikaGlyphIndex; | ||
vec4 bounds = aTroikaGlyphBounds; | ||
vec4 outlineBounds = vec4(bounds.xy - uTroikaDistanceOffset, bounds.zw + uTroikaDistanceOffset); | ||
bounds.xz += uTroikaPositionOffset.x; | ||
bounds.yw -= uTroikaPositionOffset.y; | ||
vec4 outlineBounds = vec4( | ||
bounds.xy - uTroikaDistanceOffset - uTroikaBlurRadius, | ||
bounds.zw + uTroikaDistanceOffset + uTroikaBlurRadius | ||
); | ||
vec4 clippedBounds = vec4( | ||
@@ -32,2 +40,3 @@ clamp(outlineBounds.xy, uTroikaClipRect.xy, uTroikaClipRect.zw), | ||
); | ||
vec2 clippedXY = (mix(clippedBounds.xy, clippedBounds.zw, position.xy) - bounds.xy) / (bounds.zw - bounds.xy); | ||
@@ -65,2 +74,8 @@ | ||
uniform float uTroikaDistanceOffset; | ||
uniform float uTroikaFillOpacity; | ||
uniform float uTroikaOutlineOpacity; | ||
uniform float uTroikaBlurRadius; | ||
uniform vec3 uTroikaStrokeColor; | ||
uniform float uTroikaStrokeWidth; | ||
uniform float uTroikaStrokeOpacity; | ||
uniform bool uTroikaSDFDebug; | ||
@@ -92,6 +107,19 @@ varying vec2 vTroikaGlyphUV; | ||
float troikaGetTextAlpha(float distanceOffset) { | ||
float troikaGetAADist() { | ||
${''/* | ||
When the standard derivatives extension is available, we choose an antialiasing alpha threshold based | ||
on the potential change in the SDF's alpha from this fragment to its neighbor. This strategy maximizes | ||
readability and edge crispness at all sizes and screen resolutions. | ||
*/} | ||
#if defined(GL_OES_standard_derivatives) || __VERSION__ >= 300 | ||
return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.5; | ||
#else | ||
return vTroikaGlyphDimensions.x / 64.0; | ||
#endif | ||
} | ||
float troikaGetFragDistValue() { | ||
vec2 clampedGlyphUV = clamp(vTroikaGlyphUV, 0.5 / uTroikaSDFGlyphSize, 1.0 - 0.5 / uTroikaSDFGlyphSize); | ||
float distance = troikaGlyphUvToDistance(clampedGlyphUV); | ||
// Extrapolate distance when outside bounds: | ||
@@ -123,17 +151,11 @@ distance += clampedGlyphUV == vTroikaGlyphUV ? 0.0 : | ||
*/} | ||
return distance; | ||
} | ||
float troikaGetEdgeAlpha(float distance, float distanceOffset, float aaDist) { | ||
#if defined(IS_DEPTH_MATERIAL) || defined(IS_DISTANCE_MATERIAL) | ||
float alpha = step(-distanceOffset, -distance); | ||
#else | ||
${''/* | ||
When the standard derivatives extension is available, we choose an antialiasing alpha threshold based | ||
on the potential change in the SDF's alpha from this fragment to its neighbor. This strategy maximizes | ||
readability and edge crispness at all sizes and screen resolutions. | ||
*/} | ||
#if defined(GL_OES_standard_derivatives) || __VERSION__ >= 300 | ||
float aaDist = length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.5; | ||
#else | ||
float aaDist = vTroikaGlyphDimensions.x / 64.0; | ||
#endif | ||
float alpha = smoothstep( | ||
@@ -145,3 +167,3 @@ distanceOffset + aaDist, | ||
#endif | ||
return alpha; | ||
@@ -153,11 +175,22 @@ } | ||
const FRAGMENT_TRANSFORM = ` | ||
float alpha = uTroikaSDFDebug ? | ||
float aaDist = troikaGetAADist(); | ||
float distance = troikaGetFragDistValue(); | ||
float edgeAlpha = uTroikaSDFDebug ? | ||
troikaGlyphUvToSdfValue(vTroikaGlyphUV) : | ||
troikaGetTextAlpha(uTroikaDistanceOffset); | ||
troikaGetEdgeAlpha(distance, uTroikaDistanceOffset, max(aaDist, uTroikaBlurRadius)); | ||
#if !defined(IS_DEPTH_MATERIAL) && !defined(IS_DISTANCE_MATERIAL) | ||
gl_FragColor.a *= alpha; | ||
vec4 fillRGBA = gl_FragColor; | ||
fillRGBA.a *= uTroikaFillOpacity; | ||
vec4 strokeRGBA = uTroikaStrokeWidth == 0.0 ? fillRGBA : vec4(uTroikaStrokeColor, uTroikaStrokeOpacity); | ||
if (fillRGBA.a == 0.0) fillRGBA.rgb = strokeRGBA.rgb; | ||
gl_FragColor = mix(fillRGBA, strokeRGBA, smoothstep( | ||
-uTroikaStrokeWidth - aaDist, | ||
-uTroikaStrokeWidth + aaDist, | ||
distance | ||
)); | ||
gl_FragColor.a *= edgeAlpha; | ||
#endif | ||
if (alpha == 0.0) { | ||
if (edgeAlpha == 0.0) { | ||
discard; | ||
@@ -185,2 +218,9 @@ } | ||
uTroikaDistanceOffset: {value: 0}, | ||
uTroikaOutlineOpacity: {value: 0}, | ||
uTroikaFillOpacity: {value: 1}, | ||
uTroikaPositionOffset: {value: new Vector2()}, | ||
uTroikaBlurRadius: {value: 0}, | ||
uTroikaStrokeWidth: {value: 0}, | ||
uTroikaStrokeColor: {value: new Color()}, | ||
uTroikaStrokeOpacity: {value: 1}, | ||
uTroikaOrient: {value: new Matrix3()}, | ||
@@ -187,0 +227,0 @@ uTroikaUseGlyphColors: {value: true}, |
@@ -21,3 +21,3 @@ /* | ||
import tinyInflate from 'tiny-inflate' | ||
import { inflateSync } from 'fflate' | ||
@@ -126,3 +126,3 @@ export function convert_streams(bufferIn) { | ||
var uncompressedData = new Uint8Array(TableDirectoryEntry.origLength) | ||
tinyInflate( | ||
inflateSync( | ||
new Uint8Array(compressedData, 2), //skip deflate header | ||
@@ -129,0 +129,0 @@ uncompressedData |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ /** |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
1187881
32
8951
363
+ Addedtroika-three-utils@0.37.0(transitive)
+ Addedtroika-worker-utils@0.37.0(transitive)
- Removedtroika-three-utils@0.36.0(transitive)
- Removedtroika-worker-utils@0.36.0(transitive)
Updatedtroika-three-utils@^0.37.0
Updatedtroika-worker-utils@^0.37.0