diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..3fc226a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + parser: 'typescript-eslint-parser', + parserOptions: { + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + plugins: ['prettier'], + rules: { + 'prettier/prettier': 'error', + }, +}; diff --git a/.gitignore b/.gitignore index 55b151a..ea3b429 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .cache dist +.idea diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5e9cd47 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - '9' diff --git a/application.tsx b/application.tsx index ff2a4f5..df7d121 100644 --- a/application.tsx +++ b/application.tsx @@ -1,25 +1,24 @@ -import {h} from 'preact' -import {StyleSheet, css} from 'aphrodite' -import {ReloadableComponent, SerializedComponent} from './reloadable' +import { h } from 'preact' +import { StyleSheet, css } from 'aphrodite' +import { ReloadableComponent, SerializedComponent } from './reloadable' -import {importFromBGFlameGraph} from './import/bg-flamegraph' -import {importFromStackprof} from './import/stackprof' -import {importFromChromeTimeline, importFromChromeCPUProfile} from './import/chrome' +import { importFromBGFlameGraph } from './import/bg-flamegraph' +import { importFromStackprof } from './import/stackprof' +import { importFromChromeTimeline, importFromChromeCPUProfile } from './import/chrome' import { FlamechartRenderer } from './flamechart-renderer' import { CanvasContext } from './canvas-context' -import {Profile, Frame} from './profile' -import {Flamechart} from './flamechart' +import { Profile, Frame } from './profile' +import { Flamechart } from './flamechart' import { FlamechartView } from './flamechart-view' import { FontFamily, FontSize, Colors } from './style' - declare function require(x: string): any const exampleProfileURL = require('./sample/perf-vertx-stacks-01-collapsed-all.txt') const enum SortOrder { CHRONO, - LEFT_HEAVY + LEFT_HEAVY, } interface ApplicationState { @@ -56,7 +55,7 @@ function importProfile(contents: string, fileName: string): Profile | null { // Second pass: Try to guess what file format it is based on structure try { const parsed = JSON.parse(contents) - if (Array.isArray(parsed) && parsed[parsed.length - 1].name === "CpuProfile") { + if (Array.isArray(parsed) && parsed[parsed.length - 1].name === 'CpuProfile') { console.log('Importing as Chrome CPU Profile') return importFromChromeTimeline(parsed) } else if ('nodes' in parsed && 'samples' in parsed && 'timeDeltas' in parsed) { @@ -97,31 +96,51 @@ export class Toolbar extends ReloadableComponent { render() { const help = (
- + ❓Help
) if (!this.props.profile) { - return
-
{help}
- 🔬speedscope -
+ return ( +
+
{help}
+ 🔬speedscope +
+ ) } - return
-
-
- 🕰Time Order + return ( +
+
+
+ 🕰Time Order +
+
+ ⬅️Left Heavy +
+ {help}
-
- ⬅️Left Heavy -
- {help} + {this.props.profile.getName()} +
🔬speedscope
- {this.props.profile.getName()} -
🔬speedscope
-
+ ) } } @@ -188,7 +207,7 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { flamechartRenderer: null, sortedFlamechart: null, sortedFlamechartRenderer: null, - sortOrder: SortOrder.CHRONO + sortOrder: SortOrder.CHRONO, } } @@ -205,7 +224,7 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { if (this.canvasContext && flamechart && sortedFlamechart) { this.setState({ flamechartRenderer: new FlamechartRenderer(this.canvasContext, flamechart), - sortedFlamechartRenderer: new FlamechartRenderer(this.canvasContext, sortedFlamechart) + sortedFlamechartRenderer: new FlamechartRenderer(this.canvasContext, sortedFlamechart), }) } } @@ -248,7 +267,7 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { getTotalWeight: profile.getTotalWeight.bind(profile), forEachCall: profile.forEachCall.bind(profile), formatValue: profile.formatValue.bind(profile), - getColorBucketForFrame + getColorBucketForFrame, }) const flamechartRenderer = new FlamechartRenderer(this.canvasContext, flamechart) @@ -256,29 +275,32 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { getTotalWeight: profile.getTotalNonIdleWeight.bind(profile), forEachCall: profile.forEachCallGrouped.bind(profile), formatValue: profile.formatValue.bind(profile), - getColorBucketForFrame + getColorBucketForFrame, }) const sortedFlamechartRenderer = new FlamechartRenderer(this.canvasContext, sortedFlamechart) console.timeEnd('import') console.time('first setState') - this.setState({ - profile, - flamechart, - flamechartRenderer, - sortedFlamechart, - sortedFlamechartRenderer, - loading: false - }, () => { - console.timeEnd('first setState') - }) + this.setState( + { + profile, + flamechart, + flamechartRenderer, + sortedFlamechart, + sortedFlamechartRenderer, + loading: false, + }, + () => { + console.timeEnd('first setState') + }, + ) } loadFromFile(file: File) { this.setState({ loading: true }, () => { requestAnimationFrame(() => { - const reader = new FileReader + const reader = new FileReader() reader.addEventListener('loadend', () => { this.loadFromString(file.name, reader.result) }) @@ -290,9 +312,11 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { loadExample = () => { this.setState({ loading: true }) const filename = 'perf-vertx-stacks-01-collapsed-all.txt' - fetch(exampleProfileURL).then(resp => resp.text()).then(data => { - this.loadFromString(filename, data) - }) + fetch(exampleProfileURL) + .then(resp => resp.text()) + .then(data => { + this.loadFromString(filename, data) + }) } onDrop = (ev: DragEvent) => { @@ -310,11 +334,11 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { onWindowKeyPress = (ev: KeyboardEvent) => { if (ev.key === '1') { this.setState({ - sortOrder: SortOrder.CHRONO + sortOrder: SortOrder.CHRONO, }) } else if (ev.key === '2') { this.setState({ - sortOrder: SortOrder.LEFT_HEAVY + sortOrder: SortOrder.LEFT_HEAVY, }) } } @@ -328,10 +352,10 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { } flamechartView: FlamechartView | null = null - flamechartRef = (view: FlamechartView | null) => this.flamechartView = view + flamechartRef = (view: FlamechartView | null) => (this.flamechartView = view) subcomponents() { return { - flamechart: this.flamechartView + flamechart: this.flamechartView, } } @@ -343,34 +367,71 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { } renderLanding() { - return
-
-

👋 Hi there! Welcome to 🔬speedscope, an interactive{' '} - flamegraph visualizer. - Use it to help you make your software faster.

-

Drag and drop a profile file onto this window to get started, - click the big blue button below to browse for a profile to explore, or{' '} - click here{' '} - to load an example profile.

+ return ( +
+
+

+ 👋 Hi there! Welcome to 🔬speedscope, an interactive{' '} + + flamegraph + {' '} + visualizer. Use it to help you make your software faster. +

+

+ Drag and drop a profile file onto this window to get started, click the big blue button + below to browse for a profile to explore, or{' '} + + click here + {' '} + to load an example profile. +

-
- - +
+ + +
+ +

+ See the{' '} + + documentation + {' '} + for information about supported file formats, keyboard shortcuts, and how to navigate + around the profile. +

+ +

+ speedscope is open source. Please{' '} + + report any issues on GitHub + . +

- -

See the documentation for - information about supported file formats, keyboard shortcuts, and how - to navigate around the profile.

- -

speedscope is open source. - Please report any issues on GitHub.

-
+ ) } renderLoadingBar() { - return
+ return
} setSortOrder = (sortOrder: SortOrder) => { @@ -383,23 +444,36 @@ export class Application extends ReloadableComponent<{}, ApplicationState> { } render() { - const {flamechart, flamechartRenderer, sortedFlamechart, sortedFlamechartRenderer, sortOrder, loading} = this.state + const { + flamechart, + flamechartRenderer, + sortedFlamechart, + sortedFlamechartRenderer, + sortOrder, + loading, + } = this.state const flamechartToView = sortOrder == SortOrder.CHRONO ? flamechart : sortedFlamechart - const flamechartRendererToUse = sortOrder == SortOrder.CHRONO ? flamechartRenderer : sortedFlamechartRenderer + const flamechartRendererToUse = + sortOrder == SortOrder.CHRONO ? flamechartRenderer : sortedFlamechartRenderer - return
- - - {loading ? - this.renderLoadingBar() : - this.canvasContext && flamechartToView && flamechartRendererToUse ? + return ( +
+ + + {loading ? ( + this.renderLoadingBar() + ) : this.canvasContext && flamechartToView && flamechartRendererToUse ? ( : - this.renderLanding()} -
+ flamechart={flamechartToView} + /> + ) : ( + this.renderLanding() + )} +
+ ) } } @@ -409,23 +483,25 @@ const style = StyleSheet.create({ width: '100vw', height: '100vh', zIndex: -1, - pointerEvents: 'none' + pointerEvents: 'none', }, loading: { height: 3, marginBottom: -3, background: Colors.DARK_BLUE, transformOrigin: '0% 50%', - animationName: [{ - from: { - transform: `scaleX(0)` + animationName: [ + { + from: { + transform: `scaleX(0)`, + }, + to: { + transform: `scaleX(1)`, + }, }, - to: { - transform: `scaleX(1)` - } - }], - animationTimingFunction: "cubic-bezier(0, 1, 0, 1)", - animationDuration: "30s" + ], + animationTimingFunction: 'cubic-bezier(0, 1, 0, 1)', + animationDuration: '30s', }, root: { width: '100vw', @@ -435,27 +511,27 @@ const style = StyleSheet.create({ flexDirection: 'column', position: 'relative', fontFamily: FontFamily.MONOSPACE, - lineHeight: '20px' + lineHeight: '20px', }, landingContainer: { display: 'flex', alignItems: 'center', justifyContent: 'center', - flex: 1 + flex: 1, }, landingMessage: { - maxWidth: 600 + maxWidth: 600, }, landingP: { - marginBottom: 16 + marginBottom: 16, }, hide: { - display: 'none' + display: 'none', }, browseButtonContainer: { display: 'flex', alignItems: 'center', - justifyContent: 'center' + justifyContent: 'center', }, browseButton: { marginBottom: 16, @@ -467,12 +543,12 @@ const style = StyleSheet.create({ lineHeight: '72px', background: Colors.DARK_BLUE, color: 'white', - cursor: 'pointer' + cursor: 'pointer', }, link: { color: Colors.LIGHT_BLUE, cursor: 'pointer', - textDecoration: 'none' + textDecoration: 'none', }, toolbar: { height: 18, @@ -482,7 +558,7 @@ const style = StyleSheet.create({ fontFamily: FontFamily.MONOSPACE, fontSize: FontSize.TITLE, lineHeight: '18px', - userSelect: 'none' + userSelect: 'none', }, toolbarLeft: { position: 'absolute', @@ -513,24 +589,23 @@ const style = StyleSheet.create({ marginLeft: 2, ':hover': { background: Colors.GRAY, - cursor: 'pointer' - } + cursor: 'pointer', + }, }, toolbarTabActive: { background: Colors.LIGHT_BLUE, ':hover': { - background: Colors.LIGHT_BLUE - } + background: Colors.LIGHT_BLUE, + }, }, noLinkStyle: { textDecoration: 'none', - color: 'inherit' + color: 'inherit', }, emoji: { display: 'inline-block', verticalAlign: 'middle', paddingTop: '0px', - marginRight: '0.3em' - } + marginRight: '0.3em', + }, }) - diff --git a/canvas-context.ts b/canvas-context.ts index c87d923..813dca1 100644 --- a/canvas-context.ts +++ b/canvas-context.ts @@ -1,11 +1,25 @@ import regl from 'regl' -import { RectangleBatchRenderer, RectangleBatch, RectangleBatchRendererProps } from './rectangle-batch-renderer'; -import { ViewportRectangleRenderer, ViewportRectangleRendererProps } from './overlay-rectangle-renderer'; -import { TextureCachedRenderer, TextureRenderer, TextureRendererProps } from './texture-catched-renderer' +import { + RectangleBatchRenderer, + RectangleBatch, + RectangleBatchRendererProps, +} from './rectangle-batch-renderer' +import { + ViewportRectangleRenderer, + ViewportRectangleRendererProps, +} from './overlay-rectangle-renderer' +import { + TextureCachedRenderer, + TextureRenderer, + TextureRendererProps, +} from './texture-catched-renderer' import { StatsPanel } from './stats' -import { Vec2, Rect } from './math'; -import { FlamechartColorPassRenderer, FlamechartColorPassRenderProps } from './flamechart-color-pass-renderer'; +import { Vec2, Rect } from './math' +import { + FlamechartColorPassRenderer, + FlamechartColorPassRenderProps, +} from './flamechart-color-pass-renderer' type FrameCallback = () => void @@ -26,11 +40,11 @@ export class CanvasContext { this.gl = regl({ canvas: canvas, attributes: { - antialias: false + antialias: false, }, extensions: ['ANGLE_instanced_arrays', 'WEBGL_depth_texture'], optionalExtensions: ['EXT_disjoint_timer_query'], - profile: true + profile: true, }) ;(window as any)['CanvasContext'] = this this.rectangleBatchRenderer = new RectangleBatchRenderer(this.gl) @@ -45,15 +59,18 @@ export class CanvasContext { }, viewportY: (context: regl.Context, props: SetViewportScopeProps) => { return props.physicalBounds.top() - } + }, }, viewport: (context, props) => { const { physicalBounds } = props return { x: physicalBounds.left(), - y: window.devicePixelRatio * window.innerHeight - physicalBounds.top() - physicalBounds.height(), + y: + window.devicePixelRatio * window.innerHeight - + physicalBounds.top() - + physicalBounds.height(), width: physicalBounds.width(), - height: physicalBounds.height() + height: physicalBounds.height(), } }, scissor: (context, props) => { @@ -62,12 +79,15 @@ export class CanvasContext { enable: true, box: { x: physicalBounds.left(), - y: window.devicePixelRatio * window.innerHeight - physicalBounds.top() - physicalBounds.height(), + y: + window.devicePixelRatio * window.innerHeight - + physicalBounds.top() - + physicalBounds.height(), width: physicalBounds.width(), - height: physicalBounds.height() - } + height: physicalBounds.height(), + }, } - } + }, }) } @@ -139,11 +159,11 @@ export class CanvasContext { }): TextureCachedRenderer { return new TextureCachedRenderer(this.gl, { ...options, - textureRenderer: this.textureRenderer + textureRenderer: this.textureRenderer, }) } - drawViewportRectangle(props: ViewportRectangleRendererProps){ + drawViewportRectangle(props: ViewportRectangleRendererProps) { this.viewportRectangleRenderer.render(props) } @@ -151,7 +171,7 @@ export class CanvasContext { const bounds = el.getBoundingClientRect() const physicalBounds = new Rect( new Vec2(bounds.left * window.devicePixelRatio, bounds.top * window.devicePixelRatio), - new Vec2(bounds.width * window.devicePixelRatio, bounds.height * window.devicePixelRatio) + new Vec2(bounds.width * window.devicePixelRatio, bounds.height * window.devicePixelRatio), ) this.setViewportScope({ physicalBounds }, cb) } @@ -163,4 +183,4 @@ export class CanvasContext { getMaxTextureSize() { return this.gl.limits.maxTextureSize } -} \ No newline at end of file +} diff --git a/color.ts b/color.ts index 1b558a0..e700cd3 100644 --- a/color.ts +++ b/color.ts @@ -1,23 +1,32 @@ -import {Frame} from './profile' +import { Frame } from './profile' export class Color { - constructor(readonly r: number = 0, readonly g: number = 0, readonly b: number = 0, readonly a: number = 1) {} + constructor( + readonly r: number = 0, + readonly g: number = 0, + readonly b: number = 0, + readonly a: number = 1, + ) {} static fromLumaChromaHue(L: number, C: number, H: number) { // https://en.wikipedia.org/wiki/HSL_and_HSV#From_luma/chroma/hue const hPrime = H / 60 const X = C * (1 - Math.abs(hPrime % 2 - 1)) - const [R1, G1, B1] = ( - hPrime < 1 ? [C, X, 0] : - hPrime < 2 ? [X, C, 0] : - hPrime < 3 ? [0, C, X] : - hPrime < 4 ? [0, X, C] : - hPrime < 5 ? [X, 0, C] : - [C, 0, X] - ) + const [R1, G1, B1] = + hPrime < 1 + ? [C, X, 0] + : hPrime < 2 + ? [X, C, 0] + : hPrime < 3 + ? [0, C, X] + : hPrime < 4 + ? [0, X, C] + : hPrime < 5 + ? [X, 0, C] + : [C, 0, X] - const m = L - (0.30 * R1 + 0.59 * G1 + 0.11 * B1) + const m = L - (0.3 * R1 + 0.59 * G1 + 0.11 * B1) return new Color(R1 + m, G1 + m, B1 + m, 1.0) } @@ -64,11 +73,13 @@ export class FrameColorGenerator { const x = 2 * fract(100.0 * ratio) - 1 const L = 0.85 - 0.1 * x - const C = 0.20 + 0.1 * x + const C = 0.2 + 0.1 * x const H = 360 * ratio this.frameToColor.set(frames[i], Color.fromLumaChromaHue(L, C, H)) } } - getColorForFrame(f: Frame) { return this.frameToColor.get(f) || new Color() } -} \ No newline at end of file + getColorForFrame(f: Frame) { + return this.frameToColor.get(f) || new Color() + } +} diff --git a/demangle-cpp.ts b/demangle-cpp.ts index ec2e3e5..e6a9a0b 100644 --- a/demangle-cpp.ts +++ b/demangle-cpp.ts @@ -1,24 +1,24 @@ -let cppfilt: (name: string) => string; -const cache = new Map(); +let cppfilt: (name: string) => string +const cache = new Map() // This function converts a mangled C++ name such as "__ZNK7Support6ColorFeqERKS0_" // into a human-readable symbol (in this case "Support::ColorF::==(Support::ColorF&)") export function demangleCpp(name: string): string { if (name.startsWith('__Z')) { - let result = cache.get(name); + let result = cache.get(name) if (result !== undefined) { - name = result; + name = result } else { if (!cppfilt) { - cppfilt = new Function('exports', code)(); + cppfilt = new Function('exports', code)() } - result = cppfilt(name.slice(1)); + result = cppfilt(name.slice(1)) result = result === '(null)' ? name : result - cache.set(name, result); - name = result; + cache.set(name, result) + name = result } } - return name; + return name } // This was taken from https://d.fuqu.jp/c++filtjs/ @@ -29,4 +29,4 @@ Ae[n+b|0]=0;var k=Se[a]+1|0;Se[a]=k;var u=n}else if(_<<24>>24==57){var c=Wr(r,4) Se[Ar+8>>2]=Dr;var Cr=Ar,a=Cr>>2}while(0);var Cr;if(0==(0|Er))break;var Lr=h+(r+20)|0,Fr=(Se[Lr>>2]<<2)+vi+304|0,Xr=(0|pr)==(0|Se[Fr>>2]);do{if(Xr){if(Se[Fr>>2]=Cr,0!=(0|Cr))break;var jr=Se[vi+4>>2]&(1<>2]^-1);Se[vi+4>>2]=jr;break e}if(Er>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";var Ur=Er+16|0;if((0|Se[Ur>>2])==(0|pr)?Se[Ur>>2]=Cr:Se[Er+20>>2]=Cr,0==(0|Cr))break e}while(0);if(Cr>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[a+6]=Er;var xr=Me[f+(s+2)];if(0!=(0|xr)){if(xr>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[a+4]=xr,Se[xr+24>>2]=Cr}var zr=Me[f+(s+3)];if(0==(0|zr))break;if(zr>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[a+5]=zr,Se[zr+24>>2]=Cr}while(0);if(Se[i+1]=1|lr,Se[er+lr>>2]=lr,(0|S)!=(0|Se[vi+20>>2])){var Vr=lr;break}Se[vi+8>>2]=lr;break r}Se[ir>>2]=vr&-2,Se[i+1]=1|M,Se[er+M>>2]=M;var Vr=M}while(0);var Vr;if(Vr>>>0<256){var Br=Vr>>>2&1073741822,Hr=(Br<<2)+vi+40|0,Kr=Me[vi>>2],Yr=1<<(Vr>>>3),Gr=0==(Kr&Yr|0);do{if(!Gr){var Wr=(Br+2<<2)+vi+40|0,Zr=Me[Wr>>2];if(Zr>>>0>=Me[vi+16>>2]>>>0){var Qr=Zr,qr=Wr;break}throw Ka(),"Reached an unreachable!"}Se[vi>>2]=Kr|Yr;var Qr=Hr,qr=(Br+2<<2)+vi+40|0}while(0);var qr,Qr;Se[qr>>2]=S,Se[Qr+12>>2]=S,Se[i+2]=Qr,Se[i+3]=Hr;break r}var $r=S,Jr=Vr>>>8,ra=0==(0|Jr);do if(ra)var aa=0;else{if(Vr>>>0>16777215){var aa=31;break}var ea=(Jr+1048320|0)>>>16&8,va=Jr<>>16&4,_a=va<>>16&2,na=14-(fa|ea|sa)+(_a<>>15)|0,aa=Vr>>>((na+7|0)>>>0)&1|na<<1}while(0);var aa,oa=(aa<<2)+vi+304|0;Se[i+7]=aa,Se[i+5]=0,Se[i+4]=0;var la=Se[vi+4>>2],ba=1<>2]=ua,Se[oa>>2]=$r,Se[i+6]=oa,Se[i+3]=S,Se[i+2]=S}else{if(31==(0|aa))var ca=0;else var ca=25-(aa>>>1)|0;for(var ca,ha=Vr<>2];;){var da,ha;if((Se[da+4>>2]&-8|0)==(0|Vr)){var wa=da+8|0,pa=Me[wa>>2],Ea=Me[vi+16>>2],Aa=da>>>0>>0;do if(!Aa){if(pa>>>0>>0)break;Se[pa+12>>2]=$r,Se[wa>>2]=$r,Se[i+2]=pa,Se[i+3]=da,Se[i+6]=0;break e}while(0);throw Ka(),"Reached an unreachable!"}var ga=(ha>>>31<<2)+da+16|0,ya=Me[ga>>2];if(0==(0|ya)){if(ga>>>0>=Me[vi+16>>2]>>>0){Se[ga>>2]=$r,Se[i+6]=da,Se[i+3]=S,Se[i+2]=S;break e}throw Ka(),"Reached an unreachable!"}var ha=ha<<1,da=ya}}while(0);var ma=Se[vi+32>>2]-1|0;if(Se[vi+32>>2]=ma,0!=(0|ma))break r;ta();break r}while(0);throw Ka(),"Reached an unreachable!"}while(0)}function ta(){var r=Se[vi+452>>2],a=0==(0|r);r:do if(!a)for(var e=r;;){var e,i=Se[e+8>>2];if(0==(0|i))break r;var e=i}while(0);Se[vi+32>>2]=-1}function fa(r,a){if(0==(0|r))var e=Jr(a),i=e;else var v=la(r,a),i=v;var i;return i}function _a(r,a){var e,i=r>>>0<9;do if(i)var v=Jr(a),t=v;else{var f=r>>>0<16?16:r,_=0==(f-1&f|0);r:do if(_)var s=f;else{if(f>>>0<=16){var s=16;break}for(var n=16;;){var n,o=n<<1;if(o>>>0>=f>>>0){var s=o;break r}var n=o}}while(0);var s;if((-64-s|0)>>>0>a>>>0){if(a>>>0<11)var l=16;else var l=a+11&-8;var l,b=Jr(l+(s+12)|0);if(0==(0|b)){var t=0;break}var k=b-8|0;if(0==((b>>>0)%(s>>>0)|0))var u=k,c=0;else{var h=b+(s-1)&-s,d=h-8|0,w=k;if((d-w|0)>>>0>15)var p=d;else var p=h+(s-8)|0;var p,E=p-w|0,e=(b-4|0)>>2,A=Se[e],g=(A&-8)-E|0;if(0==(3&A|0)){var y=Se[k>>2]+E|0;Se[p>>2]=y,Se[p+4>>2]=g;var u=p,c=0}else{var m=p+4|0,S=g|1&Se[m>>2]|2;Se[m>>2]=S;var M=g+(p+4)|0,C=1|Se[M>>2];Se[M>>2]=C;var R=E|1&Se[e]|2;Se[e]=R;var T=b+(E-4)|0,O=1|Se[T>>2];Se[T>>2]=O;var u=p,c=b}}var c,u,N=u+4|0,I=Me[N>>2],P=0==(3&I|0);do if(P)var D=0;else{var L=I&-8;if(L>>>0<=(l+16|0)>>>0){var D=0;break}var F=L-l|0;Se[N>>2]=l|1&I|2,Se[u+(4|l)>>2]=3|F;var X=u+(4|L)|0,j=1|Se[X>>2];Se[X>>2]=j;var D=l+(u+8)|0}while(0);var D;0!=(0|c)&&va(c),0!=(0|D)&&va(D);var t=u+8|0}else{var U=Je();Se[U>>2]=12;var t=0}}while(0);var t;return t}function sa(r,a,e,i){var v,t;0==(0|Se[ti>>2])&&ba();var f=0==(0|i),_=0==(0|r);do{if(f){if(_){var s=Jr(0),n=s;t=30;break}var o=r<<2;if(o>>>0<11){var l=0,b=16;t=9;break}var l=0,b=o+11&-8;t=9;break}if(_){var n=i;t=30;break}var l=i,b=0;t=9;break}while(0);do if(9==t){var b,l,k=0==(1&e|0);r:do if(k){if(_){var u=0,c=0;break}for(var h=0,d=0;;){var d,h,w=Me[a+(d<<2)>>2];if(w>>>0<11)var p=16;else var p=w+11&-8;var p,E=p+h|0,A=d+1|0;if((0|A)==(0|r)){var u=0,c=E;break r}var h=E,d=A}}else{var g=Me[a>>2];if(g>>>0<11)var y=16;else var y=g+11&-8;var y,u=y,c=y*r|0}while(0);var c,u,m=Jr(b-4+c|0);if(0==(0|m)){var n=0;break}var S=m-8|0,M=Se[m-4>>2]&-8;if(0!=(2&e|0)){var C=-4-b+M|0;Fa(m,0,C,1)}if(0==(0|l)){var R=m+c|0,T=M-c|3;Se[m+(c-4)>>2]=T;var O=R,v=O>>2,N=c}else var O=l,v=O>>2,N=M;var N,O;Se[v]=m;var I=r-1|0,P=0==(0|I);r:do if(P)var D=S,L=N;else if(0==(0|u))for(var F=S,X=N,j=0;;){var j,X,F,U=Me[a+(j<<2)>>2];if(U>>>0<11)var x=16;else var x=U+11&-8;var x,z=X-x|0;Se[F+4>>2]=3|x;var V=F+x|0,B=j+1|0;if(Se[(B<<2>>2)+v]=x+(F+8)|0,(0|B)==(0|I)){var D=V,L=z;break r}var F=V,X=z,j=B}else for(var H=3|u,K=u+8|0,Y=S,G=N,W=0;;){var W,G,Y,Z=G-u|0;Se[Y+4>>2]=H;var Q=Y+u|0,q=W+1|0;if(Se[(q<<2>>2)+v]=Y+K|0,(0|q)==(0|I)){var D=Q,L=Z;break r}var Y=Q,G=Z,W=q}while(0);var L,D;Se[D+4>>2]=3|L;var n=O}while(0);var n;return n}function na(r){var a=r>>2;0==(0|Se[ti>>2])&&ba();var e=Me[vi+24>>2];if(0==(0|e))var i=0,v=0,t=0,f=0,_=0,s=0,n=0;else{for(var o=Me[vi+12>>2],l=o+40|0,b=vi+444|0,k=l,u=l,c=1;;){var c,u,k,b,h=Me[b>>2],d=h+8|0;if(0==(7&d|0))var w=0;else var w=7&-d;for(var w,p=b+4|0,E=h+w|0,A=c,g=u,y=k;;){var y,g,A,E;if(E>>>0>>0)break;if(E>>>0>=(h+Se[p>>2]|0)>>>0|(0|E)==(0|e))break;var m=Se[E+4>>2];if(7==(0|m))break;var S=m&-8,M=S+y|0;if(1==(3&m|0))var C=A+1|0,R=S+g|0;else var C=A,R=g;var R,C,E=E+S|0,A=C,g=R,y=M}var T=Me[b+8>>2];if(0==(0|T))break;var b=T,k=y,u=g,c=A}var O=Se[vi+432>>2],i=y,v=A,t=o,f=g,_=O-y|0,s=Se[vi+436>>2],n=O-g|0}var n,s,_,f,t,v,i;Se[a]=i,Se[a+1]=v,Se[a+2]=0,Se[a+3]=0,Se[a+4]=_,Se[a+5]=s,Se[a+6]=0,Se[a+7]=n,Se[a+8]=f,Se[a+9]=t}function oa(){0==(0|Se[ti>>2])&&ba();var r=Me[vi+24>>2],a=0==(0|r);r:do if(a)var e=0,i=0,v=0;else for(var t=Se[vi+436>>2],f=Me[vi+432>>2],_=vi+444|0,s=f-40-Se[vi+12>>2]|0;;){var s,_,n=Me[_>>2],o=n+8|0;if(0==(7&o|0))var l=0;else var l=7&-o;for(var l,b=_+4|0,k=n+l|0,u=s;;){var u,k;if(k>>>0>>0)break;if(k>>>0>=(n+Se[b>>2]|0)>>>0|(0|k)==(0|r))break;var c=Se[k+4>>2];if(7==(0|c))break;var h=c&-8,d=1==(3&c|0)?h:0,w=u-d|0,k=k+h|0,u=w}var p=Me[_+8>>2];if(0==(0|p)){var e=t,i=f,v=u;break r}var _=p,s=u}while(0);var v,i,e,E=Se[Se[qe>>2]+12>>2],A=(Qa(E,0|He.__str339,(ne=Oe,Oe+=4,Se[ne>>2]=e,ne)),Se[Se[qe>>2]+12>>2]),g=(Qa(A,0|He.__str1340,(ne=Oe,Oe+=4,Se[ne>>2]=i,ne)),Se[Se[qe>>2]+12>>2]);Qa(g,0|He.__str2341,(ne=Oe,Oe+=4,Se[ne>>2]=v,ne))}function la(r,a){var e,i,v,t=a>>>0>4294967231;r:do{if(!t){var f=r-8|0,_=f,i=(r-4|0)>>2,s=Me[i],n=s&-8,o=n-8|0,l=r+o|0,b=f>>>0>2]>>>0;do if(!b){var k=3&s;if(!(1!=(0|k)&(0|o)>-8))break;var e=(r+(n-4)|0)>>2;if(0==(1&Se[e]|0))break;if(a>>>0<11)var u=16;else var u=a+11&-8;var u,c=0==(0|k);do{if(c){var h=ka(_,u),d=0,w=h;v=17;break}if(n>>>0>>0){if((0|l)!=(0|Se[vi+24>>2])){v=21;break}var p=Se[vi+12>>2]+n|0;if(p>>>0<=u>>>0){v=21;break}var E=p-u|0,A=r+(u-8)|0;Se[i]=u|1&s|2;var g=1|E;Se[r+(u-4)>>2]=g,Se[vi+24>>2]=A,Se[vi+12>>2]=E;var d=0,w=_;v=17;break}var y=n-u|0;if(y>>>0<=15){var d=0,w=_;v=17;break}Se[i]=u|1&s|2,Se[r+(u-4)>>2]=3|y;var m=1|Se[e];Se[e]=m;var d=r+u|0,w=_;v=17;break}while(0);do if(17==v){var w,d;if(0==(0|w))break;0!=(0|d)&&va(d);var S=w+8|0;break r}while(0);var M=Jr(a);if(0==(0|M)){var S=0;break r}var C=0==(3&Se[i]|0)?8:4,R=n-C|0,T=R>>>0>>0?R:a;Pa(M,r,T,1),va(r);var S=M;break r}while(0);throw Ka(),"Reached an unreachable!"}var O=Je();Se[O>>2]=12;var S=0}while(0);var S;return S}function ba(){if(0==(0|Se[ti>>2])){var r=qa(8);if(0!=(r-1&r|0))throw Ka(),"Reached an unreachable!";Se[ti+8>>2]=r,Se[ti+4>>2]=r,Se[ti+12>>2]=-1,Se[ti+16>>2]=2097152,Se[ti+20>>2]=0,Se[vi+440>>2]=0;var a=$a(0);Se[ti>>2]=a&-16^1431655768}}function ka(r,a){var e=Se[r+4>>2]&-8,i=a>>>0<256;do if(i)var v=0;else{if(e>>>0>=(a+4|0)>>>0&&(e-a|0)>>>0<=Se[ti+8>>2]<<1>>>0){var v=r;break}var v=0}while(0);var v;return v}function ua(r){for(var a,e=vi+444|0,a=e>>2;;){var e,i=Me[a];if(i>>>0<=r>>>0&&(i+Se[a+1]|0)>>>0>r>>>0){var v=e;break}var t=Me[a+2];if(0==(0|t)){var v=0;break}var e=t,a=e>>2}var v;return v}function ca(r,a){var e=r,i=r+8|0;if(0==(7&i|0))var v=0;else var v=7&-i;var v,t=a-v|0;Se[vi+24>>2]=e+v|0,Se[vi+12>>2]=t,Se[v+(e+4)>>2]=1|t,Se[a+(e+4)>>2]=40;var f=Se[ti+16>>2];Se[vi+28>>2]=f}function ha(){for(var r=0;;){var r,a=r<<1,e=(a<<2)+vi+40|0;Se[vi+(a+3<<2)+40>>2]=e,Se[vi+(a+2<<2)+40>>2]=e;var i=r+1|0;if(32==(0|i))break;var r=i}}function da(r,a,e){var i,v,t,f,_=a>>2,s=r>>2,n=r+8|0;if(0==(7&n|0))var o=0;else var o=7&-n;var o,l=a+8|0;if(0==(7&l|0))var b=0,t=b>>2;else var b=7&-l,t=b>>2;var b,k=a+b|0,u=k,c=o+e|0,v=c>>2,h=r+c|0,d=h,w=k-(r+o)-e|0;Se[(o+4>>2)+s]=3|e;var p=(0|u)==(0|Se[vi+24>>2]);r:do if(p){var E=Se[vi+12>>2]+w|0;Se[vi+12>>2]=E,Se[vi+24>>2]=d;var A=1|E;Se[v+(s+1)]=A}else if((0|u)==(0|Se[vi+20>>2])){var g=Se[vi+8>>2]+w|0;Se[vi+8>>2]=g,Se[vi+20>>2]=d;var y=1|g;Se[v+(s+1)]=y;var m=r+g+c|0;Se[m>>2]=g}else{var S=Me[t+(_+1)];if(1==(3&S|0)){var M=S&-8,C=S>>>3,R=S>>>0<256;a:do if(R){var T=Me[((8|b)>>2)+_],O=Me[t+(_+3)];if((0|T)!=(0|O)){var N=((S>>>2&1073741822)<<2)+vi+40|0,I=(0|T)==(0|N);do{if(!I){if(T>>>0>2]>>>0){f=18;break}f=15;break}f=15}while(0);do if(15==f){if((0|O)!=(0|N)&&O>>>0>2]>>>0)break;Se[T+12>>2]=O,Se[O+8>>2]=T;break a}while(0);throw Ka(),"Reached an unreachable!"}var P=Se[vi>>2]&(1<>2]=P}else{var D=k,L=Me[((24|b)>>2)+_],F=Me[t+(_+3)],X=(0|F)==(0|D);do if(X){var j=16|b,U=j+(a+4)|0,x=Se[U>>2];if(0==(0|x)){var z=a+j|0,V=Se[z>>2];if(0==(0|V)){var B=0,i=B>>2;break}var H=z,K=V}else{var H=U,K=x;f=25}for(;;){var K,H,Y=K+20|0,G=Se[Y>>2];if(0==(0|G)){var W=K+16|0,Z=Me[W>>2];if(0==(0|Z))break;var H=W,K=Z}else var H=Y,K=G}if(H>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[H>>2]=0;var B=K,i=B>>2}else{var Q=Me[((8|b)>>2)+_];if(Q>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[Q+12>>2]=F,Se[F+8>>2]=Q;var B=F,i=B>>2}while(0);var B;if(0==(0|L))break;var q=b+(a+28)|0,$=(Se[q>>2]<<2)+vi+304|0,J=(0|D)==(0|Se[$>>2]);do{if(J){if(Se[$>>2]=B,0!=(0|B))break;var rr=Se[vi+4>>2]&(1<>2]^-1);Se[vi+4>>2]=rr;break a}if(L>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";var ar=L+16|0;if((0|Se[ar>>2])==(0|D)?Se[ar>>2]=B:Se[L+20>>2]=B,0==(0|B))break a}while(0);if(B>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[i+6]=L;var er=16|b,ir=Me[(er>>2)+_];if(0!=(0|ir)){if(ir>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[i+4]=ir,Se[ir+24>>2]=B}var vr=Me[(er+4>>2)+_];if(0==(0|vr))break;if(vr>>>0>2]>>>0)throw Ka(),"Reached an unreachable!";Se[i+5]=vr,Se[vr+24>>2]=B}while(0);var tr=a+(M|b)|0,fr=M+w|0}else var tr=u,fr=w;var fr,tr,_r=tr+4|0,sr=Se[_r>>2]&-2;if(Se[_r>>2]=sr,Se[v+(s+1)]=1|fr,Se[(fr>>2)+s+v]=fr,fr>>>0<256){var nr=fr>>>2&1073741822,or=(nr<<2)+vi+40|0,lr=Me[vi>>2],br=1<<(fr>>>3),kr=0==(lr&br|0);do{if(!kr){var ur=(nr+2<<2)+vi+40|0,cr=Me[ur>>2];if(cr>>>0>=Me[vi+16>>2]>>>0){var hr=cr,dr=ur;break}throw Ka(),"Reached an unreachable!"}Se[vi>>2]=lr|br;var hr=or,dr=(nr+2<<2)+vi+40|0}while(0);var dr,hr;Se[dr>>2]=d,Se[hr+12>>2]=d,Se[v+(s+2)]=hr,Se[v+(s+3)]=or}else{var wr=h,pr=fr>>>8,Er=0==(0|pr);do if(Er)var Ar=0;else{if(fr>>>0>16777215){var Ar=31;break}var gr=(pr+1048320|0)>>>16&8,yr=pr<>>16&4,Sr=yr<>>16&2,Cr=14-(mr|gr|Mr)+(Sr<>>15)|0,Ar=fr>>>((Cr+7|0)>>>0)&1|Cr<<1}while(0);var Ar,Rr=(Ar<<2)+vi+304|0;Se[v+(s+7)]=Ar;var Tr=c+(r+16)|0;Se[v+(s+5)]=0,Se[Tr>>2]=0;var Or=Se[vi+4>>2],Nr=1<>2]=Ir,Se[Rr>>2]=wr,Se[v+(s+6)]=Rr,Se[v+(s+3)]=wr,Se[v+(s+2)]=wr}else{if(31==(0|Ar))var Pr=0;else var Pr=25-(Ar>>>1)|0;for(var Pr,Dr=fr<>2];;){var Lr,Dr;if((Se[Lr+4>>2]&-8|0)==(0|fr)){var Fr=Lr+8|0,Xr=Me[Fr>>2],jr=Me[vi+16>>2],Ur=Lr>>>0>>0;do if(!Ur){if(Xr>>>0>>0)break;Se[Xr+12>>2]=wr,Se[Fr>>2]=wr,Se[v+(s+2)]=Xr,Se[v+(s+3)]=Lr,Se[v+(s+6)]=0;break r}while(0);throw Ka(),"Reached an unreachable!"}var xr=(Dr>>>31<<2)+Lr+16|0,zr=Me[xr>>2];if(0==(0|zr)){if(xr>>>0>=Me[vi+16>>2]>>>0){Se[xr>>2]=wr,Se[v+(s+6)]=Lr,Se[v+(s+3)]=wr,Se[v+(s+2)]=wr;break r}throw Ka(),"Reached an unreachable!"}var Dr=Dr<<1,Lr=zr}}}}while(0);return r+(8|o)|0}function wa(r){return 0|He.__str3342}function pa(r){return 0|He.__str14343}function Ea(r){Se[r>>2]=si+8|0}function Aa(r){0!=(0|r)&&va(r)}function ga(r){ya(r);var a=r;Aa(a)}function ya(r){var a=0|r;Ye(a)}function ma(r){var a=0|r;Ea(a),Se[r>>2]=ni+8|0}function Sa(r){var a=0|r;ya(a);var e=r;Aa(e)}function Ma(r,a){var e,i,v=Me[vi+24>>2],i=v>>2,t=v,f=ua(t),_=Se[f>>2],s=Se[f+4>>2],n=_+s|0,o=_+(s-39)|0;if(0==(7&o|0))var l=0;else var l=7&-o;var l,b=_+(s-47)+l|0,k=b>>>0<(v+16|0)>>>0?t:b,u=k+8|0,e=u>>2,c=u,h=r,d=a-40|0;ca(h,d);var w=k+4|0;Se[w>>2]=27,Se[e]=Se[vi+444>>2],Se[e+1]=Se[vi+448>>2],Se[e+2]=Se[vi+452>>2],Se[e+3]=Se[vi+456>>2],Se[vi+444>>2]=r,Se[vi+448>>2]=a,Se[vi+456>>2]=0,Se[vi+452>>2]=c;var p=k+28|0;Se[p>>2]=7;var E=(k+32|0)>>>0>>0;r:do if(E)for(var A=p;;){var A,g=A+4|0;if(Se[g>>2]=7,(A+8|0)>>>0>=n>>>0)break r;var A=g}while(0);var y=(0|k)==(0|t);r:do if(!y){var m=k-v|0,S=t+m|0,M=m+(t+4)|0,C=Se[M>>2]&-2;Se[M>>2]=C;var R=1|m;Se[i+1]=R;var T=S;if(Se[T>>2]=m,m>>>0<256){var O=m>>>2&1073741822,N=(O<<2)+vi+40|0,I=Me[vi>>2],P=1<<(m>>>3),D=0==(I&P|0);do{if(!D){var L=(O+2<<2)+vi+40|0,F=Me[L>>2];if(F>>>0>=Me[vi+16>>2]>>>0){var X=F,j=L;break}throw Ka(),"Reached an unreachable!"}var U=I|P;Se[vi>>2]=U;var X=N,j=(O+2<<2)+vi+40|0}while(0);var j,X;Se[j>>2]=v,Se[X+12>>2]=v,Se[i+2]=X,Se[i+3]=N}else{var x=v,z=m>>>8,V=0==(0|z);do if(V)var B=0;else{if(m>>>0>16777215){var B=31;break}var H=(z+1048320|0)>>>16&8,K=z<>>16&4,G=K<>>16&2,Z=14-(Y|H|W)+(G<>>15)|0,B=m>>>((Z+7|0)>>>0)&1|Z<<1}while(0);var B,Q=(B<<2)+vi+304|0;Se[i+7]=B,Se[i+5]=0,Se[i+4]=0;var q=Se[vi+4>>2],$=1<>2]=J,Se[Q>>2]=x,Se[i+6]=Q,Se[i+3]=v,Se[i+2]=v}else{if(31==(0|B))var rr=0;else var rr=25-(B>>>1)|0;for(var rr,ar=m<>2];;){var er,ar;if((Se[er+4>>2]&-8|0)==(0|m)){var ir=er+8|0,vr=Me[ir>>2],tr=Me[vi+16>>2],fr=er>>>0>>0;do if(!fr){if(vr>>>0>>0)break;Se[vr+12>>2]=x,Se[ir>>2]=x,Se[i+2]=vr,Se[i+3]=er,Se[i+6]=0;break r}while(0);throw Ka(),"Reached an unreachable!"}var _r=(ar>>>31<<2)+er+16|0,sr=Me[_r>>2];if(0==(0|sr)){if(_r>>>0>=Me[vi+16>>2]>>>0){Se[_r>>2]=x,Se[i+6]=er,Se[i+3]=v,Se[i+2]=v;break r}throw Ka(),"Reached an unreachable!"}var ar=ar<<1,er=sr}}}}while(0)}function Ca(r){return d(r)}function Ra(r,a){var e=0;do Ae[r+e]=Ae[a+e],e++;while(0!=Ae[a+e-1]);return r}function Ta(){var r=Ta;return r.LLVM_SAVEDSTACKS||(r.LLVM_SAVEDSTACKS=[]),r.LLVM_SAVEDSTACKS.push(le.stackSave()),r.LLVM_SAVEDSTACKS.length-1}function Oa(r){var a=Ta,e=a.LLVM_SAVEDSTACKS[r];a.LLVM_SAVEDSTACKS.splice(r,1),le.stackRestore(e)}function Na(r,a,e){for(var i=0;it?1:-1;i++}return 0}function Ia(r,a){var e=Ca(r),i=0;do Ae[r+e+i]=Ae[a+i],i++;while(0!=Ae[a+i-1]);return r}function Pa(r,a,e,i){if(e>=20&&a%2==r%2)if(a%4==r%4){for(var v=a+e;a%4;)Ae[r++]=Ae[a++];for(var t=a>>2,f=r>>2,_=v>>2;t<_;)Se[f++]=Se[t++];for(a=t<<2,r=f<<2;a>1,n=r>>1,o=v>>1;st?1:-1}return 0}function Fa(r,a,e,i){if(e>=20){for(var v=r+e;r%4;)Ae[r++]=a;a<0&&(a+=256);for(var t=r>>2,f=v>>2,_=a|a<<8|a<<16|a<<24;t>2],xe[1]=Se[a+_+4>>2],e=ze[0]):"i64"==r?e=[Se[a+_>>2],Se[a+_+4>>2]]:(r="i32",e=Se[a+_>>2]),_+=le.getNativeFieldSize(r),e}for(var i,v,t,f=r,_=0,s=[];;){var n=f;if(i=Ae[f],0===i)break;if(v=Ae[f+1],i=="%".charCodeAt(0)){var o=!1,l=!1,b=!1,k=!1;r:for(;;){switch(v){case"+".charCodeAt(0):o=!0;break;case"-".charCodeAt(0):l=!0;break;case"#".charCodeAt(0):b=!0;break;case"0".charCodeAt(0):if(k)break r;k=!0;break;default:break r}f++,v=Ae[f+1]}var u=0;if(v=="*".charCodeAt(0))u=e("i32"),f++,v=Ae[f+1];else for(;v>="0".charCodeAt(0)&&v<="9".charCodeAt(0);)u=10*u+(v-"0".charCodeAt(0)),f++,v=Ae[f+1];var c=!1;if(v==".".charCodeAt(0)){var h=0;if(c=!0,f++,v=Ae[f+1],v=="*".charCodeAt(0))h=e("i32"),f++;else for(;;){var d=Ae[f+1];if(d<"0".charCodeAt(0)||d>"9".charCodeAt(0))break;h=10*h+(d-"0".charCodeAt(0)),f++}v=Ae[f+1]}else var h=6;var E;switch(String.fromCharCode(v)){case"h":var A=Ae[f+2];A=="h".charCodeAt(0)?(f++,E=1):E=2;break;case"l":var A=Ae[f+2];A=="l".charCodeAt(0)?(f++,E=8):E=4;break;case"L":case"q":case"j":E=8;break;case"z":case"t":case"I":E=4;break;default:E=null}if(E&&f++,v=Ae[f+1],["d","i","u","o","x","X","p"].indexOf(String.fromCharCode(v))!=-1){var m=v=="d".charCodeAt(0)||v=="i".charCodeAt(0);E=E||4;var t=e("i"+8*E);if(8==E&&(t=le.makeBigInt(t[0],t[1],v=="u".charCodeAt(0))),E<=4){var S=Math.pow(256,E)-1;t=(m?y:g)(t&S,8*E)}var M,C=Math.abs(t),R="";if(v=="d".charCodeAt(0)||v=="i".charCodeAt(0))M=y(t,8*E,1).toString(10);else if(v=="u".charCodeAt(0))M=g(t,8*E,1).toString(10),t=Math.abs(t);else if(v=="o".charCodeAt(0))M=(b?"0":"")+C.toString(8);else if(v=="x".charCodeAt(0)||v=="X".charCodeAt(0)){if(R=b?"0x":"",t<0){t=-t,M=(C-1).toString(16);for(var T=[],O=0;OP&&P>=-4?(v=(v=="g".charCodeAt(0)?"f":"F").charCodeAt(0),h-=P+1):(v=(v=="g".charCodeAt(0)?"e":"E").charCodeAt(0),h--),I=Math.min(h,20)}v=="e".charCodeAt(0)||v=="E".charCodeAt(0)?(M=t.toExponential(I),/[eE][-+]\\d$/.test(M)&&(M=M.slice(0,-1)+"0"+M.slice(-1))):v!="f".charCodeAt(0)&&v!="F".charCodeAt(0)||(M=t.toFixed(I));var D=M.split("e");if(N&&!b)for(;D[0].length>1&&D[0].indexOf(".")!=-1&&("0"==D[0].slice(-1)||"."==D[0].slice(-1));)D[0]=D[0].slice(0,-1);else for(b&&M.indexOf(".")==-1&&(D[0]+=".");h>I++;)D[0]+="0";M=D[0]+(D.length>1?"e"+D[1]:""),v=="E".charCodeAt(0)&&(M=M.toUpperCase()),o&&t>=0&&(M="+"+M)}else M=(t<0?"-":"")+"inf",k=!1;for(;M.lengthh&&(L=L.slice(0,h))):L=p("(null)",!0),!l)for(;L.length0;)s.push(" ".charCodeAt(0));l||s.push(e("i8"))}else if(v=="n".charCodeAt(0)){var X=e("i32*");Se[X>>2]=s.length}else if(v=="%".charCodeAt(0))s.push(i);else for(var O=n;O="0".charCodeAt(0)&&r<="9".charCodeAt(0)}function Ha(r){for(var a;(a=Ae[r])&&Va(a);)r++;if(!a||!Ba(a))return 0;for(var e=r;(a=Ae[e])&&Ba(a);)e++;return Math.floor(Number(s(r).substr(0,e-r)))}function Ka(r){throw ke=!0,"ABORT: "+r+", at "+(new Error).stack}function Ya(r){return Ya.ret||(Ya.ret=_([0],"i32",we)),Se[Ya.ret>>2]=r,r}function Ga(r,a,e,i){var v=$e.streams[r];if(!v||v.object.isDevice)return Ya(Ge.EBADF),-1;if(v.isWrite){if(v.object.isFolder)return Ya(Ge.EISDIR),-1;if(e<0||i<0)return Ya(Ge.EINVAL),-1;for(var t=v.object.contents;t.length>2]=a),a}function Ja(){return Ya.ret}function re(r){var a=re;a.called||(Ie=o(Ie),a.called=!0);var e=Ie;return 0!=r&&le.staticAlloc(r),e}function ae(){return Se[ae.buf>>2]}function ee(r){r=r||Module.arguments,k();var a=null;return Module._main&&(a=Module.callMain(r),Module.noExitRuntime||u()),a}var ie=[],ve="object"==typeof process,te="object"==typeof window,fe="function"==typeof importScripts,_e=!te&&!ve&&!fe;if(ve){print=function(r){process.stdout.write(r+"\\n")},printErr=function(r){process.stderr.write(r+"\\n")};var se=require("fs");read=function(r){var a=se.readFileSync(r).toString();return a||"/"==r[0]||(r=__dirname.split("/").slice(0,-1).join("/")+"/src/"+r,a=se.readFileSync(r).toString()),a},load=function(a){r(read(a))},ie=process.argv.slice(2)}else if(_e)this.read||(this.read=function(r){snarf(r)}),"undefined"!=typeof scriptArgs?ie=scriptArgs:"undefined"!=typeof arguments&&(ie=arguments);else if(te)this.print=printErr=function(r){console.log(r)},this.read=function(r){var a=new XMLHttpRequest;return a.open("GET",r,!1),a.send(null),a.responseText},this.arguments&&(ie=arguments);else{if(!fe)throw"Unknown runtime environment. Where are we?";this.load=importScripts}"undefined"==typeof load&&"undefined"!=typeof read&&(this.load=function(a){r(read(a))}),"undefined"==typeof printErr&&(this.printErr=function(){}),"undefined"==typeof print&&(this.print=printErr);try{this.Module=Module}catch(r){this.Module=Module={}}Module.arguments||(Module.arguments=ie),Module.print&&(print=Module.print);var ne,oe,le={stackSave:function(){return Oe},stackRestore:function(r){Oe=r},forceAlign:function(r,a){if(a=a||4,1==a)return r;if(isNumber(r)&&isNumber(a))return Math.ceil(r/a)*a;if(isNumber(a)&&isPowerOfTwo(a)){var e=log2(a);return"(((("+r+")+"+(a-1)+")>>"+e+")<<"+e+")"}return"Math.ceil(("+r+")/"+a+")*"+a},isNumberType:function(r){return r in le.INT_TYPES||r in le.FLOAT_TYPES},isPointerType:function(r){return"*"==r[r.length-1]},isStructType:function(r){return!isPointerType(r)&&(!!/^\\[\\d+\\ x\\ (.*)\\]/.test(r)||(!!/?/.test(r)||"%"==r[0]))},INT_TYPES:{i1:0,i8:0,i16:0,i32:0,i64:0},FLOAT_TYPES:{float:0,double:0},bitshift64:function(r,e,i,v){var t=Math.pow(2,v)-1;if(v<32)switch(i){case"shl":return[r<>>32-v];case"ashr":return[(r>>>v|(e&t)<<32-v)>>0>>>0,e>>v>>>0];case"lshr":return[(r>>>v|(e&t)<<32-v)>>>0,e>>>v]}else if(32==v)switch(i){case"shl":return[0,r];case"ashr":return[e,(0|e)<0?t:0];case"lshr":return[e,0]}else switch(i){case"shl":return[0,r<>v-32>>>0,(0|e)<0?t:0];case"lshr":return[e>>>v-32,0]}a("unknown bitshift64 op: "+[value,i,v])},or64:function(r,a){var e=0|r|(0|a),i=4294967296*(Math.round(r/4294967296)|Math.round(a/4294967296));return e+i},and64:function(r,a){var e=(0|r)&(0|a),i=4294967296*(Math.round(r/4294967296)&Math.round(a/4294967296));return e+i},xor64:function(r,a){var e=(0|r)^(0|a),i=4294967296*(Math.round(r/4294967296)^Math.round(a/4294967296));return e+i},getNativeTypeSize:function(r,a){if(1==le.QUANTUM_SIZE)return 1;var i={"%i1":1,"%i8":1,"%i16":2,"%i32":4,"%i64":8,"%float":4,"%double":8}["%"+r];if(!i)if("*"==r[r.length-1])i=le.QUANTUM_SIZE;else if("i"==r[0]){var v=parseInt(r.substr(1));e(v%8==0),i=v/8}return i},getNativeFieldSize:function(r){return Math.max(le.getNativeTypeSize(r),le.QUANTUM_SIZE)},dedup:function(r,a){var e={};return a?r.filter(function(r){return!e[r[a]]&&(e[r[a]]=!0,!0)}):r.filter(function(r){return!e[r]&&(e[r]=!0,!0)})},set:function(){for(var r="object"==typeof arguments[0]?arguments[0]:arguments,a={},e=0;e=0&&a.push(f-e),e=f,f}),r.flatSize=le.alignMemory(r.flatSize,r.alignSize),0==a.length?r.flatFactor=r.flatSize:1==le.dedup(a).length&&(r.flatFactor=a[0]),r.needsFlattening=1!=r.flatFactor,r.flatIndexes},generateStructInfo:function(r,a,i){var v,t;if(a){if(i=i||0,v=("undefined"==typeof Types?le.typeInfo:Types.types)[a],!v)return null;e(v.fields.length===r.length,"Number of named fields must match the type for "+a),t=v.flatIndexes}else{var v={fields:r.map(function(r){return r[0]})};t=le.calculateStructAlignment(v)}var f={__size__:v.flatSize};return a?r.forEach(function(r,a){if("string"==typeof r)f[r]=t[a]+i;else{var e;for(var _ in r)e=_;f[e]=le.generateStructInfo(r[e],v.fields[a],t[a])}}):r.forEach(function(r,a){f[r[1]]=t[a]}),f},stackAlloc:function(r){var a=Oe;return Oe+=r,Oe=Oe+3>>2<<2,a},staticAlloc:function(r){var a=Ie;return Ie+=r,Ie=Ie+3>>2<<2,Ie>=Le&&l(),a},alignMemory:function(r,a){var e=r=Math.ceil(r/(a?a:4))*(a?a:4);return e},makeBigInt:function(r,a,e){var i=e?(r>>>0)+4294967296*(a>>>0):(r>>>0)+4294967296*(0|a);return i},QUANTUM_SIZE:4,__dummy__:0},be={MAX_ALLOWED:0,corrections:0,sigs:{},note:function(r,e,i){e||(this.corrections++,this.corrections>=this.MAX_ALLOWED&&a("\\n\\nToo many corrections!"))},print:function(){}},ke=!1,ue=0,ce=this;Module.ccall=i,Module.setValue=t,Module.getValue=f;var he=0,de=1,we=2;Module.ALLOC_NORMAL=he,Module.ALLOC_STACK=de,Module.ALLOC_STATIC=we,Module.allocate=_,Module.Pointer_stringify=s,Module.Array_stringify=n;var pe,Ee,Ae,ge,ye,me,Se,Me,Ce,Re,Te,Oe,Ne,Ie,Pe=4096,De=Module.TOTAL_STACK||5242880,Le=Module.TOTAL_MEMORY||10485760;Module.FAST_MEMORY||2097152;e(!!(Int32Array&&Float64Array&&new Int32Array(1).subarray&&new Int32Array(1).set),"Cannot fallback to non-typed array case: Code is too specialized");var Fe=new ArrayBuffer(Le);Ae=new Int8Array(Fe),ye=new Int16Array(Fe),Se=new Int32Array(Fe),ge=new Uint8Array(Fe),me=new Uint16Array(Fe),Me=new Uint32Array(Fe),Ce=new Float32Array(Fe),Re=new Float64Array(Fe),Se[0]=255,e(255===ge[0]&&0===ge[3],"Typed arrays 2 must be run on a little-endian system");var Xe=p("(null)");Ie=Xe.length;for(var je=0;je>2)),ze=(Ce.subarray(Ue>>2),Re.subarray(Ue>>3));Ne=Ue+8,Ie=o(Ne);var Ve=[],Be=[];Module.Array_copy=c,Module.TypedArray_copy=h,Module.String_len=d,Module.String_copy=w,Module.intArrayFromString=p,Module.intArrayToString=E,Module.writeStringToMemory=A;var He=[],Ke=0;O.X=1,N.X=1,V.X=1,H.X=1,G.X=1,W.X=1,q.X=1,$.X=1,rr.X=1,ar.X=1,er.X=1,vr.X=1,nr.X=1,or.X=1,kr.X=1,hr.X=1,Ar.X=1,Sr.X=1,Tr.X=1,Ir.X=1,Pr.X=1,Dr.X=1,Lr.X=1,Fr.X=1,Xr.X=1,zr.X=1,Vr.X=1,Br.X=1,Gr.X=1,$r.X=1,Module._malloc=Jr,Jr.X=1,ra.X=1,aa.X=1,ea.X=1,ia.X=1,Module._free=va,va.X=1,_a.X=1,sa.X=1,na.X=1,oa.X=1,la.X=1,da.X=1,Ma.X=1;var Ye,Ge={E2BIG:7,EACCES:13,EADDRINUSE:98,EADDRNOTAVAIL:99,EAFNOSUPPORT:97,EAGAIN:11,EALREADY:114,EBADF:9,EBADMSG:74,EBUSY:16,ECANCELED:125,ECHILD:10,ECONNABORTED:103,ECONNREFUSED:111,ECONNRESET:104,EDEADLK:35,EDESTADDRREQ:89,EDOM:33,EDQUOT:122,EEXIST:17,EFAULT:14,EFBIG:27,EHOSTUNREACH:113,EIDRM:43,EILSEQ:84,EINPROGRESS:115,EINTR:4,EINVAL:22,EIO:5,EISCONN:106,EISDIR:21,ELOOP:40,EMFILE:24,EMLINK:31,EMSGSIZE:90,EMULTIHOP:72,ENAMETOOLONG:36,ENETDOWN:100,ENETRESET:102,ENETUNREACH:101,ENFILE:23,ENOBUFS:105,ENODATA:61,ENODEV:19,ENOENT:2,ENOEXEC:8,ENOLCK:37,ENOLINK:67,ENOMEM:12,ENOMSG:42,ENOPROTOOPT:92,ENOSPC:28,ENOSR:63,ENOSTR:60,ENOSYS:38,ENOTCONN:107,ENOTDIR:20,ENOTEMPTY:39,ENOTRECOVERABLE:131,ENOTSOCK:88,ENOTSUP:95,ENOTTY:25,ENXIO:6,EOVERFLOW:75,EOWNERDEAD:130,EPERM:1,EPIPE:32,EPROTO:71,EPROTONOSUPPORT:93,EPROTOTYPE:91,ERANGE:34,EROFS:30,ESPIPE:29,ESRCH:3,ESTALE:116,ETIME:62,ETIMEDOUT:110,ETXTBSY:26,EWOULDBLOCK:11,EXDEV:18},We=0,Ze=0,Qe=0,qe=0,$e={currentPath:"/",nextInode:2,streams:[null],ignorePermissions:!0,absolutePath:function(r,a){if("string"!=typeof r)return null;void 0===a&&(a=$e.currentPath),r&&"/"==r[0]&&(a="");for(var e=a+"/"+r,i=e.split("/").reverse(),v=[""];i.length;){var t=i.pop();""==t||"."==t||(".."==t?v.length>1&&v.pop():v.push(t))}return 1==v.length?"/":v.join("/")},analyzePath:function(r,a,e){var i={isRoot:!1,exists:!1,error:0,name:null,path:null,object:null,parentExists:!1,parentPath:null,parentObject:null};if(r=$e.absolutePath(r),"/"==r)i.isRoot=!0,i.exists=i.parentExists=!0,i.name="/",i.path=i.parentPath="/",i.object=i.parentObject=$e.root;else if(null!==r){e=e||0,r=r.slice(1).split("/");for(var v=$e.root,t=[""];r.length;){1==r.length&&v.isFolder&&(i.parentExists=!0,i.parentPath=1==t.length?"/":t.join("/"),i.parentObject=v,i.name=r[0]);var f=r.shift();if(!v.isFolder){i.error=Ge.ENOTDIR;break}if(!v.read){i.error=Ge.EACCES;break}if(!v.contents.hasOwnProperty(f)){i.error=Ge.ENOENT;break}if(v=v.contents[f],v.link&&(!a||0!=r.length)){if(e>40){i.error=Ge.ELOOP;break}var _=$e.absolutePath(v.link,t.join("/"));return $e.analyzePath([_].concat(r).join("/"),a,e+1)}t.push(f),0==r.length&&(i.exists=!0,i.path=t.join("/"),i.object=v)}return i}return i},findObject:function(r,a){$e.ensureRoot();var e=$e.analyzePath(r,a);return e.exists?e.object:(Ya(e.error),null)},createObject:function(r,a,e,i,v){if(r||(r="/"),"string"==typeof r&&(r=$e.findObject(r)),!r)throw Ya(Ge.EACCES),new Error("Parent path must exist.");if(!r.isFolder)throw Ya(Ge.ENOTDIR), new Error("Parent must be a folder.");if(!r.write&&!$e.ignorePermissions)throw Ya(Ge.EACCES),new Error("Parent folder must be writeable.");if(!a||"."==a||".."==a)throw Ya(Ge.ENOENT),new Error("Name must not be empty.");if(r.contents.hasOwnProperty(a))throw Ya(Ge.EEXIST),new Error("Can't overwrite object.");r.contents[a]={read:void 0===i||i,write:void 0!==v&&v,timestamp:Date.now(),inodeNumber:$e.nextInode++};for(var t in e)e.hasOwnProperty(t)&&(r.contents[a][t]=e[t]);return r.contents[a]},createFolder:function(r,a,e,i){var v={isFolder:!0,isDevice:!1,contents:{}};return $e.createObject(r,a,v,e,i)},createPath:function(r,a,e,i){var v=$e.findObject(r);if(null===v)throw new Error("Invalid parent.");for(a=a.split("/").reverse();a.length;){var t=a.pop();t&&(v.contents.hasOwnProperty(t)||$e.createFolder(v,t,e,i),v=v.contents[t])}return v},createFile:function(r,a,e,i,v){return e.isFolder=!1,$e.createObject(r,a,e,i,v)},createDataFile:function(r,a,e,i,v){if("string"==typeof e){for(var t=new Array(e.length),f=0,_=e.length;f<_;++f)t[f]=e.charCodeAt(f);e=t}var s={isDevice:!1,contents:e};return $e.createFile(r,a,s,i,v)},createLazyFile:function(r,a,e,i,v){var t={isDevice:!1,url:e};return $e.createFile(r,a,t,i,v)},createLink:function(r,a,e,i,v){var t={isDevice:!1,link:e};return $e.createFile(r,a,t,i,v)},createDevice:function(r,a,e,i){if(!e&&!i)throw new Error("A device must have at least one callback defined.");var v={isDevice:!0,input:e,output:i};return $e.createFile(r,a,v,Boolean(e),Boolean(i))},forceLoadFile:function(r){if(r.isDevice||r.isFolder||r.link||r.contents)return!0;var a=!0;if("undefined"!=typeof XMLHttpRequest)e("Cannot do synchronous binary XHRs in modern browsers. Use --embed-file or --preload-file in emcc");else{if("undefined"==typeof read)throw new Error("Cannot load without read() or XMLHttpRequest.");try{r.contents=p(read(r.url),!0)}catch(r){a=!1}}return a||Ya(Ge.EIO),a},ensureRoot:function(){$e.root||($e.root={read:!0,write:!0,isFolder:!0,isDevice:!1,timestamp:Date.now(),inodeNumber:1,contents:{}})},init:function(r,a,i){function v(r){null===r||r==="\\n".charCodeAt(0)?(a.printer(a.buffer.join("")),a.buffer=[]):a.buffer.push(String.fromCharCode(r))}e(!$e.init.initialized,"FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)"),$e.init.initialized=!0,$e.ensureRoot(),r=r||Module.stdin,a=a||Module.stdout,i=i||Module.stderr;var t=!0,f=!0,s=!0;r||(t=!1,r=function(){if(!r.cache||!r.cache.length){var a;"undefined"!=typeof window&&"function"==typeof window.prompt?a=window.prompt("Input: "):"function"==typeof readline&&(a=readline()),a||(a=""),r.cache=p(a+"\\n",!0)}return r.cache.shift()}),a||(f=!1,a=v),a.printer||(a.printer=print),a.buffer||(a.buffer=[]),i||(s=!1,i=v),i.printer||(i.printer=print),i.buffer||(i.buffer=[]),$e.createFolder("/","tmp",!0,!0);var n=$e.createFolder("/","dev",!0,!0),o=$e.createDevice(n,"stdin",r),l=$e.createDevice(n,"stdout",null,a),b=$e.createDevice(n,"stderr",null,i);$e.createDevice(n,"tty",r,a),$e.streams[1]={path:"/dev/stdin",object:o,position:0,isRead:!0,isWrite:!1,isAppend:!1,isTerminal:!t,error:!1,eof:!1,ungotten:[]},$e.streams[2]={path:"/dev/stdout",object:l,position:0,isRead:!1,isWrite:!0,isAppend:!1,isTerminal:!f,error:!1,eof:!1,ungotten:[]},$e.streams[3]={path:"/dev/stderr",object:b,position:0,isRead:!1,isWrite:!0,isAppend:!1,isTerminal:!s,error:!1,eof:!1,ungotten:[]},We=_([1],"void*",we),Ze=_([2],"void*",we),Qe=_([3],"void*",we),$e.createPath("/","dev/shm/tmp",!0,!0),$e.streams[We]=$e.streams[1],$e.streams[Ze]=$e.streams[2],$e.streams[Qe]=$e.streams[3],qe=_([_([0,0,0,0,We,0,0,0,Ze,0,0,0,Qe,0,0,0],"void*",we)],"void*",we)},quit:function(){$e.init.initialized&&($e.streams[2]&&$e.streams[2].object.output.buffer.length>0&&$e.streams[2].object.output("\\n".charCodeAt(0)),$e.streams[3]&&$e.streams[3].object.output.buffer.length>0&&$e.streams[3].object.output("\\n".charCodeAt(0)))}},Je=Ja;Ve.unshift({func:function(){$e.ignorePermissions=!1,$e.init.initialized||$e.init()}}),Be.push({func:function(){$e.quit()}}),Ya(0),ae.buf=_(12,"void*",we),Module.callMain=function(r){function a(){for(var r=0;r<3;r++)i.push(0)}var e=r.length+1,i=[_(p("/bin/this.program"),"i8",we)];a();for(var v=0;v>2]=0|He.__str,Se[ri+4>>2]=0|He.__str1,Se[ri+16>>2]=0|He.__str2,Se[ri+20>>2]=0|He.__str3,Se[ri+32>>2]=0|He.__str4,Se[ri+36>>2]=0|He.__str5,Se[ri+48>>2]=0|He.__str6,Se[ri+52>>2]=0|He.__str7,Se[ri+64>>2]=0|He.__str8,Se[ri+68>>2]=0|He.__str7,Se[ri+80>>2]=0|He.__str9,Se[ri+84>>2]=0|He.__str10,Se[ri+96>>2]=0|He.__str11,Se[ri+100>>2]=0|He.__str12,Se[ri+112>>2]=0|He.__str13,Se[ri+116>>2]=0|He.__str14,Se[ri+128>>2]=0|He.__str15,Se[ri+132>>2]=0|He.__str16,Se[ri+144>>2]=0|He.__str17,Se[ri+148>>2]=0|He.__str18,Se[ri+160>>2]=0|He.__str19,Se[ri+164>>2]=0|He.__str20,Se[ri+176>>2]=0|He.__str21,Se[ri+180>>2]=0|He.__str22,Se[ri+192>>2]=0|He.__str23,Se[ri+196>>2]=0|He.__str24,Se[ri+208>>2]=0|He.__str25,Se[ri+212>>2]=0|He.__str26,Se[ri+224>>2]=0|He.__str27,Se[ri+228>>2]=0|He.__str28,Se[ri+240>>2]=0|He.__str29,Se[ri+244>>2]=0|He.__str30,Se[ri+256>>2]=0|He.__str31,Se[ri+260>>2]=0|He.__str32,Se[ri+272>>2]=0|He.__str33,Se[ri+276>>2]=0|He.__str34,Se[ri+288>>2]=0|He.__str35,Se[ri+292>>2]=0|He.__str36,Se[ri+304>>2]=0|He.__str37,Se[ri+308>>2]=0|He.__str38,Se[ri+320>>2]=0|He.__str39,Se[ri+324>>2]=0|He.__str40,Se[ri+336>>2]=0|He.__str41,Se[ri+340>>2]=0|He.__str42,Se[ri+352>>2]=0|He.__str43,Se[ri+356>>2]=0|He.__str44,Se[ri+368>>2]=0|He.__str45,Se[ri+372>>2]=0|He.__str46,Se[ri+384>>2]=0|He.__str47,Se[ri+388>>2]=0|He.__str48,Se[ri+400>>2]=0|He.__str49,Se[ri+404>>2]=0|He.__str119289,Se[ri+416>>2]=0|He.__str51,Se[ri+420>>2]=0|He.__str20,Se[ri+432>>2]=0|He.__str52,Se[ri+436>>2]=0|He.__str53,Se[ri+448>>2]=0|He.__str54,Se[ri+452>>2]=0|He.__str55,Se[ri+464>>2]=0|He.__str56,Se[ri+468>>2]=0|He.__str57,Se[ri+480>>2]=0|He.__str58,Se[ri+484>>2]=0|He.__str119289,Se[ri+496>>2]=0|He.__str59,Se[ri+500>>2]=0|He.__str60,Se[ri+512>>2]=0|He.__str61,Se[ri+516>>2]=0|He.__str62,Se[ri+528>>2]=0|He.__str63,Se[ri+532>>2]=0|He.__str64,Se[ri+544>>2]=0|He.__str65,Se[ri+548>>2]=0|He.__str66,Se[ri+560>>2]=0|He.__str67,Se[ri+564>>2]=0|He.__str68,Se[ri+576>>2]=0|He.__str69,Se[ri+580>>2]=0|He.__str70,Se[ri+592>>2]=0|He.__str71,Se[ri+596>>2]=0|He.__str72,Se[ri+608>>2]=0|He.__str73,Se[ri+612>>2]=0|He.__str74,Se[ri+624>>2]=0|He.__str75,Se[ri+628>>2]=0|He.__str76,Se[ri+640>>2]=0|He.__str77,Se[ri+644>>2]=0|He.__str72,Se[ri+656>>2]=0|He.__str78,Se[ri+660>>2]=0|He.__str79,Se[ri+672>>2]=0|He.__str80,Se[ri+676>>2]=0|He.__str81,Se[ri+688>>2]=0|He.__str82,Se[ri+692>>2]=0|He.__str83,Se[ri+704>>2]=0|He.__str84,Se[ri+708>>2]=0|He.__str85,Se[ri+720>>2]=0|He.__str86,Se[ri+724>>2]=0|He.__str87,Se[ri+736>>2]=0|He.__str88,Se[ri+740>>2]=0|He.__str89,Se[ri+752>>2]=0|He.__str90,Se[ri+756>>2]=0|He.__str91,Se[ri+768>>2]=0|He.__str92,Se[ri+772>>2]=0|He.__str91,Se[ai>>2]=0|He.__str145315,Se[ai+8>>2]=0|He.__str145315,Se[ai+20>>2]=0|He.__str167337,Se[ai+28>>2]=0|He.__str95,Se[ai+40>>2]=0|He.__str146316,Se[ai+48>>2]=0|He.__str97,Se[ai+60>>2]=0|He.__str155325,Se[ai+68>>2]=0|He.__str155325,Se[ai+80>>2]=0|He.__str156326,Se[ai+88>>2]=0|He.__str156326,Se[ai+100>>2]=0|He.__str154324,Se[ai+108>>2]=0|He.__str154324,Se[ai+120>>2]=0|He.__str101,Se[ai+128>>2]=0|He.__str101,Se[ai+140>>2]=0|He.__str147317,Se[ai+148>>2]=0|He.__str147317,Se[ai+160>>2]=0|He.__str150320,Se[ai+168>>2]=0|He.__str150320,Se[ai+180>>2]=0|He.__str151321,Se[ai+188>>2]=0|He.__str105,Se[ai+220>>2]=0|He.__str152322,Se[ai+228>>2]=0|He.__str152322,Se[ai+240>>2]=0|He.__str153323,Se[ai+248>>2]=0|He.__str153323,Se[ai+260>>2]=0|He.__str165335,Se[ai+268>>2]=0|He.__str165335,Se[ai+280>>2]=0|He.__str166336,Se[ai+288>>2]=0|He.__str166336,Se[ai+360>>2]=0|He.__str148318,Se[ai+368>>2]=0|He.__str148318,Se[ai+380>>2]=0|He.__str149319,Se[ai+388>>2]=0|He.__str149319,Se[ai+420>>2]=0|He.__str84254,Se[ai+428>>2]=0|He.__str84254,Se[ai+440>>2]=0|He.__str168338,Se[ai+448>>2]=0|He.__str146316,Se[ai+460>>2]=0|He.__str114,Se[ai+468>>2]=0|He.__str152322,Se[ai+480>>2]=0|He.__str115,Se[ai+488>>2]=0|He.__str115,Se[ai+500>>2]=0|He.__str110280,Se[ai+508>>2]=0|He.__str110280,Se[ei+4>>2]=0|He.__str152,Se[ei+12>>2]=0|He.__str152,Se[ei+32>>2]=0|He.__str153,Se[ei+40>>2]=0|He.__str153,Se[ei+48>>2]=0|He.__str154,Se[ei+60>>2]=0|He.__str155,Se[ei+68>>2]=0|He.__str155,Se[ei+76>>2]=0|He.__str156,Se[ei+88>>2]=0|He.__str157,Se[ei+96>>2]=0|He.__str158,Se[ei+104>>2]=0|He.__str156,Se[ei+116>>2]=0|He.__str159,Se[ei+124>>2]=0|He.__str160,Se[ei+132>>2]=0|He.__str161,Se[ei+144>>2]=0|He.__str162,Se[ei+152>>2]=0|He.__str163,Se[ei+160>>2]=0|He.__str164,Se[ei+172>>2]=0|He.__str165,Se[ei+180>>2]=0|He.__str166,Se[ei+188>>2]=0|He.__str167,Se[si+4>>2]=bi,Se[ni+4>>2]=ki,oi=_([2,0,0,0,0],["i8*",0,0,0,0],we),Se[bi>>2]=oi+8|0,Se[bi+4>>2]=0|He.__ZTSSt9bad_alloc,Se[bi+8>>2]=li,Se[ki>>2]=oi+8|0,Se[ki+4>>2]=0|He.__ZTSSt20bad_array_new_length,Se[ki+8>>2]=bi,ui=16,ci=6,hi=18,di=6,wi=6,pe=[0,0,Jr,0,va,0,ya,0,ga,0,wa,0,Sa,0,pa,0,Ea,0,ma,0],Module.FUNCTION_TABLE=pe,Module.run=ee,Module.preRun&&Module.preRun(),0==Ke){ee()}Module.postRun&&Module.postRun(),Module.___cxa_demangle=G;var pi=v("__cxa_demangle","string",["string","string","number","number"]);return function(r){return pi(r,"",1,0)}}(); -` \ No newline at end of file +` diff --git a/flamechart-color-pass-renderer.ts b/flamechart-color-pass-renderer.ts index 323fd90..992c0c6 100644 --- a/flamechart-color-pass-renderer.ts +++ b/flamechart-color-pass-renderer.ts @@ -102,7 +102,7 @@ export class FlamechartColorPassRenderer { `, depth: { - enable: false + enable: false, }, attributes: { @@ -114,18 +114,8 @@ export class FlamechartColorPassRenderer { // | /| // |/ | // 2 +--+ 3 - position: gl.buffer([ - [-1, 1], - [1, 1], - [-1, -1], - [1, -1] - ]), - uv: gl.buffer([ - [0, 1], - [1, 1], - [0, 0], - [1, 0] - ]) + position: gl.buffer([[-1, 1], [1, 1], [-1, -1], [1, -1]]), + uv: gl.buffer([[0, 1], [1, 1], [0, 0], [1, 0]]), }, count: 4, @@ -138,34 +128,33 @@ export class FlamechartColorPassRenderer { const { srcRect, rectInfoTexture } = props const physicalToUV = AffineTransform.withTranslation(new Vec2(0, 1)) .times(AffineTransform.withScale(new Vec2(1, -1))) - .times(AffineTransform.betweenRects( + .times( + AffineTransform.betweenRects( new Rect(Vec2.zero, new Vec2(rectInfoTexture.width, rectInfoTexture.height)), - Rect.unit - )) + Rect.unit, + ), + ) const uvRect = physicalToUV.transformRect(srcRect) - return AffineTransform.betweenRects( - Rect.unit, - uvRect, - ).flatten() + return AffineTransform.betweenRects(Rect.unit, uvRect).flatten() }, renderOutlines: (context, props) => { return props.renderOutlines ? 1.0 : 0.0 }, uvSpacePixelSize: (context, props) => { - return Vec2.unit.dividedByPointwise(new Vec2(props.rectInfoTexture.width, props.rectInfoTexture.height)).flatten() + return Vec2.unit + .dividedByPointwise(new Vec2(props.rectInfoTexture.width, props.rectInfoTexture.height)) + .flatten() }, positionTransform: (context, props) => { const { dstRect } = props const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight) - const physicalToNDC = AffineTransform.withScale(new Vec2(1, -1)) - .times(AffineTransform.betweenRects( - new Rect(Vec2.zero, viewportSize), - Rect.NDC) - ) + const physicalToNDC = AffineTransform.withScale(new Vec2(1, -1)).times( + AffineTransform.betweenRects(new Rect(Vec2.zero, viewportSize), Rect.NDC), + ) const ndcRect = physicalToNDC.transformRect(dstRect) return AffineTransform.betweenRects(Rect.NDC, ndcRect).flatten() - } + }, }, }) } diff --git a/flamechart-minimap-view.tsx b/flamechart-minimap-view.tsx index bddeb4a..3f8df07 100644 --- a/flamechart-minimap-view.tsx +++ b/flamechart-minimap-view.tsx @@ -2,10 +2,10 @@ import { h, Component } from 'preact' import { css } from 'aphrodite' import { Flamechart } from './flamechart' import { Rect, Vec2, AffineTransform, clamp } from './math' -import { FlamechartRenderer } from "./flamechart-renderer" -import { cachedMeasureTextWidth } from "./utils"; -import { style, Sizes } from "./flamechart-style"; -import { FontFamily, FontSize, Colors } from "./style" +import { FlamechartRenderer } from './flamechart-renderer' +import { cachedMeasureTextWidth } from './utils' +import { style, Sizes } from './flamechart-style' +import { FontFamily, FontSize, Colors } from './style' import { CanvasContext } from './canvas-context' import { TextureCachedRenderer } from './texture-catched-renderer' @@ -24,7 +24,7 @@ interface FlamechartMinimapViewProps { enum DraggingMode { DRAW_NEW_VIEWPORT, - TRANSLATE_VIEWPORT + TRANSLATE_VIEWPORT, } export class FlamechartMinimapView extends Component { @@ -39,7 +39,7 @@ export class FlamechartMinimapView extends Component { + render: props => { this.props.flamechartRenderer.render({ physicalSpaceDstRect: new Rect( this.minimapOrigin(), - this.physicalViewSize().minus(this.minimapOrigin()) + this.physicalViewSize().minus(this.minimapOrigin()), ), configSpaceSrcRect: new Rect(new Vec2(0, 0), this.configSpaceSize()), - renderOutlines: false + renderOutlines: false, }) - } + }, }) } - this.props.canvasContext.renderInto(this.container, (context) => { + this.props.canvasContext.renderInto(this.container, context => { // TODO(jlfwong): Switch back to the texture cached renderer once I figure out // how to resize a framebuffer while another framebuffer is active. It seems // to crash regl. I should submit a reduced repro case and hopefully get it fixed? @@ -116,9 +116,9 @@ export class FlamechartMinimapView extends Component { if (this.interactionLock) { if (!this.frameHadWheelEvent) { - this.framesWithoutWheelEvents++; + this.framesWithoutWheelEvents++ if (this.framesWithoutWheelEvents >= 2) { this.interactionLock = null this.framesWithoutWheelEvents = 0 @@ -275,11 +281,10 @@ export class FlamechartMinimapView extends Component - + className={css(style.minimap, style.vbox)} + > +
) } diff --git a/flamechart-renderer.ts b/flamechart-renderer.ts index 5a88bc5..cca2c11 100644 --- a/flamechart-renderer.ts +++ b/flamechart-renderer.ts @@ -1,11 +1,11 @@ import regl from 'regl' import { Flamechart } from './flamechart' import { RectangleBatch } from './rectangle-batch-renderer' -import { CanvasContext } from './canvas-context'; +import { CanvasContext } from './canvas-context' import { Vec2, Rect, AffineTransform } from './math' import { LRUCache } from './lru-cache' import { Color } from './color' -import { getOrInsert } from './utils'; +import { getOrInsert } from './utils' const MAX_BATCH_SIZE = 10000 @@ -26,15 +26,21 @@ class RowAtlas { this.framebuffer = canvasContext.gl.framebuffer({ color: [this.texture] }) this.rowCache = new LRUCache(this.texture.height) this.renderToFramebuffer = canvasContext.gl({ - framebuffer: this.framebuffer + framebuffer: this.framebuffer, }) this.clearLineBatch = canvasContext.createRectangleBatch() this.clearLineBatch.addRect(Rect.unit, new Color(0, 0, 0, 0)) } - has(key: K) { return this.rowCache.has(key) } - getResolution() { return this.texture.width } - getCapacity() { return this.texture.height } + has(key: K) { + return this.rowCache.has(key) + } + getResolution() { + return this.texture.width + } + getCapacity() { + return this.texture.height + } private allocateLine(key: K): number { if (this.rowCache.getSize() < this.rowCache.getCapacity()) { @@ -50,10 +56,7 @@ class RowAtlas { } } - writeToAtlasIfNeeded( - keys: K[], - render: (textureDstRect: Rect, key: K) => void - ) { + writeToAtlasIfNeeded(keys: K[], render: (textureDstRect: Rect, key: K) => void) { this.renderToFramebuffer((context: regl.Context) => { for (let key of keys) { let row = this.rowCache.get(key) @@ -64,14 +67,11 @@ class RowAtlas { // Not cached -- we'll have to actually render row = this.allocateLine(key) - const textureRect = new Rect( - new Vec2(0, row), - new Vec2(this.texture.width, 1) - ) + const textureRect = new Rect(new Vec2(0, row), new Vec2(this.texture.width, 1)) this.canvasContext.drawRectangleBatch({ batch: this.clearLineBatch, configSpaceSrcRect: Rect.unit, - physicalSpaceDstRect: textureRect + physicalSpaceDstRect: textureRect, }) render(textureRect, key) } @@ -84,17 +84,14 @@ class RowAtlas { return false } - const textureRect = new Rect( - new Vec2(0, row), - new Vec2(this.texture.width, 1) - ) + const textureRect = new Rect(new Vec2(0, row), new Vec2(this.texture.width, 1)) // At this point, we have the row in cache, and we can // paint directly from it into the framebuffer. this.canvasContext.drawTexture({ texture: this.texture, srcRect: textureRect, - dstRect: dstRect + dstRect: dstRect, }) return true } @@ -113,16 +110,26 @@ class RangeTreeLeafNode implements RangeTreeNode { constructor( private batch: RectangleBatch, private bounds: Rect, - private numPrecedingRectanglesInRow: number + private numPrecedingRectanglesInRow: number, ) { batch.uploadToGPU() } - getBatch() { return this.batch } - getBounds() { return this.bounds } - getRectCount() { return this.batch.getRectCount() } - getChildren() { return this.children } - getParity() { return this.numPrecedingRectanglesInRow % 2 } + getBatch() { + return this.batch + } + getBounds() { + return this.bounds + } + getRectCount() { + return this.batch.getRectCount() + } + getChildren() { + return this.children + } + getParity() { + return this.numPrecedingRectanglesInRow % 2 + } forEachLeafNodeWithinBounds(configSpaceBounds: Rect, cb: (leaf: RangeTreeLeafNode) => void) { if (!this.bounds.hasIntersectionWith(configSpaceBounds)) return cb(this) @@ -134,7 +141,7 @@ class RangeTreeInteriorNode implements RangeTreeNode { private bounds: Rect constructor(private children: RangeTreeNode[]) { if (children.length === 0) { - throw new Error("Empty interior node") + throw new Error('Empty interior node') } let minLeft = Infinity let maxRight = -Infinity @@ -150,13 +157,19 @@ class RangeTreeInteriorNode implements RangeTreeNode { } this.bounds = new Rect( new Vec2(minLeft, minTop), - new Vec2(maxRight - minLeft, maxBottom - minTop) + new Vec2(maxRight - minLeft, maxBottom - minTop), ) } - getBounds() { return this.bounds } - getRectCount() { return this.rectCount } - getChildren() { return this.children } + getBounds() { + return this.bounds + } + getRectCount() { + return this.rectCount + } + getChildren() { + return this.children + } forEachLeafNodeWithinBounds(configSpaceBounds: Rect, cb: (leaf: RangeTreeLeafNode) => void) { if (!this.bounds.hasIntersectionWith(configSpaceBounds)) return @@ -202,17 +215,20 @@ export class FlamechartRenderer { for (let i = 0; i < layer.length; i++) { const frame = layer[i] if (batch.getRectCount() >= MAX_BATCH_SIZE) { - leafNodes.push(new RangeTreeLeafNode(batch, new Rect( - new Vec2(minLeft, stackDepth), - new Vec2(maxRight - minLeft, 1) - ), rectCount)) + leafNodes.push( + new RangeTreeLeafNode( + batch, + new Rect(new Vec2(minLeft, stackDepth), new Vec2(maxRight - minLeft, 1)), + rectCount, + ), + ) minLeft = Infinity maxRight = -Infinity batch = canvasContext.createRectangleBatch() } const configSpaceBounds = new Rect( new Vec2(frame.start, y), - new Vec2(frame.end - frame.start, 1) + new Vec2(frame.end - frame.start, 1), ) minLeft = Math.min(minLeft, configSpaceBounds.left()) maxRight = Math.max(maxRight, configSpaceBounds.right()) @@ -225,17 +241,20 @@ export class FlamechartRenderer { const color = new Color( (1 + i % 255) / 256, (1 + stackDepth % 255) / 256, - (1 + this.flamechart.getColorBucketForFrame(frame.node.frame)) / 256 + (1 + this.flamechart.getColorBucketForFrame(frame.node.frame)) / 256, ) batch.addRect(configSpaceBounds, color) rectCount++ } if (batch.getRectCount() > 0) { - leafNodes.push(new RangeTreeLeafNode(batch, new Rect( - new Vec2(minLeft, stackDepth), - new Vec2(maxRight - minLeft, 1) - ), rectCount)) + leafNodes.push( + new RangeTreeLeafNode( + batch, + new Rect(new Vec2(minLeft, stackDepth), new Vec2(maxRight - minLeft, 1)), + rectCount, + ), + ) } // TODO(jlfwong): Making this into a binary tree @@ -260,16 +279,13 @@ export class FlamechartRenderer { const width = configSpaceContentWidth / Math.pow(2, zoomLevel) - return new Rect( - new Vec2(width * index, stackDepth), - new Vec2(width, 1) - ) + return new Rect(new Vec2(width * index, stackDepth), new Vec2(width, 1)) } render(props: FlamechartRendererProps) { const { configSpaceSrcRect, physicalSpaceDstRect } = props - const atlasKeysToRender: { stackDepth: number, zoomLevel: number, index: number }[] = [] + const atlasKeysToRender: { stackDepth: number; zoomLevel: number; index: number }[] = [] // We want to render the lowest resolution we can while still guaranteeing that the // atlas line is higher resolution than its corresponding destination rectangle on @@ -295,8 +311,12 @@ export class FlamechartRenderer { const configSpaceContentWidth = this.flamechart.getTotalWeight() const numAtlasEntriesPerLayer = Math.pow(2, zoomLevel) - const left = Math.floor(numAtlasEntriesPerLayer * configSpaceSrcRect.left() / configSpaceContentWidth) - const right = Math.ceil(numAtlasEntriesPerLayer * configSpaceSrcRect.right() / configSpaceContentWidth) + const left = Math.floor( + numAtlasEntriesPerLayer * configSpaceSrcRect.left() / configSpaceContentWidth, + ) + const right = Math.ceil( + numAtlasEntriesPerLayer * configSpaceSrcRect.right() / configSpaceContentWidth, + ) for (let stackDepth = top; stackDepth < bottom; stackDepth++) { for (let index = left; index <= right; index++) { @@ -314,21 +334,24 @@ export class FlamechartRenderer { // Fill the cache this.rowAtlas.writeToAtlasIfNeeded(keysToRenderCached, (textureDstRect, key) => { const configSpaceBounds = this.configSpaceBoundsForKey(key) - this.layers[key.stackDepth].forEachLeafNodeWithinBounds(configSpaceBounds, (leaf) => { + this.layers[key.stackDepth].forEachLeafNodeWithinBounds(configSpaceBounds, leaf => { this.canvasContext.drawRectangleBatch({ batch: leaf.getBatch(), configSpaceSrcRect: configSpaceBounds, physicalSpaceDstRect: textureDstRect, parityMin: key.stackDepth % 2 == 0 ? 2 : 0, - parityOffset: leaf.getParity() + parityOffset: leaf.getParity(), }) }) }) this.framebuffer.resize(physicalSpaceDstRect.width(), physicalSpaceDstRect.height()) this.framebuffer.use(context => { - this.canvasContext.gl.clear({color: [0, 0, 0, 0]}) - const viewportRect = new Rect(Vec2.zero, new Vec2(context.viewportWidth, context.viewportHeight)) + this.canvasContext.gl.clear({ color: [0, 0, 0, 0] }) + const viewportRect = new Rect( + Vec2.zero, + new Vec2(context.viewportWidth, context.viewportHeight), + ) const configToViewport = AffineTransform.betweenRects(configSpaceSrcRect, viewportRect) @@ -342,13 +365,13 @@ export class FlamechartRenderer { for (let key of keysToRenderUncached) { const configSpaceBounds = this.configSpaceBoundsForKey(key) const physicalBounds = configToViewport.transformRect(configSpaceBounds) - this.layers[key.stackDepth].forEachLeafNodeWithinBounds(configSpaceBounds, (leaf) => { + this.layers[key.stackDepth].forEachLeafNodeWithinBounds(configSpaceBounds, leaf => { this.canvasContext.drawRectangleBatch({ batch: leaf.getBatch(), configSpaceSrcRect, physicalSpaceDstRect: physicalBounds, parityMin: key.stackDepth % 2 == 0 ? 2 : 0, - parityOffset: leaf.getParity() + parityOffset: leaf.getParity(), }) }) } @@ -356,9 +379,12 @@ export class FlamechartRenderer { this.canvasContext.drawFlamechartColorPass({ rectInfoTexture: this.rectInfoTexture, - srcRect: new Rect(Vec2.zero, new Vec2(this.rectInfoTexture.width, this.rectInfoTexture.height)), + srcRect: new Rect( + Vec2.zero, + new Vec2(this.rectInfoTexture.width, this.rectInfoTexture.height), + ), dstRect: physicalSpaceDstRect, - renderOutlines: props.renderOutlines + renderOutlines: props.renderOutlines, }) // Overlay the atlas on top of the canvas for debugging @@ -370,4 +396,4 @@ export class FlamechartRenderer { }) */ } -} \ No newline at end of file +} diff --git a/flamechart-style.ts b/flamechart-style.ts index e994622..27a3910 100644 --- a/flamechart-style.ts +++ b/flamechart-style.ts @@ -1,4 +1,4 @@ -import {StyleSheet} from 'aphrodite' +import { StyleSheet } from 'aphrodite' import { FontFamily, FontSize, Colors } from './style' const HOVERTIP_PADDING = 2 @@ -22,7 +22,7 @@ export const style = StyleSheet.create({ pointerEvents: 'none', userSelect: 'none', fontSize: FontSize.LABEL, - fontFamily: FontFamily.MONOSPACE + fontFamily: FontFamily.MONOSPACE, }, hoverTipRow: { textOverflow: 'ellipsis', @@ -33,10 +33,10 @@ export const style = StyleSheet.create({ maxWidth: Sizes.TOOLTIP_WIDTH_MAX, }, hoverCount: { - color: '#6FCF97' + color: '#6FCF97', }, clip: { - overflow: 'hidden' + overflow: 'hidden', }, vbox: { display: 'flex', @@ -48,13 +48,13 @@ export const style = StyleSheet.create({ height: '100%', position: 'absolute', left: 0, - top: 0 + top: 0, }, minimap: { height: Sizes.MINIMAP_HEIGHT, - borderBottom: `${Sizes.SEPARATOR_HEIGHT}px solid ${Colors.MEDIUM_GRAY}` + borderBottom: `${Sizes.SEPARATOR_HEIGHT}px solid ${Colors.MEDIUM_GRAY}`, }, panZoomView: { - flex: 1 + flex: 1, }, -}); \ No newline at end of file +}) diff --git a/flamechart-view.tsx b/flamechart-view.tsx index e954b64..c92b4e4 100644 --- a/flamechart-view.tsx +++ b/flamechart-view.tsx @@ -1,13 +1,13 @@ -import {h} from 'preact' -import {css} from 'aphrodite' -import {ReloadableComponent} from './reloadable' +import { h } from 'preact' +import { css } from 'aphrodite' +import { ReloadableComponent } from './reloadable' import { CallTreeNode } from './profile' import { Flamechart, FlamechartFrame } from './flamechart' import { Rect, Vec2, AffineTransform, clamp } from './math' -import { cachedMeasureTextWidth } from "./utils"; -import { FlamechartMinimapView } from "./flamechart-minimap-view" +import { cachedMeasureTextWidth } from './utils' +import { FlamechartMinimapView } from './flamechart-minimap-view' import { style, Sizes } from './flamechart-style' import { FontSize, FontFamily, Colors } from './style' @@ -19,7 +19,13 @@ interface FlamechartFrameLabel { node: CallTreeNode } -function binarySearch(lo: number, hi: number, f: (val: number) => number, target: number, targetRangeSize = 1): [number, number] { +function binarySearch( + lo: number, + hi: number, + f: (val: number) => number, + target: number, + targetRangeSize = 1, +): [number, number] { console.assert(!isNaN(targetRangeSize) && !isNaN(target)) while (true) { if (hi - lo <= targetRangeSize) return [lo, hi] @@ -41,9 +47,14 @@ function buildTrimmedText(text: string, length: number) { function trimTextMid(ctx: CanvasRenderingContext2D, text: string, maxWidth: number) { if (cachedMeasureTextWidth(ctx, text) <= maxWidth) return text - const [lo,] = binarySearch(0, text.length, (n) => { - return cachedMeasureTextWidth(ctx, buildTrimmedText(text, n)) - }, maxWidth) + const [lo] = binarySearch( + 0, + text.length, + n => { + return cachedMeasureTextWidth(ctx, buildTrimmedText(text, n)) + }, + maxWidth, + ) return buildTrimmedText(text, lo) } @@ -108,14 +119,14 @@ export class FlamechartPanZoomView extends ReloadableComponent { const width = frame.end - frame.start - const configSpaceBounds = new Rect( - new Vec2(frame.start, depth), - new Vec2(width, 1) - ) + const configSpaceBounds = new Rect(new Vec2(frame.start, depth), new Vec2(width, 1)) if (width < minConfigSpaceWidthToRender) return if (configSpaceBounds.left() > this.props.configSpaceViewportRect.right()) return @@ -210,11 +226,16 @@ export class FlamechartPanZoomView extends ReloadableComponent physicalViewSize.x) { - physicalLabelBounds = physicalLabelBounds - .withSize(physicalLabelBounds.size.withX(physicalViewSize.x - physicalLabelBounds.left())) + physicalLabelBounds = physicalLabelBounds.withSize( + physicalLabelBounds.size.withX(physicalViewSize.x - physicalLabelBounds.left()), + ) } physicalLabelBounds = physicalLabelBounds @@ -230,7 +251,7 @@ export class FlamechartPanZoomView extends ReloadableComponent { + this.props.canvasContext.renderInto(this.container, context => { this.props.flamechartRenderer.render({ physicalSpaceDstRect: new Rect(Vec2.zero, this.physicalViewSize()), configSpaceSrcRect: this.props.configSpaceViewportRect, - renderOutlines: true + renderOutlines: true, }) }) } @@ -333,7 +359,7 @@ export class FlamechartPanZoomView extends ReloadableComponent { if (this.interactionLock) { if (!this.frameHadWheelEvent) { - this.framesWithoutWheelEvents++; + this.framesWithoutWheelEvents++ if (this.framesWithoutWheelEvents >= 2) { this.interactionLock = null this.framesWithoutWheelEvents = 0 @@ -367,12 +393,15 @@ export class FlamechartPanZoomView extends ReloadableComponent { const width = frame.end - frame.start - const configSpaceBounds = new Rect( - new Vec2(frame.start, depth), - new Vec2(width, 1) - ) + const configSpaceBounds = new Rect(new Vec2(frame.start, depth), new Vec2(width, 1)) if (configSpaceMouse.x < configSpaceBounds.left()) return null if (configSpaceMouse.x > configSpaceBounds.right()) return null if (configSpaceBounds.contains(configSpaceMouse)) { this.hoveredLabel = { configSpaceBounds, - node: frame.node + node: frame.node, } } @@ -460,7 +490,7 @@ export class FlamechartPanZoomView extends ReloadableComponent { if (!this.container) return - const {width, height} = this.container.getBoundingClientRect() + const { width, height } = this.container.getBoundingClientRect() if (ev.key === '=' || ev.key === '+') { this.zoom(new Vec2(width / 2, height / 2), 0.5) @@ -528,8 +558,9 @@ export class FlamechartPanZoomView extends ReloadableComponent - + ref={this.containerRef} + > +
) } @@ -587,37 +616,40 @@ export class FlamechartView extends ReloadableComponent { const configSpaceOriginBounds = new Rect( new Vec2(0, -1), - Vec2.max(new Vec2(0, 0), this.configSpaceSize().minus(viewportRect.size)) + Vec2.max(new Vec2(0, 0), this.configSpaceSize().minus(viewportRect.size)), ) const configSpaceSizeBounds = new Rect( new Vec2(this.minConfigSpaceViewportRectWidth(), viewportRect.height()), - new Vec2(this.configSpaceSize().x, viewportRect.height()) + new Vec2(this.configSpaceSize().x, viewportRect.height()), ) this.setState({ configSpaceViewportRect: new Rect( configSpaceOriginBounds.closestPointTo(viewportRect.origin), - configSpaceSizeBounds.closestPointTo(viewportRect.size) - ) + configSpaceSizeBounds.closestPointTo(viewportRect.size), + ), }) } @@ -629,8 +661,8 @@ export class FlamechartView extends ReloadableComponent { this.setState({ hoveredNode, - logicalSpaceMouse: logicalSpaceMouse.plus(new Vec2(0, Sizes.MINIMAP_HEIGHT)) - }); + logicalSpaceMouse: logicalSpaceMouse.plus(new Vec2(0, Sizes.MINIMAP_HEIGHT)), + }) } formatValue(weight: number) { @@ -652,7 +684,7 @@ export class FlamechartView extends ReloadableComponent
- {this.formatValue(hoveredNode.getTotalWeight())}{' '} + + {this.formatValue(hoveredNode.getTotalWeight())} + {' '} {hoveredNode.frame.name}
) } - containerRef = (container?: Element) => { this.container = container as HTMLDivElement || null } + containerRef = (container?: Element) => { + this.container = (container as HTMLDivElement) || null + } panZoomView: FlamechartPanZoomView | null = null panZoomRef = (view: FlamechartPanZoomView | null) => { @@ -692,7 +728,7 @@ export class FlamechartView extends ReloadableComponent + setConfigSpaceViewportRect={this.setConfigSpaceViewportRect} + /> {this.renderTooltip()}
- ) + ) } } diff --git a/flamechart.ts b/flamechart.ts index 2ad9e62..5822fca 100644 --- a/flamechart.ts +++ b/flamechart.ts @@ -1,4 +1,4 @@ -import {Frame, CallTreeNode} from './profile' +import { Frame, CallTreeNode } from './profile' import { lastOf } from './utils' @@ -19,7 +19,7 @@ interface FlamechartDataSource { forEachCall( openFrame: (node: CallTreeNode, value: number) => void, - closeFrame: (value: number) => void + closeFrame: (value: number) => void, ): void getColorBucketForFrame(f: Frame): number @@ -31,11 +31,21 @@ export class Flamechart { private totalWeight: number = 0 private minFrameWidth: number = 1 - getTotalWeight() { return this.totalWeight } - getLayers() { return this.layers } - getColorBucketForFrame(frame: Frame) { return this.source.getColorBucketForFrame(frame) } - getMinFrameWidth() { return this.minFrameWidth } - formatValue(v: number) { return this.source.formatValue(v) } + getTotalWeight() { + return this.totalWeight + } + getLayers() { + return this.layers + } + getColorBucketForFrame(frame: Frame) { + return this.source.getColorBucketForFrame(frame) + } + getMinFrameWidth() { + return this.minFrameWidth + } + formatValue(v: number) { + return this.source.formatValue(v) + } constructor(private source: FlamechartDataSource) { const stack: FlamechartFrame[] = [] @@ -71,4 +81,4 @@ export class Flamechart { if (!isFinite(this.minFrameWidth)) this.minFrameWidth = 1 } -} \ No newline at end of file +} diff --git a/import/bg-flamegraph.ts b/import/bg-flamegraph.ts index 73bc17d..eec01e1 100644 --- a/import/bg-flamegraph.ts +++ b/import/bg-flamegraph.ts @@ -1,6 +1,6 @@ // https://github.com/brendangregg/FlameGraph#2-fold-stacks -import {Profile, FrameInfo} from '../profile' +import { Profile, FrameInfo } from '../profile' interface BGSample { stack: FrameInfo[] @@ -9,10 +9,10 @@ interface BGSample { function parseBGFoldedStacks(contents: string): BGSample[] { const samples: BGSample[] = [] - contents.replace(/^(.*) (\d+)$/mg, (match: string, stack: string, n: string) => { + contents.replace(/^(.*) (\d+)$/gm, (match: string, stack: string, n: string) => { samples.push({ - stack: stack.split(';').map(name => ({key: name, name: name})), - duration: parseInt(n, 10) + stack: stack.split(';').map(name => ({ key: name, name: name })), + duration: parseInt(n, 10), }) return match }) @@ -27,4 +27,4 @@ export function importFromBGFlameGraph(contents: string): Profile { profile.appendSample(sample.stack, sample.duration) } return profile -} \ No newline at end of file +} diff --git a/import/chrome.ts b/import/chrome.ts index be05af3..29fd9db 100644 --- a/import/chrome.ts +++ b/import/chrome.ts @@ -1,29 +1,29 @@ -import {Profile, TimeFormatter, FrameInfo} from '../profile' -import {getOrInsert, lastOf} from '../utils' +import { Profile, TimeFormatter, FrameInfo } from '../profile' +import { getOrInsert, lastOf } from '../utils' interface TimelineEvent { - pid: number, - tid: number, - ts: number, - ph: string, - cat: string, - name: string, - dur: number, - tdur: number, - tts: number, + pid: number + tid: number + ts: number + ph: string + cat: string + name: string + dur: number + tdur: number + tts: number args: { [key: string]: any } } interface PositionTickInfo { - line: number, + line: number ticks: number } interface CPUProfileCallFrame { - columnNumber: number, - functionName: string, - lineNumber: number, - scriptId: string, + columnNumber: number + functionName: string + lineNumber: number + scriptId: string url: string } @@ -37,10 +37,10 @@ interface CPUProfileNode { } interface CPUProfile { - startTime: number, - endTime: number, - nodes: CPUProfileNode[], - samples: number[], + startTime: number + endTime: number + nodes: CPUProfileNode[] + samples: number[] timeDeltas: number[] } @@ -48,18 +48,18 @@ export function importFromChromeTimeline(events: TimelineEvent[]) { // It seems like sometimes Chrome timeline files contain multiple CpuProfiles? // For now, choose the first one in the list. for (let event of events) { - if (event.name == "CpuProfile") { + if (event.name == 'CpuProfile') { const chromeProfile = event.args.data.cpuProfile as CPUProfile return importFromChromeCPUProfile(chromeProfile) } } - throw new Error("Could not find CPU profile in Timeline") + throw new Error('Could not find CPU profile in Timeline') } const callFrameToFrameInfo = new Map() function frameInfoForCallFrame(callFrame: CPUProfileCallFrame) { - return getOrInsert(callFrameToFrameInfo, callFrame, (callFrame) => { - const name = callFrame.functionName || "(anonymous)" + return getOrInsert(callFrameToFrameInfo, callFrame, callFrame => { + const name = callFrame.functionName || '(anonymous)' const file = callFrame.url const line = callFrame.lineNumber const col = callFrame.columnNumber @@ -68,7 +68,7 @@ function frameInfoForCallFrame(callFrame: CPUProfileCallFrame) { name, file, line, - col + col, } }) } @@ -117,7 +117,7 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile) { let value = 0 for (let i = 0; i < samples.length; i++) { - const timeDelta = timeDeltas[i+1] || 0 + const timeDelta = timeDeltas[i + 1] || 0 const nodeId = samples[i] let stackTop = nodeById.get(nodeId) if (!stackTop) continue @@ -130,7 +130,10 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile) { for ( lca = stackTop; lca && prevStack.indexOf(lca) === -1; - lca = lca.callFrame.functionName === "(garbage collector)" ? lastOf(prevStack) : lca.parent || null + lca = + lca.callFrame.functionName === '(garbage collector)' + ? lastOf(prevStack) + : lca.parent || null ) {} // Close frames that are no longer open @@ -146,7 +149,10 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile) { let node: CPUProfileNode | null = stackTop; node && node != lca; // Place GC calls on top of the previous call stack - node = node.callFrame.functionName === "(garbage collector)" ? lastOf(prevStack) : node.parent || null + node = + node.callFrame.functionName === '(garbage collector)' + ? lastOf(prevStack) + : node.parent || null ) { toOpen.push(node) } @@ -167,4 +173,4 @@ export function importFromChromeCPUProfile(chromeProfile: CPUProfile) { profile.setValueFormatter(new TimeFormatter('microseconds')) return profile -} \ No newline at end of file +} diff --git a/import/stackprof.ts b/import/stackprof.ts index 7cabeeb..8944b9c 100644 --- a/import/stackprof.ts +++ b/import/stackprof.ts @@ -1,6 +1,6 @@ // https://github.com/tmm1/stackprof -import {Profile, TimeFormatter, FrameInfo} from '../profile' +import { Profile, TimeFormatter, FrameInfo } from '../profile' interface StackprofFrame { name: string @@ -9,7 +9,7 @@ interface StackprofFrame { } export interface StackprofProfile { - frames: {[number: string]: StackprofFrame} + frames: { [number: string]: StackprofFrame } raw: number[] raw_timestamp_deltas: number[] } @@ -18,9 +18,9 @@ export function importFromStackprof(stackprofProfile: StackprofProfile): Profile const duration = stackprofProfile.raw_timestamp_deltas.reduce((a, b) => a + b, 0) const profile = new Profile(duration) - const {frames, raw, raw_timestamp_deltas} = stackprofProfile + const { frames, raw, raw_timestamp_deltas } = stackprofProfile let sampleIndex = 0 - for (let i = 0; i < raw.length;) { + for (let i = 0; i < raw.length; ) { const stackHeight = raw[i++] const stack: FrameInfo[] = [] @@ -28,7 +28,7 @@ export function importFromStackprof(stackprofProfile: StackprofProfile): Profile const id = raw[i++] stack.push({ key: id, - ...frames[id] + ...frames[id], }) } const nSamples = raw[i++] @@ -43,4 +43,4 @@ export function importFromStackprof(stackprofProfile: StackprofProfile): Profile profile.setValueFormatter(new TimeFormatter('microseconds')) return profile -} \ No newline at end of file +} diff --git a/lru-cache.ts b/lru-cache.ts index 2869607..53b26be 100644 --- a/lru-cache.ts +++ b/lru-cache.ts @@ -1,18 +1,24 @@ class ListNode { prev: ListNode | null = null next: ListNode | null = null - constructor(readonly data: V) { } + constructor(readonly data: V) {} } export class List { private head: ListNode | null = null private tail: ListNode | null = null private size: number = 0 - constructor() { } + constructor() {} - getHead(): ListNode | null { return this.head } - getTail(): ListNode | null { return this.tail } - getSize(): number { return this.size } + getHead(): ListNode | null { + return this.head + } + getTail(): ListNode | null { + return this.tail + } + getSize(): number { + return this.size + } append(node: ListNode): void { if (!this.tail) { @@ -97,7 +103,7 @@ export class LRUCache { private list = new List() private map = new Map>() - constructor(private capacity: number) { } + constructor(private capacity: number) {} has(key: K): boolean { return this.map.has(key) @@ -115,9 +121,13 @@ export class LRUCache { return node ? node.value : null } - getSize() { return this.list.getSize() } + getSize() { + return this.list.getSize() + } - getCapacity() { return this.capacity } + getCapacity() { + return this.capacity + } insert(key: K, value: V) { const node = this.map.get(key) @@ -149,4 +159,4 @@ export class LRUCache { this.map.delete(key) return [key, value] } -} \ No newline at end of file +} diff --git a/math.ts b/math.ts index e174a14..01b435c 100644 --- a/math.ts +++ b/math.ts @@ -6,19 +6,43 @@ export function clamp(x: number, minVal: number, maxVal: number) { export class Vec2 { constructor(readonly x: number, readonly y: number) {} - withX(x: number) { return new Vec2(x, this.y) } - withY(y: number) { return new Vec2(this.x, y) } + withX(x: number) { + return new Vec2(x, this.y) + } + withY(y: number) { + return new Vec2(this.x, y) + } - plus(other: Vec2) { return new Vec2(this.x + other.x, this.y + other.y) } - minus(other: Vec2) { return new Vec2(this.x - other.x, this.y - other.y) } - times(scalar: number) { return new Vec2(this.x * scalar, this.y * scalar) } - timesPointwise(other: Vec2) { return new Vec2(this.x * other.x, this.y * other.y) } - dividedByPointwise(other: Vec2) { return new Vec2(this.x / other.x, this.y / other.y) } - dot(other: Vec2) { return this.x * other.x + this.y * other.y } - equals(other: Vec2) { return this.x === other.x && this.y === other.y } - length2() { return this.dot(this) } - length() { return Math.sqrt(this.length2()) } - abs() { return new Vec2(Math.abs(this.x), Math.abs(this.y)) } + plus(other: Vec2) { + return new Vec2(this.x + other.x, this.y + other.y) + } + minus(other: Vec2) { + return new Vec2(this.x - other.x, this.y - other.y) + } + times(scalar: number) { + return new Vec2(this.x * scalar, this.y * scalar) + } + timesPointwise(other: Vec2) { + return new Vec2(this.x * other.x, this.y * other.y) + } + dividedByPointwise(other: Vec2) { + return new Vec2(this.x / other.x, this.y / other.y) + } + dot(other: Vec2) { + return this.x * other.x + this.y * other.y + } + equals(other: Vec2) { + return this.x === other.x && this.y === other.y + } + length2() { + return this.dot(this) + } + length() { + return Math.sqrt(this.length2()) + } + abs() { + return new Vec2(Math.abs(this.x), Math.abs(this.y)) + } static min(a: Vec2, b: Vec2) { return new Vec2(Math.min(a.x, b.x), Math.min(a.y, b.y)) @@ -31,52 +55,56 @@ export class Vec2 { static zero = new Vec2(0, 0) static unit = new Vec2(1, 1) - flatten(): [number, number] { return [this.x, this.y] } + flatten(): [number, number] { + return [this.x, this.y] + } } export class AffineTransform { constructor( - readonly m00 = 1, readonly m01 = 0, readonly m02 = 0, - readonly m10 = 0, readonly m11 = 1, readonly m12 = 0 + readonly m00 = 1, + readonly m01 = 0, + readonly m02 = 0, + readonly m10 = 0, + readonly m11 = 1, + readonly m12 = 0, ) {} withScale(s: Vec2) { - let { - m00, m01, m02, - m10, m11, m12 - } = this + let { m00, m01, m02, m10, m11, m12 } = this m00 = s.x m11 = s.y return new AffineTransform(m00, m01, m02, m10, m11, m12) } static withScale(s: Vec2) { - return (new AffineTransform).withScale(s) + return new AffineTransform().withScale(s) + } + scaledBy(s: Vec2) { + return AffineTransform.withScale(s).times(this) + } + getScale() { + return new Vec2(this.m00, this.m11) } - scaledBy(s: Vec2) { return AffineTransform.withScale(s).times(this) } - getScale() { return new Vec2(this.m00, this.m11) } withTranslation(t: Vec2) { - let { - m00, m01, m02, - m10, m11, m12 - } = this + let { m00, m01, m02, m10, m11, m12 } = this m02 = t.x m12 = t.y return new AffineTransform(m00, m01, m02, m10, m11, m12) } static withTranslation(t: Vec2) { - return (new AffineTransform).withTranslation(t) + return new AffineTransform().withTranslation(t) + } + getTranslation() { + return new Vec2(this.m02, this.m12) + } + translatedBy(t: Vec2) { + return AffineTransform.withTranslation(t).times(this) } - getTranslation() { return new Vec2(this.m02, this.m12) } - translatedBy(t: Vec2) { return AffineTransform.withTranslation(t).times(this) } static betweenRects(from: Rect, to: Rect) { - return AffineTransform - .withTranslation(from.origin.times(-1)) - .scaledBy(new Vec2( - to.size.x / from.size.x, - to.size.y / from.size.y - )) + return AffineTransform.withTranslation(from.origin.times(-1)) + .scaledBy(new Vec2(to.size.x / from.size.x, to.size.y / from.size.y)) .translatedBy(to.origin) } @@ -92,34 +120,34 @@ export class AffineTransform { } equals(other: AffineTransform) { - return this.m00 == other.m00 && - this.m01 == other.m01 && - this.m02 == other.m02 && - this.m10 == other.m10 && - this.m11 == other.m11 && - this.m12 == other.m12; + return ( + this.m00 == other.m00 && + this.m01 == other.m01 && + this.m02 == other.m02 && + this.m10 == other.m10 && + this.m11 == other.m11 && + this.m12 == other.m12 + ) } timesScalar(s: number) { - const {m00, m01, m02, m10, m11, m12} = this - return new AffineTransform(s*m00, s*m01, s*m02, s*m10, s*m11, s*m12) + const { m00, m01, m02, m10, m11, m12 } = this + return new AffineTransform(s * m00, s * m01, s * m02, s * m10, s * m11, s * m12) } det() { - const {m00, m01, m02, m10, m11, m12} = this + const { m00, m01, m02, m10, m11, m12 } = this const m20 = 0 const m21 = 0 const m22 = 1 return ( - m00 * (m11 * m22 - m12 * m21) - - m01 * (m10 * m22 - m12 * m20) + - m02 * (m10 * m21 - m11 * m20) + m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m12 * m20) + m02 * (m10 * m21 - m11 * m20) ) } adj() { - const {m00, m01, m02, m10, m11, m12} = this + const { m00, m01, m02, m10, m11, m12 } = this const m20 = 0 const m21 = 0 const m22 = 1 @@ -149,10 +177,7 @@ export class AffineTransform { } transformVector(v: Vec2) { - return new Vec2( - v.x * this.m00 + v.y * this.m01, - v.x * this.m10 + v.y * this.m11 - ) + return new Vec2(v.x * this.m00 + v.y * this.m01, v.x * this.m10 + v.y * this.m11) } inverseTransformVector(v: Vec2): Vec2 | null { @@ -164,7 +189,7 @@ export class AffineTransform { transformPosition(v: Vec2) { return new Vec2( v.x * this.m00 + v.y * this.m01 + this.m02, - v.x * this.m10 + v.y * this.m11 + this.m12 + v.x * this.m10 + v.y * this.m11 + this.m12, ) } @@ -189,7 +214,7 @@ export class AffineTransform { return new Rect(origin, size) } - inverseTransformRect(r: Rect): Rect | null{ + inverseTransformRect(r: Rect): Rect | null { const inv = this.inverted() if (!inv) return null return inv.transformRect(r) @@ -197,44 +222,60 @@ export class AffineTransform { flatten(): [number, number, number, number, number, number, number, number, number] { // Flatten into GLSL format - return [ - this.m00, this.m10, 0, - this.m01, this.m11, 0, - this.m02, this.m12, 1, - ] + return [this.m00, this.m10, 0, this.m01, this.m11, 0, this.m02, this.m12, 1] } } export class Rect { - constructor( - readonly origin: Vec2, - readonly size: Vec2 - ) {} + constructor(readonly origin: Vec2, readonly size: Vec2) {} - isEmpty() { return this.width() == 0 || this.height() == 0 } + isEmpty() { + return this.width() == 0 || this.height() == 0 + } - width() { return this.size.x } - height() { return this.size.y } + width() { + return this.size.x + } + height() { + return this.size.y + } - left() { return this.origin.x } - right() { return this.left() + this.width() } - top() { return this.origin.y } - bottom() { return this.top() + this.height() } + left() { + return this.origin.x + } + right() { + return this.left() + this.width() + } + top() { + return this.origin.y + } + bottom() { + return this.top() + this.height() + } - topLeft() { return this.origin } - topRight() { return this.origin.plus(new Vec2(this.width(), 0)) } + topLeft() { + return this.origin + } + topRight() { + return this.origin.plus(new Vec2(this.width(), 0)) + } - bottomRight() { return this.origin.plus(this.size) } - bottomLeft() { return this.origin.plus(new Vec2(0, this.height())) } + bottomRight() { + return this.origin.plus(this.size) + } + bottomLeft() { + return this.origin.plus(new Vec2(0, this.height())) + } - withOrigin(origin: Vec2) { return new Rect(origin, this.size) } - withSize(size: Vec2) { return new Rect(this.origin, size) } + withOrigin(origin: Vec2) { + return new Rect(origin, this.size) + } + withSize(size: Vec2) { + return new Rect(this.origin, size) + } closestPointTo(p: Vec2) { - return new Vec2( - clamp(p.x, this.left(), this.right()), - clamp(p.y, this.top(), this.bottom()) - ) + return new Vec2(clamp(p.x, this.left(), this.right()), clamp(p.y, this.top(), this.bottom())) } distanceFrom(p: Vec2) { @@ -270,4 +311,4 @@ export class Rect { static empty = new Rect(Vec2.zero, Vec2.zero) static unit = new Rect(Vec2.zero, Vec2.unit) static NDC = new Rect(new Vec2(-1, -1), new Vec2(2, 2)) -} \ No newline at end of file +} diff --git a/overlay-rectangle-renderer.ts b/overlay-rectangle-renderer.ts index 05bbec1..8b23d23 100644 --- a/overlay-rectangle-renderer.ts +++ b/overlay-rectangle-renderer.ts @@ -66,12 +66,12 @@ export class ViewportRectangleRenderer { srcRGB: 'src alpha', srcAlpha: 'one', dstRGB: 'one minus src alpha', - dstAlpha: 'one' - } + dstAlpha: 'one', + }, }, depth: { - enable: false + enable: false, }, attributes: { @@ -83,12 +83,7 @@ export class ViewportRectangleRenderer { // | /| // |/ | // 2 +--+ 3 - position: [ - [-1, 1], - [1, 1], - [-1, -1], - [1, -1] - ] + position: [[-1, 1], [1, 1], [-1, -1], [1, -1]], }, uniforms: { @@ -109,12 +104,12 @@ export class ViewportRectangleRenderer { }, framebufferHeight: (context, props) => { return context.framebufferHeight - } + }, }, primitive: 'triangle strip', - count: 4 + count: 4, }) } @@ -122,6 +117,10 @@ export class ViewportRectangleRenderer { this.command(props) } - resetStats() { return Object.assign(this.command.stats, { cpuTime: 0, gpuTime: 0, count: 0 }) } - stats() { return this.command.stats } -} \ No newline at end of file + resetStats() { + return Object.assign(this.command.stats, { cpuTime: 0, gpuTime: 0, count: 0 }) + } + stats() { + return this.command.stats + } +} diff --git a/package-lock.json b/package-lock.json index 6007db5..fcb9f3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,53 @@ "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==", "dev": true }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -346,6 +387,27 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -1449,6 +1511,21 @@ } } }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, "caniuse-api": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", @@ -1516,6 +1593,12 @@ } } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "chokidar": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", @@ -1672,6 +1755,12 @@ "safe-buffer": "5.1.1" } }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", @@ -1791,6 +1880,21 @@ } } }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -1803,6 +1907,12 @@ "integrity": "sha1-h+kEEy1hQMXAtyAGwIwNBb17Y7M=", "dev": true }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "coa": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", @@ -2296,6 +2406,21 @@ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2338,6 +2463,15 @@ "randombytes": "2.0.6" } }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "2.0.2" + } + }, "dom-serializer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", @@ -2520,12 +2654,158 @@ } } }, + "eslint": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "babel-code-frame": "6.26.0", + "chalk": "2.3.2", + "concat-stream": "1.6.2", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.1.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", + "espree": "3.5.4", + "esquery": "1.0.1", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "11.4.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.11.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.5", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "regexpp": "1.1.0", + "require-uncached": "1.0.3", + "semver": "5.5.0", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.2", + "text-table": "0.2.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.2", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz", + "integrity": "sha512-Dyzmifil8n/TmSqYDEXbm+C8yitzJQqQIlJQLNRMwa+BOUJpRC19pyVeN12JAjt61xonvXjtff+hJruTRXn5HA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", + "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + } + } + }, + "eslint-plugin-prettier": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz", + "integrity": "sha512-floiaI4F7hRkTrFe8V2ItOK97QYrX75DjmdzmVITZoAP6Cn06oEDPQRsO6MlHEP/u2SxI3xQ52Kpjw6j5WGfeQ==", + "dev": true, + "requires": { + "fast-diff": "1.1.2", + "jest-docblock": "21.2.0" + } + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.1", + "estraverse": "4.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "5.5.3", + "acorn-jsx": "3.0.1" + }, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + } + } + }, "esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", @@ -2581,6 +2861,17 @@ } } }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.21", + "tmp": "0.0.33" + } + }, "falafel": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", @@ -2601,18 +2892,67 @@ } } }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, "filesize": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", "dev": true }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -3562,6 +3902,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -3594,6 +3940,20 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -3886,12 +4246,33 @@ "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=", "dev": true }, + "iconv-lite": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, "ieee754": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", "dev": true }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -3926,6 +4307,28 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.1.0", + "chalk": "2.3.2", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.2.0", + "figures": "2.0.0", + "lodash": "4.17.5", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4036,6 +4439,12 @@ "number-is-nan": "1.0.1" } }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "is-odd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", @@ -4053,6 +4462,30 @@ } } }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -4076,6 +4509,12 @@ } } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -4085,6 +4524,12 @@ "has": "1.0.1" } }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, "is-svg": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", @@ -4139,6 +4584,12 @@ "isarray": "1.0.0" } }, + "jest-docblock": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", + "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", + "dev": true + }, "js-base64": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", @@ -4187,6 +4638,18 @@ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -4230,6 +4693,12 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -4351,6 +4820,12 @@ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", "dev": true }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -4414,6 +4889,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", @@ -4460,6 +4941,12 @@ } } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "nice-try": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", @@ -4711,6 +5198,15 @@ "wrappy": "1.0.2" } }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, "opn": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", @@ -5118,6 +5614,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -5149,6 +5651,33 @@ "integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA=", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -7154,6 +7683,12 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "prettier": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.12.0.tgz", + "integrity": "sha512-Wz0SMncgaglBzDcohH3ZIAi4nVpzOIEweFzCOmgVEoRSeO72b4dcKGfgxoRGVMaFlh1r7dlVaJ+f3CIHfeH6xg==", + "dev": true + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -7172,6 +7707,12 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -7377,6 +7918,12 @@ "safe-regex": "1.1.0" } }, + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", @@ -7444,6 +7991,16 @@ "is-finite": "1.0.2" } }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -7453,18 +8010,43 @@ "path-parse": "1.0.5" } }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", @@ -7475,6 +8057,30 @@ "inherits": "2.0.3" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -7490,6 +8096,12 @@ "ret": "0.1.15" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "safer-eval": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/safer-eval/-/safer-eval-1.2.3.tgz", @@ -7632,12 +8244,27 @@ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", "dev": true }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -7996,6 +8623,16 @@ "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", "dev": true }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8022,6 +8659,12 @@ } } }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -8055,6 +8698,32 @@ } } }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.3.2", + "lodash": "4.17.5", + "slice-ansi": "1.0.0", + "string-width": "2.1.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", @@ -8080,6 +8749,15 @@ "integrity": "sha1-k9nez/yIBb1X6uQxDwt0Xptvs6c=", "dev": true }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -8179,6 +8857,16 @@ "integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg==", "dev": true }, + "typescript-eslint-parser": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-14.0.0.tgz", + "integrity": "sha512-xDvhgZX08RRKSyz8oWXI/V9gjXelh4/a9jGFmmHnn/j/GMXKOQ6E6NAGVjQSvGBrjT/TMobKqsrxnBcVr4sfIw==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, "uglify-es": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.2.2.tgz", @@ -8458,6 +9146,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, "ws": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", @@ -8473,6 +9170,12 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } } diff --git a/package.json b/package.json index 2091266..f4fead7 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,11 @@ "description": "", "main": "index.js", "scripts": { - "serve": "parcel index.html --open --no-autoinstall", - "deploy": "./deploy.sh" + "deploy": "./deploy.sh", + "prettier": "prettier --write './**/*.ts' './**/*.tsx'", + "lint": "eslint './**/*.ts' './**/*.tsx'", + "test": "npm run lint", + "serve": "parcel index.html --open --no-autoinstall" }, "browserslist": [ "last 2 Chrome versions", @@ -15,10 +18,14 @@ "license": "MIT", "devDependencies": { "aphrodite": "2.1.0", + "eslint": "^4.19.1", + "eslint-plugin-prettier": "^2.6.0", "parcel-bundler": "1.7.0", "preact": "8.2.7", + "prettier": "^1.12.0", "regl": "1.3.1", "typescript": "2.8.1", + "typescript-eslint-parser": "^14.0.0", "uglify-es": "3.2.2" } } diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..a3a29d3 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,6 @@ +module.exports = { + printWidth: 100, + semi: false, + singleQuote: true, + trailingComma: 'all', +}; diff --git a/profile.ts b/profile.ts index 8a04419..9b04b7e 100644 --- a/profile.ts +++ b/profile.ts @@ -25,10 +25,18 @@ export interface FrameInfo { export class HasWeights { private selfWeight = 0 private totalWeight = 0 - getSelfWeight() { return this.selfWeight } - getTotalWeight() { return this.totalWeight } - addToTotalWeight(delta: number) { this.totalWeight += delta } - addToSelfWeight(delta: number) { this.selfWeight += delta } + getSelfWeight() { + return this.selfWeight + } + getTotalWeight() { + return this.totalWeight + } + addToTotalWeight(delta: number) { + this.totalWeight += delta + } + addToSelfWeight(delta: number) { + this.selfWeight += delta + } } export class Frame extends HasWeights { @@ -81,7 +89,7 @@ export class RawValueFormatter implements ValueFormatter { } export class TimeFormatter implements ValueFormatter { - private multiplier : number + private multiplier: number constructor(unit: 'nanoseconds' | 'microseconds' | 'milliseconds' | 'seconds') { if (unit === 'nanoseconds') this.multiplier = 1e-9 @@ -93,7 +101,7 @@ export class TimeFormatter implements ValueFormatter { format(v: number) { const s = v * this.multiplier - if (s / 1e0 >= 1) return `${s.toFixed(2)}s` + if (s / 1 >= 1) return `${s.toFixed(2)}s` if (s / 1e-3 >= 1) return `${(s / 1e-3).toFixed(2)}ms` if (s / 1e-6 >= 1) return `${(s / 1e-6).toFixed(2)}µs` else return `${(s / 1e-9).toFixed(2)}ms` @@ -120,20 +128,30 @@ export class Profile { this.totalWeight = totalWeight } - formatValue(v: number) { return this.valueFormatter.format(v) } - setValueFormatter(f: ValueFormatter) { this.valueFormatter = f } + formatValue(v: number) { + return this.valueFormatter.format(v) + } + setValueFormatter(f: ValueFormatter) { + this.valueFormatter = f + } - getName() { return this.name } - setName(name: string) { this.name = name } + getName() { + return this.name + } + setName(name: string) { + this.name = name + } - getTotalWeight() { return this.totalWeight } + getTotalWeight() { + return this.totalWeight + } getTotalNonIdleWeight() { return this.groupedCalltreeRoot.children.reduce((n, c) => n + c.getTotalWeight(), 0) } forEachCallGrouped( openFrame: (node: CallTreeNode, value: number) => void, - closeFrame: (value: number) => void + closeFrame: (value: number) => void, ) { function visit(node: CallTreeNode, start: number) { if (node.frame !== rootFrame) { @@ -143,9 +161,9 @@ export class Profile { let childTime = 0 const children = [...node.children] - children.sort((a, b) => a.getTotalWeight() > b.getTotalWeight() ? -1 : 1) + children.sort((a, b) => (a.getTotalWeight() > b.getTotalWeight() ? -1 : 1)) - children.forEach(function (child) { + children.forEach(function(child) { visit(child, start + childTime) childTime += child.getTotalWeight() }) @@ -159,7 +177,7 @@ export class Profile { forEachCall( openFrame: (node: CallTreeNode, value: number) => void, - closeFrame: (value: number) => void + closeFrame: (value: number) => void, ) { let prevStack: CallTreeNode[] = [] let value = 0 @@ -220,7 +238,9 @@ export class Profile { for (let frameInfo of stack) { const frame = getOrInsert(this.frames, frameInfo.key, () => new Frame(frameInfo)) - const last = useAppendOrder ? lastOf(node.children) : node.children.find(c => c.frame === frame) + const last = useAppendOrder + ? lastOf(node.children) + : node.children.find(c => c.frame === frame) if (last && last.frame == frame) { node = last } else { @@ -303,7 +323,9 @@ export class Profile { } } - const last = useAppendOrder ? lastOf(prevTop.children) : prevTop.children.find(c => c.frame === frame) + const last = useAppendOrder + ? lastOf(prevTop.children) + : prevTop.children.find(c => c.frame === frame) let node: CallTreeNode if (last && last.frame == frame) { node = last diff --git a/rectangle-batch-renderer.ts b/rectangle-batch-renderer.ts index 41ec0c6..b1714a0 100644 --- a/rectangle-batch-renderer.ts +++ b/rectangle-batch-renderer.ts @@ -10,19 +10,23 @@ export class RectangleBatch { private configSpaceSizes = new Float32Array(this.rectCapacity * 2) private colors = new Float32Array(this.rectCapacity * 3) - constructor(private gl: regl.Instance) { } + constructor(private gl: regl.Instance) {} - getRectCount() { return this.rectCount } + getRectCount() { + return this.rectCount + } private configSpaceOffsetBuffer: regl.Buffer | null = null getConfigSpaceOffsetBuffer() { - if (!this.configSpaceOffsetBuffer) this.configSpaceOffsetBuffer = this.gl.buffer(this.configSpaceOffsets) + if (!this.configSpaceOffsetBuffer) + this.configSpaceOffsetBuffer = this.gl.buffer(this.configSpaceOffsets) return this.configSpaceOffsetBuffer } private configSpaceSizeBuffer: regl.Buffer | null = null getConfigSpaceSizeBuffer() { - if (!this.configSpaceSizeBuffer) this.configSpaceSizeBuffer = this.gl.buffer(this.configSpaceSizes) + if (!this.configSpaceSizeBuffer) + this.configSpaceSizeBuffer = this.gl.buffer(this.configSpaceSizes) return this.configSpaceSizeBuffer } @@ -105,11 +109,11 @@ export class RectangleBatchRenderer { } `, - depth: { - enable: false - }, + depth: { + enable: false, + }, - frag: ` + frag: ` precision mediump float; varying vec3 vColor; varying float vParity; @@ -121,12 +125,7 @@ export class RectangleBatchRenderer { attributes: { // Non-instanced attributes - corner: gl.buffer([ - [0, 0], - [1, 0], - [0, 1], - [1, 1], - ]), + corner: gl.buffer([[0, 0], [1, 0], [0, 1], [1, 1]]), // Instanced attributes configSpaceOffset: (context, props) => { @@ -135,7 +134,7 @@ export class RectangleBatchRenderer { offset: 0, stride: 2 * 4, size: 2, - divisor: 1 + divisor: 1, } }, configSpaceSize: (context, props) => { @@ -144,7 +143,7 @@ export class RectangleBatchRenderer { offset: 0, stride: 2 * 4, size: 2, - divisor: 1 + divisor: 1, } }, color: (context, props) => { @@ -153,22 +152,23 @@ export class RectangleBatchRenderer { offset: 0, stride: 3 * 4, size: 3, - divisor: 1 + divisor: 1, } - } + }, }, uniforms: { configSpaceToNDC: (context, props) => { const configToPhysical = AffineTransform.betweenRects( props.configSpaceSrcRect, - props.physicalSpaceDstRect + props.physicalSpaceDstRect, ) const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight) - const physicalToNDC = AffineTransform.withTranslation(new Vec2(-1, 1)) - .times(AffineTransform.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize))) + const physicalToNDC = AffineTransform.withTranslation(new Vec2(-1, 1)).times( + AffineTransform.withScale(new Vec2(2, -2).dividedByPointwise(viewportSize)), + ) return physicalToNDC.times(configToPhysical).flatten() }, @@ -179,7 +179,7 @@ export class RectangleBatchRenderer { parityMin: (context, props) => { return props.parityMin == null ? 0 : 1 + props.parityMin - } + }, }, instances: (context, props) => { @@ -196,6 +196,10 @@ export class RectangleBatchRenderer { this.command(props) } - resetStats() { return Object.assign(this.command.stats, { cpuTime: 0, gpuTime: 0, count: 0 }) } - stats() { return this.command.stats } -} \ No newline at end of file + resetStats() { + return Object.assign(this.command.stats, { cpuTime: 0, gpuTime: 0, count: 0 }) + } + stats() { + return this.command.stats + } +} diff --git a/regl.d.ts b/regl.d.ts index 4816b60..190d672 100644 --- a/regl.d.ts +++ b/regl.d.ts @@ -1,4 +1,4 @@ -declare module "regl" { +declare module 'regl' { interface InitializationOptions { /** A reference to a WebGL rendering context. (Default created from canvas) */ gl?: WebGLRenderingContext @@ -57,7 +57,24 @@ declare module "regl" { export type vec3 = [number, number, number] export type vec4 = [number, number, number, number] export type mat3 = [number, number, number, number, number, number, number, number, number] - export type mat4 = [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number] + export type mat4 = [ + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number + ] type GlslPrimitive = number | vec2 | vec3 | vec4 | mat3 | mat4 interface Tick { @@ -68,9 +85,9 @@ declare module "regl" {

(params: CommandOptions

): Command

clear(args: { - color?: [number, number, number, number], - depth?: number, - stencil?: number, + color?: [number, number, number, number] + depth?: number + stencil?: number }): void // TODO(jlfwong): read() @@ -127,10 +144,25 @@ declare module "regl" { frame(callback: (context: Context) => void): Tick } - type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | - Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array + type TypedArray = + | Int8Array + | Uint8Array + | Uint8ClampedArray + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array - type DrawMode = 'points' | 'lines' | 'line strip' | 'line loop' | 'triangles' | 'triangle strip' | 'triangle fan' + type DrawMode = + | 'points' + | 'lines' + | 'line strip' + | 'line loop' + | 'triangles' + | 'triangle strip' + | 'triangle fan' interface Context { tick: number @@ -177,14 +209,49 @@ declare module "regl" { } type MagFilter = 'nearest' | 'linear' - type MinFilter = 'nearest' | 'linear' | 'mipmap' | 'linear mipmap linear' | 'nearest mipmap linear' | 'nearest mipmap nearest' + type MinFilter = + | 'nearest' + | 'linear' + | 'mipmap' + | 'linear mipmap linear' + | 'nearest mipmap linear' + | 'nearest mipmap nearest' type WrapMode = 'repeat' | 'clamp' | 'mirror' - type TextureFormat = 'alpha' | 'luminance' | 'luminance alpha' | 'rgb' | 'rgba' | 'rgba4' | 'rgb5 a1' | 'rgb565' | 'srgb' | 'srgba' | 'depth' | 'depth stencil' | 'rgb s3tc dxt1' | 'rgb s3tc dxt5' | 'rgb atc' | 'rgba atc explicit alpha' | 'rgba atc interpolated alpha' | 'rgb pvrtc 4bppv1' | 'rgb pvrtc 2bppv1' | 'rgba pvrtc 4bppv1' | 'rgba pvrtc 2bppv1' | 'rgb etc1' + type TextureFormat = + | 'alpha' + | 'luminance' + | 'luminance alpha' + | 'rgb' + | 'rgba' + | 'rgba4' + | 'rgb5 a1' + | 'rgb565' + | 'srgb' + | 'srgba' + | 'depth' + | 'depth stencil' + | 'rgb s3tc dxt1' + | 'rgb s3tc dxt5' + | 'rgb atc' + | 'rgba atc explicit alpha' + | 'rgba atc interpolated alpha' + | 'rgb pvrtc 4bppv1' + | 'rgb pvrtc 2bppv1' + | 'rgba pvrtc 4bppv1' + | 'rgba pvrtc 2bppv1' + | 'rgb etc1' type TextureType = 'uint8' | 'uint16' | 'float' | 'float32' | 'half float' | 'float16' type ColorSpace = 'none' | 'browser' type MipmapHint = "don't care" | 'dont care' | 'nice' | 'fast' - type TextureData = number[] | number[][] | TypedArray | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | CanvasRenderingContext2D + type TextureData = + | number[] + | number[][] + | TypedArray + | HTMLImageElement + | HTMLVideoElement + | HTMLCanvasElement + | CanvasRenderingContext2D interface TextureOptions { width?: number height?: number @@ -233,7 +300,17 @@ declare module "regl" { // TODO(jlfwong): Cubic frame buffers interface RenderBufferOptions { - format?: 'rgba4' | 'rgb565' | 'rgb5 a1' | 'depth' | 'stencil' | 'depth stencil' | 'srgba' | 'rgba16f' | 'rgb16f' | 'rgba32f' + format?: + | 'rgba4' + | 'rgb565' + | 'rgb5 a1' + | 'depth' + | 'stencil' + | 'depth stencil' + | 'srgba' + | 'rgba16f' + | 'rgb16f' + | 'rgba32f' width?: number height?: number shape?: [number, number] @@ -256,7 +333,15 @@ declare module "regl" { depth?: boolean | RenderBuffer | Texture stencil?: boolean | RenderBuffer | Texture depthStencil?: boolean | RenderBuffer | Texture - colorFormat?: 'rgba' | 'rgba4' | 'rgb565' | 'rgb5 a1' | 'rgb16f' | 'rgba16f' | 'rgba32f' | 'srgba' + colorFormat?: + | 'rgba' + | 'rgba4' + | 'rgb565' + | 'rgb5 a1' + | 'rgb16f' + | 'rgba16f' + | 'rgba32f' + | 'srgba' colorType?: 'uint8' | 'half float' | 'float' } interface Framebuffer { @@ -281,18 +366,60 @@ declare module "regl" { size?: number divisor?: number } - type Attribute = AttributeOptions | Buffer | BufferArgs | { constant: number | vec2 | vec3 | vec4 | mat3 | mat4 } + type Attribute = + | AttributeOptions + | Buffer + | BufferArgs + | { constant: number | vec2 | vec3 | vec4 | mat3 | mat4 } interface Computed { (context: Context, props: P, batchId: number): T } type MaybeComputed = Computed | T - type DepthFunction = 'never' | 'always' | '<' | 'less' | '<=' | 'lequal' | '>' | 'greater' | '>=' | 'gequal' | '=' | 'equal' | '!=' | 'notequal' - type BlendFunction = 0 | 'zero' | 1 | 'one' | 'src color' | 'one minus src color' | 'src alpha' | 'one minus src alpha' | 'dst color' | 'one minus dst color' | 'dst alpha' | 'one minus dst alpha' | 'constant color' | 'one minus constant color' | 'one minus constant alpha' | 'src alpha saturate' + type DepthFunction = + | 'never' + | 'always' + | '<' + | 'less' + | '<=' + | 'lequal' + | '>' + | 'greater' + | '>=' + | 'gequal' + | '=' + | 'equal' + | '!=' + | 'notequal' + type BlendFunction = + | 0 + | 'zero' + | 1 + | 'one' + | 'src color' + | 'one minus src color' + | 'src alpha' + | 'one minus src alpha' + | 'dst color' + | 'one minus dst color' + | 'dst alpha' + | 'one minus dst alpha' + | 'constant color' + | 'one minus constant color' + | 'one minus constant alpha' + | 'src alpha saturate' type BlendEquation = 'add' | 'subtract' | 'reverse subtract' | 'min' | 'max' type StencilFunction = DepthFunction - type StencilOp = 'zero' | 'keep' | 'replace' | 'invert' | 'increment' | 'decrement' | 'increment wrap' | 'decrement wrap' + type StencilOp = + | 'zero' + | 'keep' + | 'replace' + | 'invert' + | 'increment' + | 'decrement' + | 'increment wrap' + | 'decrement wrap' interface CommandOptions

{ /** Source code of vertex shader */ vert?: string @@ -324,51 +451,70 @@ declare module "regl" { profile?: MaybeComputed - depth?: MaybeComputed - - blend?: MaybeComputed - - stencil?: MaybeComputed - - polygonOffset?: MaybeComputed + > - cull?: MaybeComputed + blend?: MaybeComputed< + P, + { + enable?: boolean + func?: + | { + src: BlendFunction + dst: BlendFunction + } + | { + srcRGB: BlendFunction + srcAlpha: BlendFunction + dstRGB: BlendFunction + dstAlpha: BlendFunction + } + equation?: + | BlendEquation + | { + rgb: BlendEquation + alpha: BlendEquation + } + color?: vec4 + } + > + + stencil?: MaybeComputed< + P, + { + enable?: boolean + mask?: number + func?: StencilFunction + opFront?: { fail: StencilOp; zfail: StencilOp; pass: StencilOp } + opBack?: { fail: StencilOp; zfail: StencilOp; pass: StencilOp } + } + > + + polygonOffset?: MaybeComputed< + P, + { + enable?: boolean + offset?: { + factor: number + units: number + } + } + > + + cull?: MaybeComputed< + P, + { + enable?: boolean + face?: 'front' | 'back' + } + > frontFace?: MaybeComputed @@ -378,35 +524,46 @@ declare module "regl" { colorMask?: MaybeComputed - sample?: MaybeComputed + > - scissor?: MaybeComputed + + viewport?: MaybeComputed< + P, + { x: number y: number width: number height: number } - }> - - viewport?: MaybeComputed + > } function prop

(name: keyof P): (context: Context, props: P, batchId: number) => P[keyof P] - function context

(name: keyof Context): (context: Context, props: P, batchId: number) => Context[keyof Context] + function context

( + name: keyof Context, + ): (context: Context, props: P, batchId: number) => Context[keyof Context] interface Command

{ /** One shot rendering */ diff --git a/reloadable.tsx b/reloadable.tsx index ef47808..06ea24a 100644 --- a/reloadable.tsx +++ b/reloadable.tsx @@ -1,13 +1,13 @@ -import {Component} from 'preact' +import { Component } from 'preact' export interface SerializedComponent { state: S - serializedSubcomponents: {[key: string]: any} + serializedSubcomponents: { [key: string]: any } } export abstract class ReloadableComponent extends Component { serialize(): SerializedComponent { - const serializedSubcomponents: {[key: string]: any} = Object.create(null) + const serializedSubcomponents: { [key: string]: any } = Object.create(null) const subcomponents = this.subcomponents() for (const key in subcomponents) { @@ -34,9 +34,7 @@ export abstract class ReloadableComponent extends Component { } }) } - subcomponents(): {[key: string]: any} { + subcomponents(): { [key: string]: any } { return Object.create(null) } } - - diff --git a/speedscope.tsx b/speedscope.tsx index 986375b..cc45623 100644 --- a/speedscope.tsx +++ b/speedscope.tsx @@ -1,5 +1,5 @@ -import {h, render} from 'preact' -import {Application} from'./application' +import { h, render } from 'preact' +import { Application } from './application' let app: Application | null = null const retained = (window as any)['__retained__'] as any @@ -7,7 +7,7 @@ declare const module: any if (module.hot) { module.hot.dispose(() => { if (app) { - (window as any)['__retained__'] = app.serialize() + ;(window as any)['__retained__'] = app.serialize() } }) module.hot.accept() @@ -21,4 +21,4 @@ function ref(instance: Application | null) { } } -render(, document.body, document.body.lastElementChild || undefined) \ No newline at end of file +render(, document.body, document.body.lastElementChild || undefined) diff --git a/stats.ts b/stats.ts index c1040a8..284b5ea 100644 --- a/stats.ts +++ b/stats.ts @@ -35,33 +35,32 @@ export class StatsPanel { showPanel(id: number) { for (var i = 0; i < this.container.children.length; i++) { - (this.container.children[i] as HTMLElement).style.display = i === id ? 'block' : 'none'; + ;(this.container.children[i] as HTMLElement).style.display = i === id ? 'block' : 'none' } - this.shown = id; + this.shown = id } - private beginTime: number = 0 begin() { - this.beginTime = ( performance || Date ).now(); + this.beginTime = (performance || Date).now() } private frames = 0 private prevTime = 0 end() { - this.frames++; - var time = ( performance || Date ).now(); - this.msPanel.update(time - this.beginTime, 200); + this.frames++ + var time = (performance || Date).now() + this.msPanel.update(time - this.beginTime, 200) - if ( time >= this.prevTime + 1000 ) { - this.fpsPanel.update(( this.frames * 1000 ) / ( time - this.prevTime ), 100); - this.prevTime = time; - this.frames = 0; + if (time >= this.prevTime + 1000) { + this.fpsPanel.update(this.frames * 1000 / (time - this.prevTime), 100) + this.prevTime = time + this.frames = 0 } } } -const PR = Math.round( window.devicePixelRatio || 1 ); +const PR = Math.round(window.devicePixelRatio || 1) class Panel { private min: number = Infinity @@ -75,23 +74,23 @@ class Panel { private GRAPH_X = 3 * PR private GRAPH_Y = 15 * PR private GRAPH_WIDTH = 74 * PR - private GRAPH_HEIGHT = 30 * PR; + private GRAPH_HEIGHT = 30 * PR constructor(private name: string, private fg: string, private bg: string) { - this.canvas.width = this.WIDTH; - this.canvas.height = this.HEIGHT; - this.canvas.style.cssText = 'width:80px;height:48px'; + this.canvas.width = this.WIDTH + this.canvas.height = this.HEIGHT + this.canvas.style.cssText = 'width:80px;height:48px' - this.context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif'; - this.context.textBaseline = 'top'; - this.context.fillStyle = bg; - this.context.fillRect( 0, 0, this.WIDTH, this.HEIGHT ); - this.context.fillStyle = fg; + this.context.font = 'bold ' + 9 * PR + 'px Helvetica,Arial,sans-serif' + this.context.textBaseline = 'top' + this.context.fillStyle = bg + this.context.fillRect(0, 0, this.WIDTH, this.HEIGHT) + this.context.fillStyle = fg this.context.fillText(this.name, this.TEXT_X, this.TEXT_Y) - this.context.fillRect(this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH, this.GRAPH_HEIGHT); - this.context.fillStyle = bg; - this.context.globalAlpha = 0.9; - this.context.fillRect(this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH, this.GRAPH_HEIGHT); + this.context.fillRect(this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH, this.GRAPH_HEIGHT) + this.context.fillStyle = bg + this.context.globalAlpha = 0.9 + this.context.fillRect(this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH, this.GRAPH_HEIGHT) } appendTo(el: HTMLElement) { @@ -99,18 +98,44 @@ class Panel { } update(value: number, maxValue: number) { - this.min = Math.min(this.min, value ); - this.max = Math.max(this.max, value ); + this.min = Math.min(this.min, value) + this.max = Math.max(this.max, value) - this.context.fillStyle = this.bg; - this.context.globalAlpha = 1; - this.context.fillRect( 0, 0, this.WIDTH, this.GRAPH_Y ); - this.context.fillStyle = this.fg; - this.context.fillText( Math.round( value ) + ' ' + name + ' (' + Math.round( this.min ) + '-' + Math.round( this.max ) + ')', this.TEXT_X, this.TEXT_Y ); - this.context.drawImage( this.canvas, this.GRAPH_X + PR, this.GRAPH_Y, this.GRAPH_WIDTH - PR, this.GRAPH_HEIGHT, this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH - PR, this.GRAPH_HEIGHT ); - this.context.fillRect( this.GRAPH_X + this.GRAPH_WIDTH - PR, this.GRAPH_Y, PR, this.GRAPH_HEIGHT ); - this.context.fillStyle = this.bg; - this.context.globalAlpha = 0.9; - this.context.fillRect( this.GRAPH_X + this.GRAPH_WIDTH - PR, this.GRAPH_Y, PR, Math.round( ( 1 - ( value / maxValue ) ) * this.GRAPH_HEIGHT ) ); + this.context.fillStyle = this.bg + this.context.globalAlpha = 1 + this.context.fillRect(0, 0, this.WIDTH, this.GRAPH_Y) + this.context.fillStyle = this.fg + this.context.fillText( + Math.round(value) + + ' ' + + name + + ' (' + + Math.round(this.min) + + '-' + + Math.round(this.max) + + ')', + this.TEXT_X, + this.TEXT_Y, + ) + this.context.drawImage( + this.canvas, + this.GRAPH_X + PR, + this.GRAPH_Y, + this.GRAPH_WIDTH - PR, + this.GRAPH_HEIGHT, + this.GRAPH_X, + this.GRAPH_Y, + this.GRAPH_WIDTH - PR, + this.GRAPH_HEIGHT, + ) + this.context.fillRect(this.GRAPH_X + this.GRAPH_WIDTH - PR, this.GRAPH_Y, PR, this.GRAPH_HEIGHT) + this.context.fillStyle = this.bg + this.context.globalAlpha = 0.9 + this.context.fillRect( + this.GRAPH_X + this.GRAPH_WIDTH - PR, + this.GRAPH_Y, + PR, + Math.round((1 - value / maxValue) * this.GRAPH_HEIGHT), + ) } -} \ No newline at end of file +} diff --git a/style.ts b/style.ts index 490efdd..47ef6a8 100644 --- a/style.ts +++ b/style.ts @@ -1,18 +1,18 @@ export enum FontFamily { - MONOSPACE = "Courier, monospace" + MONOSPACE = 'Courier, monospace', } export enum FontSize { LABEL = 10, TITLE = 12, - BIG_BUTTON = 36 + BIG_BUTTON = 36, } export enum Colors { - LIGHT_GRAY = "#C4C4C4", - MEDIUM_GRAY = "#BDBDBD", + LIGHT_GRAY = '#C4C4C4', + MEDIUM_GRAY = '#BDBDBD', GRAY = '#666666', DARK_GRAY = '#222222', LIGHT_BLUE = '#56CCF2', - DARK_BLUE = '#2F80ED' + DARK_BLUE = '#2F80ED', } diff --git a/texture-catched-renderer.ts b/texture-catched-renderer.ts index 5827a90..15bf667 100644 --- a/texture-catched-renderer.ts +++ b/texture-catched-renderer.ts @@ -36,7 +36,7 @@ export class TextureRenderer { `, depth: { - enable: false + enable: false, }, attributes: { @@ -48,18 +48,8 @@ export class TextureRenderer { // | /| // |/ | // 2 +--+ 3 - position: gl.buffer([ - [-1, 1], - [1, 1], - [-1, -1], - [1, -1] - ]), - uv: gl.buffer([ - [0, 1], - [1, 1], - [0, 0], - [1, 0] - ]) + position: gl.buffer([[-1, 1], [1, 1], [-1, -1], [1, -1]]), + uv: gl.buffer([[0, 1], [1, 1], [0, 0], [1, 0]]), }, uniforms: { @@ -68,34 +58,31 @@ export class TextureRenderer { const { srcRect, texture } = props const physicalToUV = AffineTransform.withTranslation(new Vec2(0, 1)) .times(AffineTransform.withScale(new Vec2(1, -1))) - .times(AffineTransform.betweenRects( + .times( + AffineTransform.betweenRects( new Rect(Vec2.zero, new Vec2(texture.width, texture.height)), - Rect.unit - )) + Rect.unit, + ), + ) const uvRect = physicalToUV.transformRect(srcRect) - return AffineTransform.betweenRects( - Rect.unit, - uvRect, - ).flatten() + return AffineTransform.betweenRects(Rect.unit, uvRect).flatten() }, positionTransform: (context, props) => { const { dstRect } = props const viewportSize = new Vec2(context.viewportWidth, context.viewportHeight) - const physicalToNDC = AffineTransform.withScale(new Vec2(1, -1)) - .times(AffineTransform.betweenRects( - new Rect(Vec2.zero, viewportSize), - Rect.NDC) - ) + const physicalToNDC = AffineTransform.withScale(new Vec2(1, -1)).times( + AffineTransform.betweenRects(new Rect(Vec2.zero, viewportSize), Rect.NDC), + ) const ndcRect = physicalToNDC.transformRect(dstRect) return AffineTransform.betweenRects(Rect.NDC, ndcRect).flatten() - } + }, }, primitive: 'triangle strip', - count: 4 + count: 4, }) } @@ -103,8 +90,12 @@ export class TextureRenderer { this.command(props) } - resetStats() { return Object.assign(this.command.stats, { cpuTime: 0, gpuTime: 0, count: 0 }) } - stats() { return this.command.stats } + resetStats() { + return Object.assign(this.command.stats, { cpuTime: 0, gpuTime: 0, count: 0 }) + } + stats() { + return this.command.stats + } } export interface TextureCachedRendererOptions { @@ -127,7 +118,7 @@ export class TextureCachedRenderer { this.textureRenderer = options.textureRenderer this.texture = gl.texture(1, 1) - this.framebuffer = gl.framebuffer({color: [this.texture]}) + this.framebuffer = gl.framebuffer({ color: [this.texture] }) this.withContext = gl({}) } @@ -141,7 +132,10 @@ export class TextureCachedRenderer { render(props: T) { this.withContext((context: regl.Context) => { let needsRender = false - if (this.texture.width !== context.viewportWidth || this.texture.height !== context.viewportHeight) { + if ( + this.texture.width !== context.viewportWidth || + this.texture.height !== context.viewportHeight + ) { // TODO(jlfwong): Can probably just use this.framebuffer.resize this.texture({ width: context.viewportWidth, height: context.viewportHeight }) this.framebuffer({ color: [this.texture] }) @@ -161,26 +155,29 @@ export class TextureCachedRenderer { x: 0, y: 0, width: context.viewportWidth, - height: context.viewportHeight + height: context.viewportHeight, } }, - framebuffer: this.framebuffer + framebuffer: this.framebuffer, })(() => { - this.gl.clear({color: [0, 0, 0, 0]}) + this.gl.clear({ color: [0, 0, 0, 0] }) this.renderUncached(props) }) } - const glViewportRect = new Rect(Vec2.zero, new Vec2(context.viewportWidth, context.viewportHeight)) + const glViewportRect = new Rect( + Vec2.zero, + new Vec2(context.viewportWidth, context.viewportHeight), + ) // Render from texture this.textureRenderer.render({ texture: this.texture, srcRect: glViewportRect, - dstRect: glViewportRect + dstRect: glViewportRect, }) this.lastRenderProps = props this.dirty = false }) } -} \ No newline at end of file +} diff --git a/utils.ts b/utils.ts index 23aa8b9..fc1054a 100644 --- a/utils.ts +++ b/utils.ts @@ -1,5 +1,5 @@ export function lastOf(ts: T[]): T | null { - return ts[ts.length-1] || null + return ts[ts.length - 1] || null } export function sortBy(ts: T[], key: (t: T) => number | string): void { @@ -26,7 +26,9 @@ export function* itMap(it: Iterable, f: (t: T) => U): Iterable { } export function itForEach(it: Iterable, f: (t: T) => void): void { - for (let t of it) { f(t) } + for (let t of it) { + f(t) + } } export function itReduce(it: Iterable, f: (a: U, b: T) => U, init: U): U { @@ -44,4 +46,4 @@ export function cachedMeasureTextWidth(ctx: CanvasRenderingContext2D, text: stri measureTextCache.set(text, ctx.measureText(text).width) } return measureTextCache.get(text)! -} \ No newline at end of file +}