From 1bcb88670bbba48524d398c0342970ebf737c317 Mon Sep 17 00:00:00 2001 From: Alan Pierce Date: Sat, 14 Apr 2018 08:17:59 -0700 Subject: [PATCH] Set up Prettier and run it on the whole codebase * Install prettier, set up the config file, and run it on all ts and tsx files. * Install eslint and configure it with just eslint-plugin-prettier to check to make sure that prettier has been run. * Add a basic .travis.yml that runs eslint. There are other style things that might be nice to enforce with ESLint/TSLint, like using const, import order, etc, but this commit just focuses on prettier, which gets most of the way there. One annoying issue for now is that typescript-eslint-parser gives a big warning message since they haven't updated to officially support TypeScript 2.8 yet. We aren't even using any ESLint rules that need the parser, but if we don't include it, ESLint will crash. TS2.8 support is hopefully coming really soon, though: https://github.com/eslint/typescript-eslint-parser/pull/454 As for the prettier config specifically, see https://prettier.io/docs/en/options.html for the available options. Config settings that seem non-controversial: Semicolons: You don't use semicolons. (I prefer semicolons, but either way is fine.) Quote style: Looks like you consistently use single quotes outside JSX and double quotes in JSX, which is the `singleQuote: true` option. Config settings worth discussion: Line width: You don't have a specific max. I put 100 since I think it's a good number for people (like both of us, probably) who find 80 a bit cramped. (At Benchling we use 110.) Prettier has a big red warning box recommending 80, but I still prefer 100ish. Bracket spacing: This is `{foo}` vs `{ foo }` for imports, exports, object literals, and destructuring. Looks like you're inconsistent but lean toward spaces. I personally really dislike bracket spacing (it feels inconsistent with arrays and function calls), but I'm certainly fine with it and Prettier has it enabled by default, so I kept it enabled. Trailing comma style: Options are "no trailing commas", "trailing commas for everything exception function calls and parameter lists", and "trailing commas everywhere". TypeScript can handle trailing commas everywhere, so there isn't a concern with tooling. You're inconsistent, and it looks like you tend to not have trailing commas, but I think it's probably best to just have them everywhere, so I enabled them. JSX Brackets: You're inconsistent about this, I think. I'd prefer to just keep the default and wrap the `>` to the next line. Arrow function parens: I only found two cases of arrow functions with one param (both `c => c.frame === frame`), and both omitted the parens, so I kept the default of omitting parens. This makes it mildly more annoying to add a typescript type or additional param, which is a possible reason for always requiring parens. Everything else is non-configurable, although it's possible some places would be better with a `// prettier-ignore` comment (but I usually try to avoid those). --- .eslintrc.js | 13 + .gitignore | 1 + .travis.yml | 3 + application.tsx | 293 ++++++++----- canvas-context.ts | 56 ++- color.ts | 39 +- demangle-cpp.ts | 20 +- flamechart-color-pass-renderer.ts | 43 +- flamechart-minimap-view.tsx | 95 ++-- flamechart-renderer.ts | 140 +++--- flamechart-style.ts | 16 +- flamechart-view.tsx | 203 +++++---- flamechart.ts | 26 +- import/bg-flamegraph.ts | 10 +- import/chrome.ts | 64 +-- import/stackprof.ts | 12 +- lru-cache.ts | 28 +- math.ts | 203 +++++---- overlay-rectangle-renderer.ts | 27 +- package-lock.json | 703 ++++++++++++++++++++++++++++++ package.json | 11 +- prettier.config.js | 6 + profile.ts | 56 ++- rectangle-batch-renderer.ts | 54 +-- regl.d.ts | 315 +++++++++---- reloadable.tsx | 10 +- speedscope.tsx | 8 +- stats.ts | 101 +++-- style.ts | 10 +- texture-catched-renderer.ts | 71 ++- utils.ts | 8 +- 31 files changed, 1908 insertions(+), 737 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .travis.yml create mode 100644 prettier.config.js 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 +}