feat(post-processing): add some post processing effects

add noise, sepia, halftone and ink effects
This commit is contained in:
yuqi.pyq 2019-10-30 15:21:13 +08:00
parent f37587c6f5
commit 585ea28d74
51 changed files with 1634 additions and 23447 deletions

View File

@ -22,6 +22,8 @@ module.exports = (api) => {
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator',
[
'@babel/plugin-proposal-decorators',
{
@ -35,7 +37,6 @@ module.exports = (api) => {
loose: true,
}
],
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-modules-commonjs',
[

View File

@ -50,13 +50,13 @@ ClearPass -> RenderPass -> [ ...其他后处理 Pass ] -> CopyPass
| -------- | --- | ------------- | --------- |
| ClearPass | normal | 无 | 清除 framebufferclearColor 为 [0, 0, 0, 0] |
| RenderPass | normal | 无 | 渲染到 framebuffer作为后续后处理的输入 |
| PickingPass | normal | 无 | 负责拾取,[详见](./PixelPickingEngine.md) |
| TAAPass | normal | 无 | [详见](./TAA.md) |
| CopyPass | post-processing | 无 | 作为后处理最后一个 Pass负责拷贝 framebuffer 到屏幕输出 |
| BlurHPass | post-processing | `blurRadius` 水平方向模糊半径,默认值为 `8.0` | [高斯模糊 blur9](https://github.com/Jam3/glsl-fast-gaussian-blur/blob/master/9.glsl) |
| BlurVPass | post-processing | `blurRadius` 垂直方向模糊半径,默认值为 `8.0` | 同上 |
剩余后处理效果见最后一节。
后续待实现 Pass 如下:
- [ ] PickingPass 负责拾取
- [ ] ShadowPass 负责生成 shadowMap供 PCF、CSM 等实时阴影技术使用
## 使用方法
@ -76,3 +76,92 @@ const layer = new PolygonLayer({
],
});
```
## 内置后处理效果
参考了 [glfx](https://github.com/evanw/glfx.js) 中的一些常用图像处理效果。可以按照名称引用,顺序决定了各个效果的应用次序。例如我们想依次应用噪声和模糊效果:
```typescript
const layer = new PolygonLayer({
passes: [
[
'noise', // 使用 NoisePass
{
amount: 0.5,
},
]
'blurH', // 使用 BlurHPass
'blurV', // 使用 BlurVPass
],
});
```
下面详细介绍各个后处理效果及其参数,在 DEMO 中也可以通过 GUI 任意调节参数。
### 高斯模糊
采用 [高斯模糊 blur9](https://github.com/Jam3/glsl-fast-gaussian-blur/blob/master/9.glsl)。
名称:`blurH/blurV`
参数:
* `blurRadius` 水平/垂直方向模糊半径,默认值为 `8.0`
效果如下:
![](./screenshots/blurpass.png)
### ColorHalftone
CMYK halftone 效果
名称:`colorHalftone`
参数:
* `angle` pattern 旋转角度,默认值为 0
* `size` pattern 大小,默认值为 8
* `center` `[x, y]` pattern 的中心,默认值为 `[0, 0]`
效果如下:
![](./screenshots/halftone.png)
### 噪声
噪声效果。
名称:`noise`
参数:
* `amount` 噪声程度,范围 `[0, 1]`,默认值为 `0.5`
效果如下:
![](./screenshots/noise.png)
### 六边形像素化处理
六边形像素化处理。
名称:`hexagonalPixelate`
参数:
* `scale` 六边形大小,默认值为 `10`
* `center` `[x, y]` pattern 的中心,默认值为 `[0.5, 0.5]`
效果如下:
![](./screenshots/hexagonalPixelate.png)
### Sepia
Sepia 颜色映射。
名称:`sepia`
参数:
* `amount` 程度,范围 `[0, 1]`,默认值为 `0.5`
效果如下:
![](./screenshots/sepia.png)

View File

@ -65,7 +65,7 @@ this.cameraService.jitterProjectionMatrix(
![](./screenshots/taa-step3.png)
这里我们选择当前帧权重为 0.9,历史帧为 0.1,最终的混合结果供后续后处理模块继续处理
这里我们选择当前帧权重为 0.9,历史帧为 0.1
```typescript
useFramebuffer(this.outputRenderTarget, () => {
@ -83,6 +83,23 @@ useFramebuffer(this.outputRenderTarget, () => {
});
```
最后我们将最终的混合结果“拷贝”给后处理模块,实现渐进增强的效果:
```typescript
useFramebuffer(
layer.multiPassRenderer.getPostProcessor().getReadFBO(),
() => {
this.copyModel.draw({
uniforms: {
u_Texture: this.copyRenderTarget,
},
});
},
);
// 调用后处理模块应用后续效果
layer.multiPassRenderer.getPostProcessor().render(layer);
```
## 最终效果
为了更直观地看到效果,在 DEMO 中我们可以调节相机抖动范围:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 KiB

23219
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
"devDependencies": {
"@babel/cli": "^7.6.4",
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
"@babel/plugin-proposal-optional-chaining": "^7.6.0",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
@ -109,8 +111,5 @@
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.6.0"
}
}

View File

@ -2,9 +2,6 @@ import container, { lazyInject, lazyMultiInject } from './inversify.config';
import ClearPass from './services/renderer/passes/ClearPass';
import MultiPassRenderer from './services/renderer/passes/MultiPassRenderer';
import PixelPickingPass from './services/renderer/passes/PixelPickingPass';
import BlurHPass from './services/renderer/passes/post-processing/BlurHPass';
import BlurVPass from './services/renderer/passes/post-processing/BlurVPass';
import CopyPass from './services/renderer/passes/post-processing/CopyPass';
import RenderPass from './services/renderer/passes/RenderPass';
import TAAPass from './services/renderer/passes/TAAPass';
import { TYPES } from './types';
@ -30,9 +27,6 @@ export {
ClearPass,
RenderPass,
PixelPickingPass,
BlurHPass,
BlurVPass,
CopyPass,
TAAPass,
};

View File

@ -32,6 +32,18 @@ import StyleAttributeService from './services/layer/StyleAttributeService';
import LogService from './services/log/LogService';
import SceneService from './services/scene/SceneService';
import ShaderModuleService from './services/shader/ShaderModuleService';
/** PostProcessing passes */
import { IPostProcessingPass } from './services/renderer/IMultiPassRenderer';
import BlurHPass from './services/renderer/passes/post-processing/BlurHPass';
import BlurVPass from './services/renderer/passes/post-processing/BlurVPass';
import ColorHalfTonePass from './services/renderer/passes/post-processing/ColorHalfTonePass';
import CopyPass from './services/renderer/passes/post-processing/CopyPass';
import HexagonalPixelatePass from './services/renderer/passes/post-processing/HexagonalPixelatePass';
import InkPass from './services/renderer/passes/post-processing/InkPass';
import NoisePass from './services/renderer/passes/post-processing/NoisePass';
import SepiaPass from './services/renderer/passes/post-processing/SepiaPass';
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md#defaultscope
const container = new Container();
@ -140,4 +152,51 @@ export const lazyMultiInject = (
};
};
// 绑定 post processing passes
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(CopyPass)
.whenTargetNamed('copy');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(BlurHPass)
.whenTargetNamed('blurH');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(BlurVPass)
.whenTargetNamed('blurV');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(NoisePass)
.whenTargetNamed('noise');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(SepiaPass)
.whenTargetNamed('sepia');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(ColorHalfTonePass)
.whenTargetNamed('colorHalftone');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(HexagonalPixelatePass)
.whenTargetNamed('hexagonalPixelate');
container
.bind<IPostProcessingPass<unknown>>(TYPES.INewablePostProcessingPass)
.to(InkPass)
.whenTargetNamed('ink');
container
.bind<interfaces.Factory<IPostProcessingPass<unknown>>>(
TYPES.IFactoryPostProcessingPass,
)
.toFactory<IPostProcessingPass<unknown>>((context) => {
return (named: string) => {
return context.container.getNamed<IPostProcessingPass<unknown>>(
TYPES.INewablePostProcessingPass,
named,
);
};
});
export default container;

View File

@ -4,7 +4,6 @@ import {
StyleScaleType,
} from '../layer/IStyleAttributeService';
import { IAttribute } from '../renderer/IAttribute';
import { IBuffer } from '../renderer/IBuffer';
import {
AttributeType,
IEncodeFeature,
@ -45,8 +44,6 @@ export default class StyleAttribute implements IStyleAttribute {
};
public vertexAttribute: IAttribute;
private buffer: IBuffer;
constructor(options: Partial<IStyleAttributeInitializationOptions>) {
this.setProps(options);
}

View File

@ -48,8 +48,7 @@ export default class StyleAttributeService implements IStyleAttributeService {
public registerStyleAttribute(
options: Partial<IStyleAttributeInitializationOptions>,
) {
let attributeToUpdate =
options.name && this.getLayerStyleAttribute(options.name);
let attributeToUpdate = this.getLayerStyleAttribute(options.name || '');
if (attributeToUpdate) {
attributeToUpdate.setProps(options);
} else {
@ -72,7 +71,7 @@ export default class StyleAttributeService implements IStyleAttributeService {
});
}
const { scale } = options;
if (scale) {
if (scale && attributeToUpdate) {
// TODO: 需要比较新旧值确定是否需要 rescale
// 需要重新 scale肯定也需要重新进行数据映射
attributeToUpdate.scale = scale;
@ -278,7 +277,9 @@ export default class StyleAttributeService implements IStyleAttributeService {
public clearAllAttributes() {
// 销毁关联的 vertex attribute buffer objects
this.attributes.forEach((attribute) => {
if (attribute.vertexAttribute) {
attribute.vertexAttribute.destroy();
}
});
this.attributes = [];
}

View File

@ -12,9 +12,10 @@ export enum PassType {
* 2. PostProcessing eg. CopyPassBlurPass
* Pass render
*/
export interface IPass {
export interface IPass<InitializationOptions> {
getName(): string;
getType(): PassType;
init(layer: ILayer): void;
init(layer: ILayer, config?: Partial<InitializationOptions>): void;
render(layer: ILayer): void;
}
@ -22,24 +23,33 @@ export interface IPass {
* PostProcessing renderTarget
* PostProcessingPass renderTarget
*/
export interface IPostProcessingPass extends IPass {
export interface IPostProcessingPass<InitializationOptions>
extends IPass<InitializationOptions> {
setRenderToScreen(renderToScreen: boolean): void;
isEnabled(): boolean;
setEnabled(enabled: boolean): void;
updateOptions(config: Partial<InitializationOptions>): void;
}
export interface IPostProcessor {
getReadFBO(): IFramebuffer;
getWriteFBO(): IFramebuffer;
resize(viewportWidth: number, viewportHeight: number): void;
add(pass: IPostProcessingPass, layer: ILayer): void;
add<InitializationOptions>(
pass: IPostProcessingPass<InitializationOptions>,
layer: ILayer,
config?: Partial<InitializationOptions>,
): void;
render(layer: ILayer): Promise<unknown>;
getPostProcessingPassByName(
name: string,
): IPostProcessingPass<unknown> | undefined;
}
export interface IMultiPassRenderer {
getPostProcessor(): IPostProcessor;
resize(viewportWidth: number, viewportHeight: number): void;
add(pass: IPass): void;
add<InitializationOptions>(pass: IPass<InitializationOptions>): void;
render(): void;
getRenderFlag(): boolean;
setRenderFlag(enabled: boolean): void;

View File

@ -15,7 +15,7 @@ export interface IRenderConfig {
* multi pass
*/
enableMultiPassRenderer?: boolean;
passes?: IPass[];
passes?: Array<IPass<unknown>>;
}
export interface IClearOptions {

View File

@ -4,7 +4,6 @@ import {
IModel,
IRendererService,
IShaderModuleService,
lazyInject,
} from '../../../index';
import { TYPES } from '../../../types';
import { ILayer } from '../../layer/ILayerService';
@ -18,11 +17,11 @@ import { IUniform } from '../IUniform';
*/
@injectable()
export default class BasePostProcessingPass<InitializationOptions = {}>
implements IPostProcessingPass {
@lazyInject(TYPES.IShaderModuleService)
implements IPostProcessingPass<InitializationOptions> {
@inject(TYPES.IShaderModuleService)
protected readonly shaderModule: IShaderModuleService;
@lazyInject(TYPES.IRendererService)
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
protected config: Partial<InitializationOptions> | undefined;
@ -42,15 +41,19 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
*/
private model: IModel;
constructor(config?: Partial<InitializationOptions>) {
this.config = config;
private optionsToUpdate: Partial<InitializationOptions> = {};
public getName() {
return '';
}
public getType() {
return PassType.PostProcessing;
}
public init() {
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
this.config = config;
const { createAttribute, createBuffer, createModel } = this.rendererService;
const { vs, fs, uniforms } = this.setupShaders();
@ -71,6 +74,7 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
uniforms: {
u_Texture: null,
...uniforms,
...(this.config && this.convertOptionsToUniforms(this.config)),
},
depth: {
enable: false,
@ -81,7 +85,8 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
public render(layer: ILayer) {
const postProcessor = layer.multiPassRenderer.getPostProcessor();
const { useFramebuffer } = this.rendererService;
const { useFramebuffer, getViewportSize } = this.rendererService;
const { width, height } = getViewportSize();
useFramebuffer(
this.renderToScreen ? null : postProcessor.getWriteFBO(),
@ -89,6 +94,8 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
this.model.draw({
uniforms: {
u_Texture: postProcessor.getReadFBO(),
u_ViewportSize: [width, height],
...this.convertOptionsToUniforms(this.optionsToUpdate),
},
});
},
@ -107,6 +114,13 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
this.renderToScreen = renderToScreen;
}
public updateOptions(config: Partial<InitializationOptions>) {
this.optionsToUpdate = {
...this.optionsToUpdate,
...config,
};
}
protected setupShaders(): {
vs: string;
fs: string;
@ -114,4 +128,12 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
} {
throw new Error('Method not implemented.');
}
protected convertOptionsToUniforms(
options: Partial<InitializationOptions>,
): {
[uniformName: string]: IUniform;
} | void {
throw new Error('Method not implemented.');
}
}

View File

@ -8,7 +8,8 @@ import { IRendererService } from '../IRendererService';
* ClearPass
*/
@injectable()
export default class ClearPass implements IPass {
export default class ClearPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
@ -16,6 +17,10 @@ export default class ClearPass implements IPass {
return PassType.Normal;
}
public getName() {
return 'clear';
}
public init() {
//
}

View File

@ -30,7 +30,7 @@ import PostProcessor from './PostProcessor';
*/
@injectable()
export default class MultiPassRenderer implements IMultiPassRenderer {
private passes: IPass[] = [];
private passes: Array<IPass<unknown>> = [];
private postProcessor: IPostProcessor;
private layer: ILayer;
@ -64,17 +64,21 @@ export default class MultiPassRenderer implements IMultiPassRenderer {
this.postProcessor.resize(width, height);
}
public add(pass: IPass) {
public add<T>(pass: IPass<T>, config?: Partial<T>) {
if (pass.getType() === PassType.PostProcessing) {
this.postProcessor.add(pass as IPostProcessingPass, this.layer);
this.postProcessor.add<T>(
pass as IPostProcessingPass<T>,
this.layer,
config,
);
} else {
pass.init(this.layer);
pass.init(this.layer, config);
this.passes.push(pass);
}
}
public insert(pass: IPass, index: number) {
pass.init(this.layer);
public insert<T>(pass: IPass<T>, config: Partial<T>, index: number) {
pass.init(this.layer, config);
this.passes.splice(index, 0, pass);
}
}

View File

@ -24,7 +24,7 @@ function decodePickingColor(color: Uint8Array): number {
* @see https://github.com/antvis/L7/blob/next/dev-docs/PixelPickingEngine.md
*/
@injectable()
export default class PixelPickingPass implements IPass {
export default class PixelPickingPass<InitializationOptions = {}> implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
@ -53,6 +53,10 @@ export default class PixelPickingPass implements IPass {
return PassType.Normal;
}
public getName() {
return 'pixelPicking';
}
public init(layer: ILayer) {
this.layer = layer;
const { createTexture2D, createFramebuffer } = this.rendererService;

View File

@ -16,7 +16,7 @@ export default class PostProcessor implements IPostProcessor {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
private passes: IPostProcessingPass[] = [];
private passes: Array<IPostProcessingPass<unknown>> = [];
private readFBO: IFramebuffer;
private writeFBO: IFramebuffer;
@ -75,16 +75,31 @@ export default class PostProcessor implements IPostProcessor {
});
}
public add(pass: IPostProcessingPass, layer: ILayer) {
pass.init(layer);
public add<T>(
pass: IPostProcessingPass<T>,
layer: ILayer,
config?: Partial<T>,
) {
pass.init(layer, config);
this.passes.push(pass);
}
public insert(pass: IPostProcessingPass, index: number, layer: ILayer) {
pass.init(layer);
public insert<T>(
pass: IPostProcessingPass<T>,
index: number,
layer: ILayer,
config?: Partial<T>,
) {
pass.init(layer, config);
this.passes.splice(index, 0, pass);
}
public getPostProcessingPassByName(
name: string,
): IPostProcessingPass<unknown> | undefined {
return this.passes.find((p) => p.getName() === name);
}
private isLastEnabledPass(index: number): boolean {
for (let i = index + 1; i < this.passes.length; i++) {
if (this.passes[i].isEnabled()) {

View File

@ -9,7 +9,8 @@ import { IRendererService } from '../IRendererService';
* RenderPass PostProcessor readFBO
*/
@injectable()
export default class RenderPass implements IPass {
export default class RenderPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
@ -17,6 +18,10 @@ export default class RenderPass implements IPass {
return PassType.Normal;
}
public getName() {
return 'render';
}
public init(layer: ILayer) {
//
}

View File

@ -38,7 +38,8 @@ let accumulatingId = 1;
* @see https://yuque.antfin-inc.com/yuqi.pyq/fgetpa/ri52hv
*/
@injectable()
export default class TAAPass implements IPass {
export default class TAAPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
@ -84,6 +85,10 @@ export default class TAAPass implements IPass {
return PassType.Normal;
}
public getName() {
return 'taa';
}
public init(layer: ILayer) {
const { createFramebuffer, createTexture2D } = this.rendererService;
this.sampleRenderTarget = createFramebuffer({
@ -258,13 +263,17 @@ export default class TAAPass implements IPass {
});
});
useFramebuffer(null, () => {
useFramebuffer(
layer.multiPassRenderer.getPostProcessor().getReadFBO(),
() => {
this.copyModel.draw({
uniforms: {
u_Texture: this.copyRenderTarget,
},
});
});
},
);
layer.multiPassRenderer.getPostProcessor().render(layer);
}
// 保存前序帧结果

View File

@ -1,27 +1,28 @@
import { injectable } from 'inversify';
import { lazyInject } from '../../../../index';
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import blur from '../../../../shaders/post-processing/blur.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IBlurHPassConfig {
blurRadius: number;
}
const defaultConfig: IBlurHPassConfig = {
blurRadius: 8.0,
};
@injectable()
export default class BlurHPass extends BasePostProcessingPass<
IBlurHPassConfig
> {
@lazyInject(TYPES.IRendererService)
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public setupShaders() {
public getName() {
return 'blurH';
}
protected setupShaders() {
this.shaderModule.registerModule('blur-pass', {
vs: quad,
fs: blur,
@ -30,19 +31,29 @@ export default class BlurHPass extends BasePostProcessingPass<
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
const { width, height } = this.rendererService.getViewportSize();
const { blurRadius } = {
...defaultConfig,
...this.config,
};
return {
vs,
fs,
uniforms: {
...uniforms,
u_BlurDir: [blurRadius, 0],
u_ViewportSize: [width, height],
},
};
}
protected convertOptionsToUniforms(
options: Partial<IBlurHPassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.blurRadius)) {
uniforms.u_BlurDir = [options.blurRadius, 0];
}
return uniforms;
}
}

View File

@ -1,26 +1,27 @@
import { injectable } from 'inversify';
import { lazyInject } from '../../../../index';
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import blur from '../../../../shaders/post-processing/blur.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IBlurVPassConfig {
blurRadius: number;
}
const defaultConfig: IBlurVPassConfig = {
blurRadius: 8.0,
};
@injectable()
export default class BlurVPass extends BasePostProcessingPass<
IBlurVPassConfig
> {
@lazyInject(TYPES.IRendererService)
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'blurV';
}
public setupShaders() {
this.shaderModule.registerModule('blur-pass', {
vs: quad,
@ -30,19 +31,29 @@ export default class BlurVPass extends BasePostProcessingPass<
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
const { width, height } = this.rendererService.getViewportSize();
const { blurRadius } = {
...defaultConfig,
...this.config,
};
return {
vs,
fs,
uniforms: {
...uniforms,
u_BlurDir: [0, blurRadius],
u_ViewportSize: [width, height],
},
};
}
protected convertOptionsToUniforms(
options: Partial<IBlurVPassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.blurRadius)) {
uniforms.u_BlurDir = [0, options.blurRadius];
}
return uniforms;
}
}

View File

@ -0,0 +1,71 @@
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import colorHalftone from '../../../../shaders/post-processing/colorhalftone.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IColorHalftonePassConfig {
center: [number, number];
angle: number;
size: number;
}
@injectable()
export default class ColorHalftonePass extends BasePostProcessingPass<
IColorHalftonePassConfig
> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'colorHalftone';
}
protected setupShaders() {
this.shaderModule.registerModule('colorhalftone-pass', {
vs: quad,
fs: colorHalftone,
});
const { vs, fs, uniforms } = this.shaderModule.getModule(
'colorhalftone-pass',
);
const { width, height } = this.rendererService.getViewportSize();
return {
vs,
fs,
uniforms: {
...uniforms,
u_ViewportSize: [width, height],
},
};
}
protected convertOptionsToUniforms(
options: Partial<IColorHalftonePassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.center)) {
uniforms.u_Center = options.center;
}
if (!isNil(options.angle)) {
uniforms.u_Angle = options.angle;
}
if (!isNil(options.size)) {
uniforms.u_Size = options.size;
}
return uniforms;
}
}

View File

@ -1,10 +1,15 @@
import { injectable } from 'inversify';
import copy from '../../../../shaders/post-processing/copy.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
@injectable()
export default class CopyPass extends BasePostProcessingPass {
public getName() {
return 'copy';
}
public setupShaders() {
this.shaderModule.registerModule('copy-pass', {
vs: quad,
@ -13,4 +18,12 @@ export default class CopyPass extends BasePostProcessingPass {
return this.shaderModule.getModule('copy-pass');
}
protected convertOptionsToUniforms(
options: Partial<{}>,
): {
[uniformName: string]: IUniform;
} | void {
return {};
}
}

View File

@ -0,0 +1,66 @@
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import hexagonalPixelate from '../../../../shaders/post-processing/hexagonalpixelate.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IHexagonalPixelatePassConfig {
center: [number, number];
scale: number;
}
@injectable()
export default class HexagonalPixelatePass extends BasePostProcessingPass<
IHexagonalPixelatePassConfig
> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'hexagonalPixelate';
}
protected setupShaders() {
this.shaderModule.registerModule('hexagonalpixelate-pass', {
vs: quad,
fs: hexagonalPixelate,
});
const { vs, fs, uniforms } = this.shaderModule.getModule(
'hexagonalpixelate-pass',
);
const { width, height } = this.rendererService.getViewportSize();
return {
vs,
fs,
uniforms: {
...uniforms,
u_ViewportSize: [width, height],
},
};
}
protected convertOptionsToUniforms(
options: Partial<IHexagonalPixelatePassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.center)) {
uniforms.u_Center = options.center;
}
if (!isNil(options.scale)) {
uniforms.u_Scale = options.scale;
}
return uniforms;
}
}

View File

@ -0,0 +1,57 @@
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import ink from '../../../../shaders/post-processing/ink.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IInkPassConfig {
strength: number;
}
@injectable()
export default class InkPass extends BasePostProcessingPass<IInkPassConfig> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'ink';
}
protected setupShaders() {
this.shaderModule.registerModule('ink-pass', {
vs: quad,
fs: ink,
});
const { vs, fs, uniforms } = this.shaderModule.getModule('ink-pass');
const { width, height } = this.rendererService.getViewportSize();
return {
vs,
fs,
uniforms: {
...uniforms,
u_ViewportSize: [width, height],
},
};
}
protected convertOptionsToUniforms(
options: Partial<IInkPassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.strength)) {
uniforms.u_Strength = options.strength;
}
return uniforms;
}
}

View File

@ -0,0 +1,44 @@
import { injectable } from 'inversify';
import { isNil } from 'lodash';
import noise from '../../../../shaders/post-processing/noise.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface INoisePassConfig {
amount: number;
}
@injectable()
export default class NoisePass extends BasePostProcessingPass<
INoisePassConfig
> {
public getName() {
return 'noise';
}
public setupShaders() {
this.shaderModule.registerModule('noise-pass', {
vs: quad,
fs: noise,
});
return this.shaderModule.getModule('noise-pass');
}
protected convertOptionsToUniforms(
options: Partial<INoisePassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.amount)) {
uniforms.u_Amount = options.amount;
}
return uniforms;
}
}

View File

@ -0,0 +1,44 @@
import { injectable } from 'inversify';
import { isNil } from 'lodash';
import quad from '../../../../shaders/post-processing/quad.glsl';
import sepia from '../../../../shaders/post-processing/sepia.glsl';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface ISepiaPassConfig {
amount: number;
}
@injectable()
export default class SepiaPass extends BasePostProcessingPass<
ISepiaPassConfig
> {
public getName() {
return 'sepia';
}
public setupShaders() {
this.shaderModule.registerModule('sepia-pass', {
vs: quad,
fs: sepia,
});
return this.shaderModule.getModule('sepia-pass');
}
protected convertOptionsToUniforms(
options: Partial<ISepiaPassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.amount)) {
uniforms.u_Amount = options.amount;
}
return uniforms;
}
}

View File

@ -3,6 +3,7 @@ import { uniq } from 'lodash';
import { extractUniforms } from '../../utils/shader-module';
import { IModuleParams, IShaderModuleService } from './IShaderModuleService';
import common from '../../shaders/common.glsl';
import decode from '../../shaders/decode.glsl';
import lighting from '../../shaders/lighting.glsl';
import pickingFrag from '../../shaders/picking.frag.glsl';
@ -21,6 +22,7 @@ export default class ShaderModuleService implements IShaderModuleService {
private rawContentCache: { [key: string]: IModuleParams } = {};
public registerBuiltinModules() {
this.registerModule('common', { vs: common, fs: common });
this.registerModule('decode', { vs: decode, fs: '' });
this.registerModule('projection', { vs: projection, fs: '' });
this.registerModule('sdf_2d', { vs: '', fs: sdf2d });

View File

@ -0,0 +1 @@
#define PI 3.14159265359

View File

@ -0,0 +1,44 @@
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform vec2 u_ViewportSize: [1.0, 1.0];
uniform vec2 u_Center : [0.5, 0.5];
uniform float u_Angle : 0;
uniform float u_Size : 8;
#pragma include "common"
float scale = PI / u_Size;
float pattern(float u_Angle, vec2 texSize, vec2 texCoord) {
float s = sin(u_Angle), c = cos(u_Angle);
vec2 tex = texCoord * texSize - u_Center * texSize;
vec2 point = vec2(
c * tex.x - s * tex.y,
s * tex.x + c * tex.y
) * scale;
return (sin(point.x) * sin(point.y)) * 4.0;
}
// https://github.com/evanw/glfx.js/blob/master/src/filters/fun/colorhalftone.js
vec4 colorHalftone_filterColor(vec4 color, vec2 texSize, vec2 texCoord) {
vec3 cmy = 1.0 - color.rgb;
float k = min(cmy.x, min(cmy.y, cmy.z));
cmy = (cmy - k) / (1.0 - k);
cmy = clamp(
cmy * 10.0 - 3.0 + vec3(
pattern(u_Angle + 0.26179, texSize, texCoord),
pattern(u_Angle + 1.30899, texSize, texCoord),
pattern(u_Angle, texSize, texCoord)
),
0.0,
1.0
);
k = clamp(k * 10.0 - 5.0 + pattern(u_Angle + 0.78539, texSize, texCoord), 0.0, 1.0);
return vec4(1.0 - cmy - k, color.a);
}
void main() {
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
gl_FragColor = colorHalftone_filterColor(gl_FragColor, u_ViewportSize, v_UV);
}

View File

@ -0,0 +1,44 @@
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform vec2 u_ViewportSize: [1.0, 1.0];
uniform vec2 u_Center : [0.5, 0.5];
uniform float u_Scale : 10;
// https://github.com/evanw/glfx.js/blob/master/src/filters/fun/hexagonalpixelate.js
vec4 hexagonalPixelate_sampleColor(sampler2D texture, vec2 texSize, vec2 texCoord) {
vec2 tex = (texCoord * texSize - u_Center * texSize) / u_Scale;
tex.y /= 0.866025404;
tex.x -= tex.y * 0.5;
vec2 a;
if (tex.x + tex.y - floor(tex.x) - floor(tex.y) < 1.0) {
a = vec2(floor(tex.x), floor(tex.y));
}
else a = vec2(ceil(tex.x), ceil(tex.y));
vec2 b = vec2(ceil(tex.x), floor(tex.y));
vec2 c = vec2(floor(tex.x), ceil(tex.y));
vec3 TEX = vec3(tex.x, tex.y, 1.0 - tex.x - tex.y);
vec3 A = vec3(a.x, a.y, 1.0 - a.x - a.y);
vec3 B = vec3(b.x, b.y, 1.0 - b.x - b.y);
vec3 C = vec3(c.x, c.y, 1.0 - c.x - c.y);
float alen = length(TEX - A);
float blen = length(TEX - B);
float clen = length(TEX - C);
vec2 choice;
if (alen < blen) {
if (alen < clen) choice = a;
else choice = c;
} else {
if (blen < clen) choice = b;
else choice = c;
}
choice.x += choice.y * 0.5;
choice.y *= 0.866025404;
choice *= u_Scale / texSize;
return texture2D(texture, choice + u_Center);
}
void main() {
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
gl_FragColor = hexagonalPixelate_sampleColor(u_Texture, u_ViewportSize, v_UV);
}

View File

@ -0,0 +1,34 @@
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform vec2 u_ViewportSize: [1.0, 1.0];
uniform float u_Strength : 0.6;
vec4 ink_sampleColor(sampler2D texture, vec2 texSize, vec2 texCoord) {
vec2 dx = vec2(1.0 / texSize.x, 0.0);
vec2 dy = vec2(0.0, 1.0 / texSize.y);
vec4 color = texture2D(texture, texCoord);
float bigTotal = 0.0;
float smallTotal = 0.0;
vec3 bigAverage = vec3(0.0);
vec3 smallAverage = vec3(0.0);
for (float x = -2.0; x <= 2.0; x += 1.0) {
for (float y = -2.0; y <= 2.0; y += 1.0) {
vec3 sample = texture2D(texture, texCoord + dx * x + dy * y).rgb;
bigAverage += sample;
bigTotal += 1.0;
if (abs(x) + abs(y) < 2.0) {
smallAverage += sample;
smallTotal += 1.0;
}
}
}
vec3 edge = max(vec3(0.0), bigAverage / bigTotal - smallAverage / smallTotal);
float power = u_Strength * u_Strength * u_Strength * u_Strength * u_Strength;
return vec4(color.rgb - dot(edge, edge) * power * 100000.0, color.a);
}
void main() {
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
gl_FragColor = ink_sampleColor(u_Texture, u_ViewportSize, v_UV);
}

View File

@ -0,0 +1,22 @@
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform float u_Amount : 0.5;
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
// https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/noise.js
vec4 noise_filterColor(vec4 color, vec2 texCoord) {
float diff = (rand(texCoord) - 0.5) * u_Amount;
color.r += diff;
color.g += diff;
color.b += diff;
return color;
}
void main() {
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
gl_FragColor = noise_filterColor(gl_FragColor, v_UV);
}

View File

@ -0,0 +1,22 @@
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform float u_Amount : 0.5;
// https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/sepia.js
vec4 sepia_filterColor(vec4 color) {
float r = color.r;
float g = color.g;
float b = color.b;
color.r =
min(1.0, (r * (1.0 - (0.607 * u_Amount))) + (g * (0.769 * u_Amount)) + (b * (0.189 * u_Amount)));
color.g = min(1.0, (r * 0.349 * u_Amount) + (g * (1.0 - (0.314 * u_Amount))) + (b * 0.168 * u_Amount));
color.b = min(1.0, (r * 0.272 * u_Amount) + (g * 0.534 * u_Amount) + (b * (1.0 - (0.869 * u_Amount))));
return color;
}
void main() {
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
gl_FragColor = sepia_filterColor(gl_FragColor);
}

View File

@ -8,6 +8,7 @@ const TYPES = {
ILayerStyleService: Symbol.for('ILayerStyleService'),
ILogService: Symbol.for('ILogService'),
IMapService: Symbol.for('IMapService'),
IFactoryMapService: Symbol.for('Factory<IMapService>'),
IRendererService: Symbol.for('IRendererService'),
IShaderModuleService: Symbol.for('IShaderModuleService'),
IIconService: Symbol.for('IIconService'),
@ -16,13 +17,8 @@ const TYPES = {
IControlService: Symbol.for('IControlService'),
IStyleAttributeService: Symbol.for('IStyleAttributeService'),
ILayerPlugin: Symbol.for('ILayerPlugin'),
/** multi-pass */
ClearPass: Symbol.for('ClearPass'),
RenderPass: Symbol.for('RenderPass'),
CopyPass: Symbol.for('CopyPass'),
BlurHPass: Symbol.for('BlurHPass'),
BlurVPass: Symbol.for('BlurVPass'),
INewablePostProcessingPass: Symbol.for('Newable<IPostProcessingPass>'),
IFactoryPostProcessingPass: Symbol.for('Factory<IPostProcessingPass>'),
};
export { TYPES };

View File

@ -28,7 +28,7 @@ import { isFunction } from 'lodash';
// @ts-ignore
import mergeJsonSchemas from 'merge-json-schemas';
import { SyncBailHook, SyncHook } from 'tapable';
import { normalizePasses } from '../plugins/MultiPassRendererPlugin';
import baseLayerSchema from './schema';
export interface ILayerModelInitializationOptions {
@ -230,11 +230,26 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
};
return this;
}
public style(options: object): ILayer {
// @ts-ignore
public style(options: object & Partial<ILayerInitializationOptions>): ILayer {
const { passes, ...rest } = options;
// passes 特殊处理
if (passes) {
normalizePasses(passes).forEach(
(pass: [string, { [key: string]: unknown }]) => {
const postProcessingPass = this.multiPassRenderer
.getPostProcessor()
.getPostProcessingPassByName(pass[0]);
if (postProcessingPass) {
postProcessingPass.updateOptions(pass[1]);
}
},
);
}
this.styleOptions = {
...this.styleOptions,
...(options as object),
...rest,
};
return this;
}

View File

@ -1,33 +1,22 @@
import {
BlurHPass,
BlurVPass,
ClearPass,
CopyPass,
IGlobalConfigService,
ILayer,
ILayerPlugin,
IPostProcessingPass,
IRendererService,
lazyInject,
MultiPassRenderer,
PixelPickingPass,
RenderPass,
TAAPass,
TYPES,
} from '@l7/core';
import { inject, injectable } from 'inversify';
const builtinPostProcessingPassMap: {
[key: string]: new (config?: { [key: string]: any }) => IPostProcessingPass;
} = {
blurH: BlurHPass,
blurV: BlurVPass,
};
import { inject, injectable, interfaces, multiInject } from 'inversify';
/**
* 'blurH' -> ['blurH', {}]
*/
function normalizePasses(
export function normalizePasses(
passes: Array<string | [string, { [key: string]: unknown }]>,
) {
return passes.map((pass: string | [string, { [key: string]: unknown }]) => {
@ -57,6 +46,11 @@ export default class MultiPassRendererPlugin implements ILayerPlugin {
@inject(TYPES.IRendererService)
private readonly rendererService: IRendererService;
@inject(TYPES.IFactoryPostProcessingPass)
private readonly postProcessingPassFactory: (
name: string,
) => IPostProcessingPass<unknown>;
private enabled: boolean;
public apply(layer: ILayer) {
@ -110,7 +104,7 @@ export default class MultiPassRendererPlugin implements ILayerPlugin {
multiPassRenderer.add(new PixelPickingPass());
}
// TAA pass if enabled
// use TAA pass if enabled instead of render pass
if (enableTAA) {
multiPassRenderer.add(new TAAPass());
} else {
@ -121,13 +115,16 @@ export default class MultiPassRendererPlugin implements ILayerPlugin {
// post processing
normalizePasses(passes).forEach(
(pass: [string, { [key: string]: unknown }]) => {
const PostProcessingPassClazz = builtinPostProcessingPassMap[pass[0]];
multiPassRenderer.add(new PostProcessingPassClazz(pass[1]));
const [passName, initializationOptions] = pass;
multiPassRenderer.add(
this.postProcessingPassFactory(passName),
initializationOptions,
);
},
);
// 末尾为固定的 CopyPass
multiPassRenderer.add(new CopyPass());
multiPassRenderer.add(this.postProcessingPassFactory('copy'));
return multiPassRenderer;
}

View File

@ -11,7 +11,7 @@ varying vec4 v_Color;
void main() {
v_Color = a_Color;
vec4 project_pos = project_position(vec4(a_Position 1.0));
vec4 project_pos = project_position(vec4(a_Position, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
setPickingColor(a_PickingColor);

View File

@ -21,6 +21,7 @@ import {
} from '@l7/core';
import { AMapService, MapboxService } from '@l7/maps';
import { ReglRendererService } from '@l7/renderer';
import { interfaces } from 'inversify';
import { Map } from 'mapbox-gl';
// 绑定渲染引擎服务
@ -28,6 +29,24 @@ container
.bind<IRendererService>(TYPES.IRendererService)
.to(ReglRendererService)
.inSingletonScope();
// // 绑定地图服务 AMap & Mapbox
// container
// .bind<IMapService>(TYPES.IMapService)
// .to(AMapService)
// .whenTargetNamed(MapType.amap)
// .inSingletonScope();
// container
// .bind<IMapService>(TYPES.IMapService)
// .to(MapboxService)
// .inSingletonScope();
// // 地图服务工厂,根据 name 返回指定服务
// container
// .bind<interfaces.Factory<IMapService>>(TYPES.IFactoryMapService)
// .toFactory<IMapService>((context) => {
// return (named: string) => {
// return context.container.getNamed<IMapService>(TYPES.IMapService, named);
// };
// });
// 缓存当前地图类型,便于 DEMO 中切换底图时动态绑定
let mapType: MapType;
@ -52,7 +71,6 @@ class Scene {
private iconService: IIconService;
// private mapType: MapType;
public constructor(config: IMapConfig & IRenderConfig) {
const { type = MapType.amap } = config;

View File

@ -1,8 +1,18 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import Blur from './components/Blur';
import ColorHalftone from './components/ColorHalftone';
import HexagonalPixelate from './components/HexagonalPixelate';
import Ink from './components/Ink';
import Noise from './components/Noise';
import Sepia from './components/Sepia';
import TAA from './components/TAA';
// @ts-ignore
storiesOf('MultiPassRenderer', module)
.add('ColorHalftone', () => <ColorHalftone />)
.add('HexagonalPixelate', () => <HexagonalPixelate />)
.add('Ink', () => <Ink />)
.add('Blur', () => <Blur />)
.add('Noise', () => <Noise />)
.add('Sepia', () => <Sepia />)
.add('TAA(Temporal Anti-Aliasing)', () => <TAA />);

View File

@ -37,7 +37,12 @@ export default class Mapbox extends React.Component {
enablePicking: true,
enableHighlight: true,
passes: [
[
'blurH',
{
blurRadius: 8,
},
],
[
'blurV',
{
@ -67,6 +72,46 @@ export default class Mapbox extends React.Component {
scene.render();
this.scene = scene;
/*** 运行时修改样式属性 ***/
const gui = new dat.GUI();
this.gui = gui;
const styleOptions = {
blurVRadius: 8,
blurHRadius: 8,
};
const pointFolder = gui.addFolder('Blur 配置');
pointFolder
.add(styleOptions, 'blurVRadius', 0, 100)
.onChange((blurRadius: number) => {
layer.style({
passes: [
[
'blurV',
{
blurRadius,
},
],
],
});
scene.render();
});
pointFolder
.add(styleOptions, 'blurHRadius', 0, 100)
.onChange((blurRadius: number) => {
layer.style({
passes: [
[
'blurH',
{
blurRadius,
},
],
],
});
scene.render();
});
pointFolder.open();
}
public render() {

View File

@ -0,0 +1,153 @@
// @ts-ignore
import { PolygonLayer } from '@l7/layers';
// @ts-ignore
import { Scene } from '@l7/scene';
import * as dat from 'dat.gui';
import * as React from 'react';
export default class ColorHalftone extends React.Component {
private gui: dat.GUI;
private $stats: Node;
private scene: Scene;
public componentWillUnmount() {
if (this.gui) {
this.gui.destroy();
}
if (this.$stats) {
document.body.removeChild(this.$stats);
}
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
);
const data = await response.json();
const scene = new Scene({
id: 'map',
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
center: [110.19382669582967, 50.258134],
pitch: 0,
zoom: 3,
});
const layer = new PolygonLayer({
enablePicking: true,
enableHighlight: true,
passes: [
[
'colorHalftone',
{
size: 8,
},
],
],
});
layer
.source(data)
.size('name', [0, 10000, 50000, 30000, 100000])
.color('name', [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
])
.shape('fill')
.style({
opacity: 0.8,
});
scene.addLayer(layer);
scene.render();
this.scene = scene;
/*** 运行时修改样式属性 ***/
const gui = new dat.GUI();
this.gui = gui;
const styleOptions = {
angle: 0,
size: 8,
centerX: 0.5,
centerY: 0.5,
};
const pointFolder = gui.addFolder('ColorHalftone 配置');
pointFolder
.add(styleOptions, 'centerX', 0, 1)
.onChange((centerX: number) => {
layer.style({
passes: [
[
'colorHalftone',
{
center: [centerX, styleOptions.centerY],
},
],
],
});
scene.render();
});
pointFolder
.add(styleOptions, 'centerY', 0, 1)
.onChange((centerY: number) => {
layer.style({
passes: [
[
'colorHalftone',
{
center: [styleOptions.centerX, centerY],
},
],
],
});
scene.render();
});
pointFolder.add(styleOptions, 'angle', 0, 10).onChange((angle: number) => {
layer.style({
passes: [
[
'colorHalftone',
{
angle,
},
],
],
});
scene.render();
});
pointFolder.add(styleOptions, 'size', 0, 20).onChange((size: number) => {
layer.style({
passes: [
[
'colorHalftone',
{
size,
},
],
],
});
scene.render();
});
pointFolder.open();
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -0,0 +1,139 @@
// @ts-ignore
import { PolygonLayer } from '@l7/layers';
// @ts-ignore
import { Scene } from '@l7/scene';
import * as dat from 'dat.gui';
import * as React from 'react';
export default class HexagonalPixelate extends React.Component {
private gui: dat.GUI;
private $stats: Node;
private scene: Scene;
public componentWillUnmount() {
if (this.gui) {
this.gui.destroy();
}
if (this.$stats) {
document.body.removeChild(this.$stats);
}
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
);
const data = await response.json();
const scene = new Scene({
id: 'map',
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
center: [110.19382669582967, 50.258134],
pitch: 0,
zoom: 3,
});
const layer = new PolygonLayer({
enablePicking: true,
enableHighlight: true,
passes: [
[
'hexagonalPixelate',
{
scale: 10,
},
],
],
});
layer
.source(data)
.size('name', [0, 10000, 50000, 30000, 100000])
.color('name', [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
])
.shape('fill')
.style({
opacity: 0.8,
});
scene.addLayer(layer);
scene.render();
this.scene = scene;
/*** 运行时修改样式属性 ***/
const gui = new dat.GUI();
this.gui = gui;
const styleOptions = {
scale: 10,
centerX: 0.5,
centerY: 0.5,
};
const pointFolder = gui.addFolder('HexagonalPixelate 配置');
pointFolder
.add(styleOptions, 'centerX', 0, 1)
.onChange((centerX: number) => {
layer.style({
passes: [
[
'hexagonalPixelate',
{
center: [centerX, styleOptions.centerY],
},
],
],
});
scene.render();
});
pointFolder
.add(styleOptions, 'centerY', 0, 1)
.onChange((centerY: number) => {
layer.style({
passes: [
[
'hexagonalPixelate',
{
center: [styleOptions.centerX, centerY],
},
],
],
});
scene.render();
});
pointFolder.add(styleOptions, 'scale', 0, 50).onChange((scale: number) => {
layer.style({
passes: [
[
'hexagonalPixelate',
{
scale,
},
],
],
});
scene.render();
});
pointFolder.open();
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -0,0 +1,109 @@
// @ts-ignore
import { PolygonLayer } from '@l7/layers';
// @ts-ignore
import { Scene } from '@l7/scene';
import * as dat from 'dat.gui';
import * as React from 'react';
export default class Ink extends React.Component {
private gui: dat.GUI;
private $stats: Node;
private scene: Scene;
public componentWillUnmount() {
if (this.gui) {
this.gui.destroy();
}
if (this.$stats) {
document.body.removeChild(this.$stats);
}
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
);
const data = await response.json();
const scene = new Scene({
id: 'map',
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
center: [110.19382669582967, 50.258134],
pitch: 0,
zoom: 3,
});
const layer = new PolygonLayer({
enablePicking: true,
enableHighlight: true,
passes: [
[
'ink',
{
strength: 0.6,
},
],
],
});
layer
.source(data)
.size('name', [0, 10000, 50000, 30000, 100000])
.color('name', [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
])
.shape('fill')
.style({
opacity: 0.8,
});
scene.addLayer(layer);
scene.render();
this.scene = scene;
/*** 运行时修改样式属性 ***/
const gui = new dat.GUI();
this.gui = gui;
const styleOptions = {
strength: 0.6,
};
const pointFolder = gui.addFolder('Ink 配置');
pointFolder
.add(styleOptions, 'strength', 0, 1)
.onChange((strength: number) => {
layer.style({
passes: [
[
'ink',
{
strength,
},
],
],
});
scene.render();
});
pointFolder.open();
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -0,0 +1,100 @@
// @ts-ignore
import { PolygonLayer } from '@l7/layers';
// @ts-ignore
import { Scene } from '@l7/scene';
import * as dat from 'dat.gui';
import * as React from 'react';
export default class Noise extends React.Component {
private gui: dat.GUI;
private $stats: Node;
private scene: Scene;
public componentWillUnmount() {
if (this.gui) {
this.gui.destroy();
}
if (this.$stats) {
document.body.removeChild(this.$stats);
}
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
);
const data = await response.json();
const scene = new Scene({
id: 'map',
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
center: [110.19382669582967, 50.258134],
pitch: 0,
zoom: 3,
});
const layer = new PolygonLayer({
enablePicking: true,
enableHighlight: true,
passes: ['noise'],
});
layer
.source(data)
.size('name', [0, 10000, 50000, 30000, 100000])
.color('name', [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
])
.shape('fill')
.style({
opacity: 0.8,
});
scene.addLayer(layer);
scene.render();
this.scene = scene;
/*** 运行时修改样式属性 ***/
const gui = new dat.GUI();
this.gui = gui;
const styleOptions = {
amount: 1,
};
const pointFolder = gui.addFolder('Noise 配置');
pointFolder.add(styleOptions, 'amount', 0, 1).onChange((amount: number) => {
layer.style({
passes: [
[
'noise',
{
amount,
},
],
],
});
scene.render();
});
pointFolder.open();
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -0,0 +1,100 @@
// @ts-ignore
import { PolygonLayer } from '@l7/layers';
// @ts-ignore
import { Scene } from '@l7/scene';
import * as dat from 'dat.gui';
import * as React from 'react';
export default class Sepia extends React.Component {
private gui: dat.GUI;
private $stats: Node;
private scene: Scene;
public componentWillUnmount() {
if (this.gui) {
this.gui.destroy();
}
if (this.$stats) {
document.body.removeChild(this.$stats);
}
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
);
const data = await response.json();
const scene = new Scene({
id: 'map',
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
center: [110.19382669582967, 50.258134],
pitch: 0,
zoom: 3,
});
const layer = new PolygonLayer({
enablePicking: true,
enableHighlight: true,
passes: ['sepia'],
});
layer
.source(data)
.size('name', [0, 10000, 50000, 30000, 100000])
.color('name', [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
])
.shape('fill')
.style({
opacity: 0.8,
});
scene.addLayer(layer);
scene.render();
this.scene = scene;
/*** 运行时修改样式属性 ***/
const gui = new dat.GUI();
this.gui = gui;
const styleOptions = {
amount: 0.5,
};
const pointFolder = gui.addFolder('Sepia 配置');
pointFolder.add(styleOptions, 'amount', 0, 1).onChange((amount: number) => {
layer.style({
passes: [
[
'sepia',
{
amount,
},
],
],
});
scene.render();
});
pointFolder.open();
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -79,7 +79,7 @@ export default class TAA extends React.Component {
const pointFolder = gui.addFolder('TAA 配置');
pointFolder
.add(styleOptions, 'jitterScale', 0, 100)
.onChange((jitterScale: boolean) => {
.onChange((jitterScale: number) => {
layer.style({
jitterScale,
});

229
yarn.lock
View File

@ -316,6 +316,14 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-json-strings" "^7.2.0"
"@babel/plugin-proposal-nullish-coalescing-operator@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz#41c360d59481d88e0ce3a3f837df10121a769b39"
integrity sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.2.0"
"@babel/plugin-proposal-object-rest-spread@7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58"
@ -399,6 +407,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-nullish-coalescing-operator@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz#f75083dfd5ade73e783db729bbd87e7b9efb7624"
integrity sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e"
@ -905,7 +920,7 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3":
version "7.6.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.3.tgz#935122c74c73d2240cafd32ddb5fc2a6cd35cf1f"
integrity sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==
@ -1124,10 +1139,10 @@
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.7.3.tgz#a166882c81c0c6040975dd30df24fae8549bd96f"
integrity sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw==
"@emotion/is-prop-valid@0.8.3":
version "0.8.3"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.3.tgz#cbe62ddbea08aa022cdf72da3971570a33190d29"
integrity sha512-We7VBiltAJ70KQA0dWkdPMXnYoizlxOXpvtjmu5/MBnExd+u0PGgV27WCYanmLAbCwAU30Le/xA0CQs/F/Otig==
"@emotion/is-prop-valid@0.8.4":
version "0.8.4"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.4.tgz#cf1dcfc1812c226f05e1ba53592eb6b51e734990"
integrity sha512-QBW8h6wVQgeQ55F52rNaprEJxtVR+/ScOP8/V1ScSpPzKqHdFB9QVqby0Z50sqS8mcaeIl5vR1vQpKwJbIS6NQ==
dependencies:
"@emotion/memoize" "0.7.3"
@ -1136,10 +1151,10 @@
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.3.tgz#5b6b1c11d6a6dddf1f2fc996f74cf3b219644d78"
integrity sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==
"@emotion/serialize@^0.11.12":
version "0.11.13"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.13.tgz#49b93e8e6f7dba70bbf0ecd697bb4e03f45d8cde"
integrity sha512-Tw+z6oIFCXeznoH25TozFoOUJ9BIyKBgZ9Gif3ej9aqPeP/Dzct8WIXSsz08xxyt1RPlKokvJ3fzMDq0UjL3RQ==
"@emotion/serialize@^0.11.12", "@emotion/serialize@^0.11.14":
version "0.11.14"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.14.tgz#56a6d8d04d837cc5b0126788b2134c51353c6488"
integrity sha512-6hTsySIuQTbDbv00AnUO6O6Xafdwo5GswRlMZ5hHqiFx+4pZ7uGWXUQFW46Kc2taGhP89uXMXn/lWQkdyTosPA==
dependencies:
"@emotion/hash" "0.7.3"
"@emotion/memoize" "0.7.3"
@ -1152,23 +1167,23 @@
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.3.tgz#689f135ecf87d3c650ed0c4f5ddcbe579883564a"
integrity sha512-c3Q6V7Df7jfwSq5AzQWbXHa5soeE4F5cbqi40xn0CzXxWW9/6Mxq48WJEtqfWzbZtW9odZdnRAkwCQwN12ob4A==
"@emotion/styled-base@^10.0.22":
version "10.0.22"
resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.22.tgz#2b6e86e477ed81bd74aa6cef5f403d500503df5b"
integrity sha512-ikSuAcz86BcmlZM5EysqCH0EUssYm5ardrWNVM3Ri5ODpOlKPrT//jVozJU2uK3q5GRcqZHLqagP/nd9beNUfQ==
"@emotion/styled-base@^10.0.23":
version "10.0.23"
resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.23.tgz#21244fa25f4c867033e670cf9d7bd0262b4f74ce"
integrity sha512-94QowN2S09nCXRz9dXBiMaEcUcXn9kHM8uFExpsspwswHWnkpFn6jTewotQEgI7RROnAXDZ8fvSTkCdqtn3sfw==
dependencies:
"@babel/runtime" "^7.5.5"
"@emotion/is-prop-valid" "0.8.3"
"@emotion/serialize" "^0.11.12"
"@emotion/is-prop-valid" "0.8.4"
"@emotion/serialize" "^0.11.14"
"@emotion/utils" "0.11.2"
"@emotion/styled@^10.0.14":
version "10.0.22"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.22.tgz#ee398710876ebda5a418f84359516c6a1c5c41b1"
integrity sha512-3+dnBk8NjXnddI8Gi2VJLMmup0bCG8HQkZLaeNky+GSLl8VyxQfuaK5I5aDVvgQ3UzkxrcZrFB3vHYU/iUakBA==
version "10.0.23"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.23.tgz#2f8279bd59b99d82deade76d1046249ddfab7c1b"
integrity sha512-gNr04eqBQ2iYUx8wFLZDfm3N8/QUOODu/ReDXa693uyQGy2OqA+IhPJk+kA7id8aOfwAsMuvZ0pJImEXXKtaVQ==
dependencies:
"@emotion/styled-base" "^10.0.22"
babel-plugin-emotion "^10.0.22"
"@emotion/styled-base" "^10.0.23"
babel-plugin-emotion "^10.0.23"
"@emotion/stylis@0.8.4":
version "0.8.4"
@ -2983,9 +2998,9 @@
integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==
"@types/jest@^24.0.18":
version "24.0.20"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.20.tgz#729d5fe8684e7fb06368d3bd557ac6d91289d861"
integrity sha512-M8ebEkOpykGdLoRrmew7UowTZ1DANeeP0HiSIChl/4DGgmnSC1ntitNtkyNSXjMTsZvXuaxJrxjImEnRWNPsPw==
version "24.0.21"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.21.tgz#2c0a25440e025bb265f4a17d8b79b11b231426bf"
integrity sha512-uyqFvx78Tuy0h5iLCPWRCvi5HhWwEqhIj30doitp191oYLqlCxUyAJHdWVm5+Nr271/vPnkyt6rWeEIjGowBTg==
dependencies:
"@types/jest-diff" "*"
@ -3007,9 +3022,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*", "@types/node@^12.0.2", "@types/node@^12.11.1", "@types/node@^12.7.3":
version "12.11.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a"
integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA==
version "12.12.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.0.tgz#ff3201972d6dc851a9275308a17b9b5094e68057"
integrity sha512-6N8Sa5AaENRtJnpKXZgvc119PKxT1Lk9VPy4kfT8JF23tIe1qDfaGkBR2DRKJFIA7NptMz+fps//C6aLi1Uoug==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@ -3906,15 +3921,15 @@ babel-plugin-dynamic-import-node@2.3.0, babel-plugin-dynamic-import-node@^2.3.0:
dependencies:
object.assign "^4.1.0"
babel-plugin-emotion@^10.0.14, babel-plugin-emotion@^10.0.22:
version "10.0.22"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.22.tgz#7860b39f96dcc1b79e326987ce29d4fcfff96f52"
integrity sha512-e3Yo9+GD6ovrcZlt2Unjgfyy0gfdz0+8httltToWL+biFMhLPPT1PJlc0GHy9i+vtPSrTBNY2hawfPJnuG2L3g==
babel-plugin-emotion@^10.0.14, babel-plugin-emotion@^10.0.22, babel-plugin-emotion@^10.0.23:
version "10.0.23"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.23.tgz#040d40bf61dcab6d31dd6043d10e180240b8515b"
integrity sha512-1JiCyXU0t5S2xCbItejCduLGGcKmF3POT0Ujbexog2MI4IlRcIn/kWjkYwCUZlxpON0O5FC635yPl/3slr7cKQ==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@emotion/hash" "0.7.3"
"@emotion/memoize" "0.7.3"
"@emotion/serialize" "^0.11.12"
"@emotion/serialize" "^0.11.14"
babel-plugin-macros "^2.0.0"
babel-plugin-syntax-jsx "^6.18.0"
convert-source-map "^1.5.0"
@ -4693,9 +4708,9 @@ can-use-dom@^0.1.0:
integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=
caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001004:
version "1.0.30001005"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001005.tgz#823054210be638c725521edcb869435dae46728d"
integrity sha512-g78miZm1Z5njjYR216a5812oPiLgV1ssndgGxITHWUopmjUrCswMisA0a2kSB7a0vZRox6JOKhM51+efmYN8Mg==
version "1.0.30001006"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001006.tgz#5b6e8288792cfa275f007b2819a00ccad7112655"
integrity sha512-MXnUVX27aGs/QINz+QG1sWSLDr3P1A3Hq5EUWoIt0T7K24DuvMxZEnh3Y5aHlJW6Bz2aApJdSewdYLd8zQnUuw==
capture-exit@^2.0.0:
version "2.0.0"
@ -5168,11 +5183,9 @@ connect-history-api-fallback@^1.6.0:
integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
console-browserify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=
dependencies:
date-now "^0.1.4"
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
@ -5361,17 +5374,17 @@ copy-webpack-plugin@^4.5.2:
serialize-javascript "^1.4.0"
core-js-compat@^3.1.1:
version "3.3.4"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.3.4.tgz#a151c6cd754edbfe6a4a2a66b9382df2ae74fbcd"
integrity sha512-7OK3/LPP8R3Ovasf3GilEOp+o1w0ZKJ75FMou2RDfTwIV69G5RkKCGFnqgBv/ZhR6xo9GCzlfVALyHmydbE7DA==
version "3.3.5"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.3.5.tgz#7abf70778b73dc74aa99d4075aefcd99b76f2c3a"
integrity sha512-44ZORuapx0MUht0MUk0p9lcQPh7n/LDXehimTmjCs0CYblpKZcqVd5w0OQDUDq5OQjEbazWObHDQJWvvHYPNTg==
dependencies:
browserslist "^4.7.2"
semver "^6.3.0"
core-js-pure@^3.0.1:
version "3.3.4"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.3.4.tgz#01d2842f552a866265dc77ededb2ccd668ff2879"
integrity sha512-hqxt6XpR4zIMNUY920oNyAtwaq4yg8IScmXumnfyRWF9+ur7wtjr/4eCdfTJzY64jmi8WRCwIqNBKzYeOKdvnw==
version "3.3.5"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.3.5.tgz#23dc7e44bb946bf1752b377709e8591faffb0bac"
integrity sha512-njLfaPe3tS+8Swgx/itYgJ1jiizCWtNXrK1VzMoXbT6LhiYbIAQioukPmZlB2wTieJY2g4fLRUh96WfXpN61oA==
core-js@^1.0.0:
version "1.2.7"
@ -5384,9 +5397,9 @@ core-js@^2.4.0, core-js@^2.5.0:
integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
core-js@^3.0.1, core-js@^3.0.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.4.tgz#6b0a23392958317bfb46e40b090529a923add669"
integrity sha512-BtibooaAmSOptGLRccsuX/dqgPtXwNgqcvYA6kOTTMzonRxZ+pJS4e+6mvVutESfXMeTnK8m3M+aBu3bkJbR+w==
version "3.3.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.5.tgz#58d20f48a95a07304b62ff752742b82b56431ed8"
integrity sha512-0J3K+Par/ZydhKg8pEiTcK/9d65/nqJOzY62uMkjeBmt05fDOt/khUVjDdh8TpeIuGQDy1yLDDCjiWN/8pFIuw==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
@ -5609,21 +5622,13 @@ css-to-react-native@^2.0.3:
css-color-keywords "^1.0.0"
postcss-value-parser "^3.3.0"
css-tree@1.0.0-alpha.29:
version "1.0.0-alpha.29"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39"
integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==
dependencies:
mdn-data "~1.1.0"
source-map "^0.5.3"
css-tree@1.0.0-alpha.33:
version "1.0.0-alpha.33"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.33.tgz#970e20e5a91f7a378ddd0fc58d0b6c8d4f3be93e"
integrity sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w==
css-tree@1.0.0-alpha.37:
version "1.0.0-alpha.37"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
dependencies:
mdn-data "2.0.4"
source-map "^0.5.3"
source-map "^0.6.1"
css-what@2.1, css-what@^2.1.2:
version "2.1.3"
@ -5655,12 +5660,12 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
csso@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b"
integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==
csso@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d"
integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==
dependencies:
css-tree "1.0.0-alpha.29"
css-tree "1.0.0-alpha.37"
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.8"
@ -5820,11 +5825,6 @@ date-fns@^1.27.2:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
dateformat@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@ -6545,14 +6545,14 @@ es-to-primitive@^1.2.0:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.51:
version "0.10.51"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.51.tgz#ed2d7d9d48a12df86e0299287e93a09ff478842f"
integrity sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==
es5-ext@^0.10.35, es5-ext@^0.10.50:
version "0.10.52"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f"
integrity sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==
dependencies:
es6-iterator "~2.0.3"
es6-symbol "~3.1.1"
next-tick "^1.0.0"
es6-symbol "~3.1.2"
next-tick "~1.0.0"
es5-shim@^4.5.13:
version "4.5.13"
@ -6585,13 +6585,13 @@ es6-shim@^0.35.5:
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.5.tgz#46f59dc0a84a1c5029e8ff1166ca0a902077a9ab"
integrity sha512-E9kK/bjtCQRpN1K28Xh4BlmP8egvZBGJJ+9GtnzOwt7mdqtrjHFuVGr7QJfdjBIKqrlU5duPf3pCBoDrkjVYFg==
es6-symbol@^3.1.1, es6-symbol@~3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.2.tgz#859fdd34f32e905ff06d752e7171ddd4444a7ed1"
integrity sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ==
es6-symbol@^3.1.1, es6-symbol@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
dependencies:
d "^1.0.1"
es5-ext "^0.10.51"
ext "^1.1.2"
escape-html@^1.0.3, escape-html@~1.0.3:
version "1.0.3"
@ -6829,6 +6829,13 @@ express@^4.17.0, express@^4.17.1:
utils-merge "1.0.1"
vary "~1.1.2"
ext@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.1.2.tgz#d1d216c83641bb4cb7684622b063cff44a19ce35"
integrity sha512-/KLjJdTNyDepCihrk4HQt57nAE1IRCEo5jUt+WgWGCr1oARhibDvmI2DMcSNWood1T9AUWwq+jaV1wvRqaXfnA==
dependencies:
type "^2.0.0"
extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@ -7783,9 +7790,9 @@ handle-thing@^2.0.0:
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
handlebars@^4.1.2, handlebars@^4.4.0:
version "4.4.5"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.4.5.tgz#1b1f94f9bfe7379adda86a8b73fb570265a0dddd"
integrity sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==
version "4.5.1"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.1.tgz#8a01c382c180272260d07f2d1aa3ae745715c7ba"
integrity sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==
dependencies:
neo-async "^2.6.0"
optimist "^0.6.1"
@ -10155,11 +10162,6 @@ mdn-data@2.0.4:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
mdn-data@~1.1.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01"
integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -10267,7 +10269,7 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge-json-schemas@^1.0.0:
merge-json-schemas@1.0.0, merge-json-schemas@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/merge-json-schemas/-/merge-json-schemas-1.0.0.tgz#2d635eaa8401c5fa3d03f30f89349fc7cafee62f"
integrity sha1-LWNeqoQBxfo9A/MPiTSfx8r+5i8=
@ -10665,7 +10667,7 @@ nested-object-assign@^1.0.3:
resolved "https://registry.yarnpkg.com/nested-object-assign/-/nested-object-assign-1.0.3.tgz#5aca69390d9affe5a612152b5f0843ae399ac597"
integrity sha512-kgq1CuvLyUcbcIuTiCA93cQ2IJFSlRwXcN+hLcb2qLJwC2qrePHGZZa7IipyWqaWF6tQjdax2pQnVxdq19Zzwg==
next-tick@^1.0.0:
next-tick@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
@ -11710,11 +11712,11 @@ pnp-webpack-plugin@1.4.3:
ts-pnp "^1.1.2"
polished@^3.3.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/polished/-/polished-3.4.1.tgz#1eb5597ec1792206365635811d465751f5cbf71c"
integrity sha512-GflTnlP5rrpDoigjczEkS6Ye7NDA4sFvAnlr5hSDrEvjiVj97Xzev3hZlLi3UB27fpxyTS9rWU64VzVLWkG+mg==
version "3.4.2"
resolved "https://registry.yarnpkg.com/polished/-/polished-3.4.2.tgz#b4780dad81d64df55615fbfc77acb52fd17d88cd"
integrity sha512-9Rch6iMZckABr6EFCLPZsxodeBpXMo9H4fRlfR/9VjMEyy5xpo1/WgXlJGgSjPyVhEZNycbW7UmYMNyWS5MI0g==
dependencies:
"@babel/runtime" "^7.4.5"
"@babel/runtime" "^7.6.3"
polyline-miter-util@^1.0.1:
version "1.0.1"
@ -12618,9 +12620,9 @@ react-test-renderer@^16.0.0-0:
scheduler "^0.17.0"
react-textarea-autosize@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.0.tgz#3132cb77e65d94417558d37c0bfe415a5afd3445"
integrity sha512-c2FlR/fP0qbxmlrW96SdrbgP/v0XZMTupqB90zybvmDVDutytUgPl7beU35klwcTeMepUIQEpQUn3P3bdshGPg==
version "7.1.1"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-7.1.1.tgz#66ff1b7d6e1ab759fdf35f09e60bdd0e15d8e143"
integrity sha512-dVDVXlUm5uUgWyZAL4gaxJiDb2xCWM/qk6Rl2ixXPSKNsngKhvAj3KbDS9mnQn/qIZSYVD+/iuZT/eQWmNjBLw==
dependencies:
"@babel/runtime" "^7.1.2"
prop-types "^15.6.0"
@ -13839,9 +13841,9 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
urix "^0.1.0"
source-map-support@^0.5.3, source-map-support@^0.5.6, source-map-support@~0.5.12:
version "0.5.13"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
@ -13858,7 +13860,7 @@ source-map@^0.4.2:
dependencies:
amdefine ">=0.0.4"
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0:
source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@ -14453,16 +14455,16 @@ svg-tags@^1.0.0:
integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
svgo@^1.2.2:
version "1.3.0"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.0.tgz#bae51ba95ded9a33a36b7c46ce9c359ae9154313"
integrity sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ==
version "1.3.1"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.1.tgz#115c1f9d7e3294dfc66288c8499e65c2a1479729"
integrity sha512-2iv3AHKL+x2/nAvkg+vTv01aK94OFU6wTRbnv/K43mf1OdKEEA8xaQl7Wjs5Vrh9AlyXvyPd8fg6s6YzYdQTnQ==
dependencies:
chalk "^2.4.1"
coa "^2.0.2"
css-select "^2.0.0"
css-select-base-adapter "^0.1.1"
css-tree "1.0.0-alpha.33"
csso "^3.5.1"
css-tree "1.0.0-alpha.37"
csso "^4.0.2"
js-yaml "^3.13.1"
mkdirp "~0.5.1"
object.values "^1.1.0"
@ -14937,6 +14939,11 @@ type@^1.0.1:
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3"
integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==
typed-styles@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9"
@ -14966,9 +14973,9 @@ uglify-js@3.4.x:
source-map "~0.6.1"
uglify-js@^3.1.4, uglify-js@^3.5.1:
version "3.6.4"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.4.tgz#88cc880c6ed5cf9868fdfa0760654e7bed463f1d"
integrity sha512-9Yc2i881pF4BPGhjteCXQNaXx1DCwm3dtOyBaG2hitHjLWOczw/ki8vD1bqyT3u6K0Ms/FpCShkmfg+FtlOfYA==
version "3.6.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.5.tgz#b0ee796d2ae7e25672e04f65629b997cd4b30bd6"
integrity sha512-7L3W+Npia1OCr5Blp4/Vw83tK1mu5gnoIURtT1fUVfQ3Kf8WStWV6NJz0fdoBJZls0KlweruRTLVe6XLafmy5g==
dependencies:
commander "~2.20.3"
source-map "~0.6.1"