598 lines
20 KiB
JavaScript
598 lines
20 KiB
JavaScript
'use strict';
|
||
|
||
// 控制颜色
|
||
const gl_Colors = new Float32Array([
|
||
0, 0, 0, 0, // 0: 空
|
||
|
||
0, 0, 1, 1, // 1: 高阻态 Z
|
||
0.2, 0.847, 0.1, 1, // 2: value = 0 用于 width = 1 的信号
|
||
0.2, 0.847, 0.1, 1, // 3: value = 1 用于 width = 1 的信号
|
||
0.9, 0.2, 0.2, 1, // 4: 未知态 X
|
||
.5, 1, 1, 1, // 5: vec 用于 width > 1 的信号
|
||
|
||
1, 1, 0, 1, // 6: yellow
|
||
1, 0, 1, 1, // 7: strange purple
|
||
|
||
0, 1, 0, .5, // 8: (l L) weak 0
|
||
0, 1, 1, .5, // 9: (h H) weak 1
|
||
1, 0, 0, .5, // 10: (w W) weak unknown
|
||
|
||
0, 0, 1, 0.1, // 11: 高阻态 Z 遮罩
|
||
0.2, 0.847, 0.1, 0.1, // 12: value = 0 遮罩
|
||
0.2, 0.847, 0.1, 0.1, // 13: value = 1 遮罩
|
||
0.9, 0.2, 0.2, 0.1, // 14: 未知态 X 遮罩
|
||
.5, 1, 1, 0.1 // 15: vec 遮罩
|
||
]);
|
||
|
||
|
||
// 控制方向
|
||
const gl_Shifts = new Float32Array([ // 14
|
||
0, 0, // 0
|
||
1, -1, // 1
|
||
1, 0, // 2
|
||
1, 1, // 3
|
||
-1, -1, // 4
|
||
-1, 0, // 5
|
||
-1, 1 // 6
|
||
]);
|
||
|
||
const gl_Shifts_map = new Map();
|
||
gl_Shifts_map.set(-1, 4);
|
||
gl_Shifts_map.set(0, 0);
|
||
gl_Shifts_map.set(1, 1);
|
||
|
||
const lineWidth = 0.004; // 不能写为 0.0045 这样会因为插值造成不同线段的宽度不一致的问题
|
||
const widthShift = lineWidth / 2;
|
||
const gl_WidthShifts = new Float32Array([
|
||
0, widthShift, // 0
|
||
- widthShift, widthShift, // 1
|
||
- widthShift, 0, // 2
|
||
- widthShift, - widthShift, // 3
|
||
0, - widthShift, // 4
|
||
widthShift, - widthShift, // 5
|
||
widthShift, 0, // 6
|
||
widthShift, widthShift // 7
|
||
]);
|
||
|
||
|
||
class ShaderMaker {
|
||
/**
|
||
*
|
||
* @param {'VERTEX_SHADER' | 'FRAGMENT_SHADER'} type
|
||
* @param {string} source
|
||
*/
|
||
constructor(type, source) {
|
||
this.type = type;
|
||
this.source = source;
|
||
}
|
||
|
||
/**
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @return {WebGLShader}
|
||
*/
|
||
make(gl) {
|
||
const shader = gl.createShader(gl[this.type]);
|
||
gl.shaderSource(shader, this.source);
|
||
gl.compileShader(shader);
|
||
const ok = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||
if (!ok) {
|
||
console.log('创建类型为 ' + type + ' 的着色器失败!');
|
||
}
|
||
return shader;
|
||
}
|
||
}
|
||
|
||
const vertexShaderScalar = new ShaderMaker('VERTEX_SHADER', `#version 300 es
|
||
in uvec4 pos;
|
||
out vec4 v_color;
|
||
uniform vec2 scale;
|
||
uniform vec2 offset;
|
||
uniform vec4 colors[16];
|
||
uniform vec2 shifts[7]; // 基础八位图偏移量,为了性能,pos 只传入整数,需要的坐标负数由该值提供
|
||
uniform vec2 widthShifts[8]; // 用于构造线宽的偏移
|
||
|
||
void main() {
|
||
v_color = colors[pos.z];
|
||
vec2 shift = shifts[pos.y];
|
||
vec2 widthShift = widthShifts[pos.w];
|
||
gl_Position = vec4(
|
||
float(pos.x) * scale.x + offset.x + float(widthShift.x),
|
||
float(shift.x) * scale.y + offset.y + float(widthShift.y),
|
||
1, 1
|
||
);
|
||
}`);
|
||
|
||
const fragmentShader = new ShaderMaker('FRAGMENT_SHADER', `#version 300 es
|
||
precision mediump float;
|
||
in vec4 v_color;
|
||
out vec4 outColor;
|
||
void main() {
|
||
outColor = v_color;
|
||
}`);
|
||
|
||
|
||
class WebGL2WaveRender {
|
||
/**
|
||
*
|
||
* @param {{
|
||
* grid: HTMLDivElement,
|
||
* view: HTMLDivElement,
|
||
* values: HTMLDivElement
|
||
* }} elements
|
||
* @param {{
|
||
* chango: Record<string, {
|
||
* kind: string,
|
||
* wave: Array<number | string>,
|
||
* lineVao: WebGLVertexArrayObject
|
||
* maskVao: WebGLVertexArrayObject
|
||
* }>
|
||
* view: Array<{ ref: string }>,
|
||
* currentWires: Set<{
|
||
* kind: string,
|
||
* link: string,
|
||
* name: string,
|
||
* size: number,
|
||
* parent: object,
|
||
* type: string
|
||
* }>,
|
||
* time: number
|
||
* }} globalLookup
|
||
* @param {{
|
||
* width: number,
|
||
* height: number,
|
||
* xScale: number,
|
||
* xOffset: number,
|
||
* yOffset: number,
|
||
* yStep: number,
|
||
* yDuty: number
|
||
* }} pstate
|
||
* @param { Array } plugins
|
||
*/
|
||
constructor(elements, globalLookup, pstate, plugins) {
|
||
const canvas = document.createElement('canvas');
|
||
elements.view.replaceChildren(canvas);
|
||
|
||
this.elements = elements;
|
||
this.globalLookup = globalLookup;
|
||
this.canvas = canvas;
|
||
this.pstate = pstate;
|
||
this.plugins = plugins;
|
||
|
||
const gl = canvas.getContext('webgl2', {
|
||
premultipliedAlpha: false,
|
||
alpha: true,
|
||
antialias: false,
|
||
depth: false
|
||
});
|
||
this.webglLocation = this.initProgram(gl);
|
||
const { lineVerticesMap, maskVerticesMap } = this.makeVertex();
|
||
this.lineVerticesMap = lineVerticesMap;
|
||
this.maskVerticesMap = maskVerticesMap;
|
||
|
||
this.initData();
|
||
this.animationHandler = undefined;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {WebGL2RenderingContext} gl
|
||
* @returns {{
|
||
* colors: WebGLUniformLocation,
|
||
* shifts: WebGLUniformLocation,
|
||
* scale: WebGLUniformLocation,
|
||
* offset: WebGLUniformLocation,
|
||
* pos: number,
|
||
* widthShifts: WebGLUniformLocation,
|
||
* gl: WebGL2RenderingContext
|
||
* }}
|
||
*/
|
||
initProgram(gl) {
|
||
const program = gl.createProgram();
|
||
gl.attachShader(program, vertexShaderScalar.make(gl));
|
||
gl.attachShader(program, fragmentShader.make(gl));
|
||
gl.linkProgram(program);
|
||
gl.useProgram(program);
|
||
|
||
const webglLocation = {
|
||
colors: gl.getUniformLocation(program, 'colors'),
|
||
shifts: gl.getUniformLocation(program, 'shifts'),
|
||
scale: gl.getUniformLocation(program, 'scale'),
|
||
offset: gl.getUniformLocation(program, 'offset'),
|
||
pos: gl.getAttribLocation(program, 'pos'),
|
||
widthShifts: gl.getUniformLocation(program, 'widthShifts'),
|
||
gl
|
||
};
|
||
|
||
return webglLocation;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @returns {{
|
||
* lineVerticesMap: Map<string, Uint32Array>,
|
||
* maskVerticesMap: Map<string, Uint32Array>
|
||
* }}
|
||
*/
|
||
makeVertex() {
|
||
const globalLookup = this.globalLookup;
|
||
const time = globalLookup.time;
|
||
const lineVerticesMap = new Map();
|
||
const maskVerticesMap = new Map();
|
||
for (const id of Reflect.ownKeys(globalLookup.chango)) {
|
||
const signalItem = globalLookup.chango[id];
|
||
const { kind, wave } = signalItem;
|
||
if (kind === 'bit') {
|
||
const { lineVertices, maskVertices } = this.makeBitVertex(wave, time);
|
||
lineVerticesMap.set(id, lineVertices);
|
||
maskVerticesMap.set(id, maskVertices);
|
||
} else if (kind === 'vec') {
|
||
// const vertices = this.makeVecVertex(wave, time);
|
||
// lineVerticesMap.set(id, vertices);
|
||
}
|
||
}
|
||
return { lineVerticesMap, maskVerticesMap };
|
||
}
|
||
|
||
|
||
/**
|
||
* @description 将wave 的值转化为渲染需要用到的值
|
||
* @param {*} value
|
||
* @return {{
|
||
* y: number // 裁剪空间的纵坐标
|
||
* color: number // 颜色的索引 颜色rgb = gl_Colors[color]
|
||
* }}
|
||
*/
|
||
translateValue2RenderParameter(value) {
|
||
switch (value) {
|
||
case 0: return { y: -1, color: 2 }; // 0 value
|
||
case 1: return { y: 1, color: 3 }; // 1 value
|
||
case 2: case 3: return { y: -1, color: 4 }; // 不定态 x
|
||
case 4: case 5: return { y: 0, color: 2 }; // 高阻态 z
|
||
default: return { y: -1, color: 7 }; // 其他,我也不知道还有啥
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {{x: number, y: number, color: number} | undefined} p0 前一个点
|
||
* @param {{x: number, y: number, color: number} | undefined} p1 当前的点
|
||
* @param {{x: number, y: number, color: number} | undefined} p2 后一个点
|
||
* @returns {number} 这是 widthshift 的索引,只需要 + 4 再 % 8 就能得到另一个
|
||
*/
|
||
makeWidthShiftIndexByPoints(p0, p1, p2) {
|
||
if (p0 === undefined) {
|
||
if (p1.y === p2.y) {
|
||
return 0;
|
||
} else if (p1.x === p2.x) {
|
||
return 6;
|
||
}
|
||
} else if (p2 === undefined) {
|
||
if (p1.y === p0.y) {
|
||
return 0;
|
||
} else if (p1.x === p0.x) {
|
||
return 6;
|
||
}
|
||
} else {
|
||
if (p0.x !== p1.x && p0.y === p1.y && p1.x === p2.x && p1.y !== p2.y) {
|
||
if (p2.y > p1.y) {
|
||
return 1;
|
||
} else {
|
||
return 7;
|
||
}
|
||
} else if (p0.x === p1.x && p0.y !== p1.y && p1.x !== p2.x && p1.y === p2.y) {
|
||
if (p1.y > p0.y) {
|
||
return 1;
|
||
} else {
|
||
return 7;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {Array<string | number>} wave
|
||
* @param { number } time
|
||
* @returns {{
|
||
* lineVertices: Uint32Array
|
||
* maskVertices: Uint32Array
|
||
* }}
|
||
*/
|
||
makeBitVertex(wave, time, debug = false) {
|
||
const length = wave.length;
|
||
// 先将节点数据转化为裁剪空间的坐标
|
||
const perspectivePoints = [];
|
||
|
||
for (let i = 0; i < length; ++ i) {
|
||
// const currentWave = wave[(i === 0) ? 0 : (i - 1)];
|
||
const currentWave = wave[i];
|
||
const nextWave = (i === (length - 1)) ? wave[i] : wave[i + 1];
|
||
|
||
const t1 = currentWave[0];
|
||
const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
|
||
const value1 = currentWave[1];
|
||
const value2 = nextWave[1];
|
||
|
||
const renderParam1 = this.translateValue2RenderParameter(value1);
|
||
const renderParam2 = this.translateValue2RenderParameter(value2);
|
||
|
||
if (i === 0) {
|
||
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
|
||
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
|
||
} else {
|
||
const lastPoint = perspectivePoints.at(-1);
|
||
if ((lastPoint.y !== renderParam1.y) || (lastPoint.color !== renderParam1.color)) {
|
||
perspectivePoints.push({ x: t1, y: renderParam1.y, color: renderParam1.color });
|
||
}
|
||
if ((renderParam1.y !== renderParam2.y) || (renderParam1.color !== renderParam2.color)) {
|
||
perspectivePoints.push({ x: t2, y: renderParam1.y, color: renderParam1.color });
|
||
}
|
||
}
|
||
}
|
||
|
||
// 确保最后一个点延申到了 time
|
||
const lastPoint = perspectivePoints.at(-1);
|
||
if (lastPoint.x < time) {
|
||
perspectivePoints.push({ x: time, y: lastPoint.y, color: lastPoint.color });
|
||
}
|
||
|
||
// 计算出传入 shader 四元组数组
|
||
// 四元组: (x, yshift_index, color_index, width_shift_index)
|
||
const pointNum = perspectivePoints.length;
|
||
const lineVertices = [];
|
||
const maskVertices = [];
|
||
|
||
// const lineVertices = [
|
||
// 0, 0, 2, 0,
|
||
// 10, 0, 2, 0,
|
||
// 10, 4, 2, 0,
|
||
// 20, 4, 2, 0,
|
||
// 20, 0, 2, 0,
|
||
// 30, 0, 2, 0,
|
||
// 30, 4, 2, 0
|
||
// ];
|
||
// return new Uint32Array(lineVertices);
|
||
|
||
|
||
// 制作 lineVertices
|
||
for (let i = 0; i < pointNum; ++ i) {
|
||
// p0: 上一个点
|
||
// p1: 当前的点
|
||
// p2: 下一个点
|
||
const p0 = perspectivePoints[i - 1];
|
||
const p1 = perspectivePoints[i];
|
||
const p2 = perspectivePoints[i + 1];
|
||
// p1.x, y1Index, p1.color, 0,
|
||
const y1Index = gl_Shifts_map.get(p1.y);
|
||
|
||
const wsIndex = this.makeWidthShiftIndexByPoints(p0, p1, p2);
|
||
lineVertices.push(
|
||
p1.x, y1Index, p1.color, wsIndex,
|
||
p1.x, y1Index, p1.color, (wsIndex + 4) % 8
|
||
);
|
||
|
||
// 防止颜色不同导致单个图元内出现两个颜色,这会引发shader的渐变
|
||
if (p2 !== undefined && p1.color !== p2.color) {
|
||
lineVertices.push(
|
||
p1.x, y1Index, p2.color, wsIndex,
|
||
p1.x, y1Index, p2.color, (wsIndex + 4) % 8
|
||
);
|
||
}
|
||
}
|
||
|
||
// 制作 maskVertices
|
||
for (let i = 0; i < pointNum; ++ i) {
|
||
const p1 = perspectivePoints[i];
|
||
const p2 = perspectivePoints[i + 1];
|
||
const p3 = perspectivePoints[i + 2];
|
||
if (p1 === undefined || p2 === undefined || p3 === undefined) {
|
||
continue;
|
||
}
|
||
if (p2.y > p1.y) {
|
||
// 矩形的四个点
|
||
// 四元组: (x, yshift_index, color_index, width_shift_index)
|
||
const r1 = [p1.x, gl_Shifts_map.get(p1.y), p2.color + 10, 4];
|
||
const r2 = [p2.x, gl_Shifts_map.get(p2.y), p2.color + 10, 4];
|
||
const r3 = [p3.x, gl_Shifts_map.get(p3.y), p2.color + 10, 4];
|
||
const r4 = [p3.x, gl_Shifts_map.get(p1.y), p2.color + 10, 4];
|
||
// 三角图元画矩形
|
||
maskVertices.push(
|
||
...r1, ...r2, ...r3,
|
||
...r1, ...r3, ...r4,
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
if (debug) {
|
||
console.log(perspectivePoints);
|
||
console.log(pointNum);
|
||
console.log(lineVertices);
|
||
}
|
||
|
||
const Uint32lineVertices = new Uint32Array(lineVertices);
|
||
const Uint32maskVertices = new Uint32Array(maskVertices);
|
||
|
||
return {
|
||
lineVertices : Uint32lineVertices,
|
||
maskVertices : Uint32maskVertices
|
||
};
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {Array<string | number>} wave
|
||
* @param { number } time
|
||
* @returns {Uint32Array}
|
||
*/
|
||
makeVecVertex(wave, time) {
|
||
const vertices = [];
|
||
const length = wave.length;
|
||
for (let i = 0; i < length; ++ i) {
|
||
const [t1, val, msk] = wave[i];
|
||
const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
|
||
// t1(val) --- t2(val)
|
||
|
||
if (msk) {
|
||
vertices.push(
|
||
t1, 0, 0,
|
||
t2, 0, 0,
|
||
t2, 0, 4,
|
||
t2, 4, 4,
|
||
t1, 6, 4,
|
||
t1, 0, 4,
|
||
t1, 3, 4,
|
||
t2, 1, 4,
|
||
t2, 0, 4
|
||
);
|
||
} else {
|
||
vertices.push(
|
||
t1, 0, 0,
|
||
t2, 0, 0,
|
||
t2, 0, 5,
|
||
t2, 4, 5,
|
||
t1, 6, 5,
|
||
t1, 0, 5,
|
||
t1, 3, 5,
|
||
t2, 1, 5,
|
||
t2, 0, 5
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
return new Uint32Array(vertices);
|
||
}
|
||
|
||
initData() {
|
||
const webglLocation = this.webglLocation;
|
||
const gl = webglLocation.gl;
|
||
const globalLookup = this.globalLookup;
|
||
const lineVerticesMap = this.lineVerticesMap;
|
||
const maskVerticesMap = this.maskVerticesMap;
|
||
|
||
for (const id of Reflect.ownKeys(globalLookup.chango)) {
|
||
const signalItem = globalLookup.chango[id];
|
||
|
||
const lineVertices = lineVerticesMap.get(id);
|
||
if (lineVertices === undefined) {
|
||
// console.warn(`无法找到 link 为 ${id} 的顶点数据`);
|
||
continue;
|
||
}
|
||
|
||
// 创建并设置 绘制wave轮廓 主体轮廓的 缓冲区、vao、顶点设置
|
||
const lineVertexBuffer = gl.createBuffer();
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexBuffer);
|
||
gl.bufferData(gl.ARRAY_BUFFER, lineVertices, gl.STATIC_DRAW);
|
||
signalItem.lineVao = gl.createVertexArray();
|
||
gl.bindVertexArray(signalItem.lineVao);
|
||
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0);
|
||
gl.enableVertexAttribArray(webglLocation.pos);
|
||
|
||
|
||
const maskVertices = maskVerticesMap.get(id);
|
||
if (maskVertices === undefined) {
|
||
continue;
|
||
}
|
||
|
||
// 创建并设置 绘制wave半透明遮罩层 主体轮廓的 缓冲区、vao、顶点设置
|
||
const maskVertexBuffer = gl.createBuffer();
|
||
gl.bindBuffer(gl.ARRAY_BUFFER, maskVertexBuffer);
|
||
gl.bufferData(gl.ARRAY_BUFFER, maskVertices, gl.STATIC_DRAW);
|
||
signalItem.maskVao = gl.createVertexArray();
|
||
gl.bindVertexArray(signalItem.maskVao);
|
||
gl.vertexAttribIPointer(webglLocation.pos, 4, gl.UNSIGNED_INT, 0, 0);
|
||
gl.enableVertexAttribArray(webglLocation.pos);
|
||
}
|
||
|
||
gl.uniform4fv(webglLocation.colors, gl_Colors);
|
||
gl.uniform2fv(webglLocation.widthShifts, gl_WidthShifts);
|
||
gl.uniform2fv(webglLocation.shifts, gl_Shifts);
|
||
}
|
||
|
||
render() {
|
||
if (this.animationHandler !== undefined) {
|
||
cancelAnimationFrame(this.animationHandler);
|
||
}
|
||
|
||
const canvas = this.canvas;
|
||
const webglLocation = this.webglLocation;
|
||
const gl = webglLocation.gl;
|
||
const globalLookup = this.globalLookup;
|
||
const lineVerticesMap = this.lineVerticesMap;
|
||
const maskVerticesMap = this.maskVerticesMap;
|
||
const elements = this.elements;
|
||
|
||
const timeScaleHeight = parseInt(document.body.style.getPropertyValue('--time-scale-height').match(/\d+/)[0]);
|
||
const sidebarPadding = parseInt(document.body.style.getPropertyValue('--sidebar-padding').match(/\d+/)[0]);
|
||
const vcdRenderPadding = parseInt(document.body.style.getPropertyValue('--vcd-render-padding').match(/\d+/)[0]);
|
||
const canvasPaddingTop = timeScaleHeight + sidebarPadding - vcdRenderPadding;
|
||
|
||
this.animationHandler = window.requestAnimationFrame(() => {
|
||
const { width, height, xScale, xOffset, yOffset, yStep, yDuty } = this.pstate;
|
||
const canvasHeight = height - canvasPaddingTop;
|
||
const canvasWidth = width;
|
||
|
||
// 默认 1594
|
||
canvas.width = canvasWidth;
|
||
|
||
// 默认 1260
|
||
canvas.height = canvasHeight;
|
||
|
||
// 设置 glsl 变量
|
||
gl.uniform2f(webglLocation.scale,
|
||
2 * xScale / canvasWidth,
|
||
yStep * yDuty / canvasHeight
|
||
);
|
||
console.log(yStep, yDuty, canvasHeight);
|
||
// 设置 webgl 和 canvas 大小位置一致
|
||
gl.viewport(0, 0, canvasWidth, canvasHeight);
|
||
// 清楚颜色缓冲区,也就是删除上一次的渲染结果
|
||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||
|
||
|
||
// 根据 globalLookup 当前激活的需要渲染的信号进行渲染
|
||
let index = 0;
|
||
for (const signal of globalLookup.currentWires) {
|
||
const wave = globalLookup.chango[signal.link].wave;
|
||
// this.makeBitVertex(wave, globalLookup.time, true);
|
||
const signalItem = globalLookup.chango[signal.link];
|
||
if (!signalItem) {
|
||
return;
|
||
}
|
||
|
||
// TODO: 将此处的 offset 计算中的参数和 globalSetting 的数字形成关联
|
||
gl.uniform2f(webglLocation.offset,
|
||
(2 * xOffset / width) - 1,
|
||
(2 * yOffset - 2 * yStep * (index + .7)) / canvasHeight + 1
|
||
);
|
||
|
||
|
||
console.log('offset y', (2 * yOffset - 2 * yStep * (index + .7)) / canvasHeight + 1);
|
||
|
||
// 根据 lineVao 进行绘制
|
||
const lineVertices = lineVerticesMap.get(signal.link);
|
||
const maskVertices = maskVerticesMap.get(signal.link);
|
||
|
||
if (signal.size === 1) {
|
||
// 如果是 width 为 1 的
|
||
gl.bindVertexArray(signalItem.lineVao);
|
||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, lineVertices.length / 4);
|
||
``
|
||
gl.bindVertexArray(signalItem.maskVao);
|
||
gl.drawArrays(gl.TRIANGLES, 0, maskVertices.length / 4);
|
||
} else {
|
||
// 如果是 width 大于 1 的
|
||
gl.drawArrays(gl.LINE_STRIP, 0, lineVertices.length / 4);
|
||
}
|
||
|
||
index ++;
|
||
}
|
||
|
||
this.plugins.map(fn => fn(globalLookup, this.pstate, elements));
|
||
this.animationHandler = undefined;
|
||
});
|
||
}
|
||
}
|
||
|
||
module.exports = WebGL2WaveRender; |