From 54819217227dd119ef145586f8a2496d6d8fb45a Mon Sep 17 00:00:00 2001
From: Kirigaya <1193466151@qq.com>
Date: Thu, 18 Apr 2024 01:12:21 +0800
Subject: [PATCH] =?UTF-8?q?[1=20bit]=E5=AE=8C=E6=88=90=20line=20=E7=B1=BB?=
=?UTF-8?q?=E5=9E=8B=E7=9A=84=E8=BD=AE=E5=BB=93=E5=92=8C=E9=81=AE=E7=BD=A9?=
=?UTF-8?q?=E5=B1=82=E7=9A=84=E7=BB=98=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
backup/drawline.js | 93 +++
public/vcd.css | 3 +
src/App.vue | 6 +-
src/components/render/cursor.vue | 2 +-
src/components/render/index.vue | 12 +-
src/components/right-nav.vue | 4 +-
src/components/sidebar.vue | 3 +-
src/hook/render.js | 4 +-
src/hook/utils.js | 41 +-
src/hook/wave-view/dom-container.js | 2 +-
src/hook/wave-view/gen-on-wheel.js | 56 +-
src/hook/wave-view/gen-render-waves-gl.js | 579 +++++++++++++------
src/hook/wave-view/gen-resize-handler.js | 6 +-
src/hook/wave-view/get-label.js | 1 -
src/hook/wave-view/key-bindo.js | 18 +-
src/hook/wave-view/render-values.js | 4 +-
src/hook/wave-view/water.js | 2 +-
src/i18n/index.js | 1 +
test/render-line/delaunator.min.js | 1 +
test/render-line/fragment.shader.glsl | 9 +
test/render-line/index.html | 24 +
test/render-line/main.js | 278 +++++++++
test/render-line/vertex.shader.glsl | 7 +
test/render-line/webgl.drawio | 655 ++++++++++++++++++++++
test/shader/fragment.glsl | 7 +
test/{ => shader}/vertex.glsl | 8 +-
26 files changed, 1563 insertions(+), 263 deletions(-)
create mode 100644 backup/drawline.js
create mode 100644 test/render-line/delaunator.min.js
create mode 100644 test/render-line/fragment.shader.glsl
create mode 100644 test/render-line/index.html
create mode 100644 test/render-line/main.js
create mode 100644 test/render-line/vertex.shader.glsl
create mode 100644 test/render-line/webgl.drawio
create mode 100644 test/shader/fragment.glsl
rename test/{ => shader}/vertex.glsl (75%)
diff --git a/backup/drawline.js b/backup/drawline.js
new file mode 100644
index 0000000..a0de98d
--- /dev/null
+++ b/backup/drawline.js
@@ -0,0 +1,93 @@
+const bar = [
+ (f, t, a, b, c0, c1) => [].concat( // 0
+ (f === 1) ? [a, 2, c1] : [],
+ [a, 5, c0],
+ [b, 5, c0],
+ (t > 1) ? [b, 0, c0] : []
+ ),
+
+ (f, t, a, b, c0, c1) => [].concat( // 1
+ (f === 0) ? [a, 5, c0] :
+ (f > 1) ? [a, 0, c0] : [],
+ [a, 2, c1],
+ [b, 2, c1],
+ (t > 1) ? [b, 0, c0] : []
+ ),
+
+ (f, t, a, b, c0, c1) => [a, 0, c0, b, 0, c1] // 2 = Z
+];
+
+const brick = [
+ (f, t, a, b, c0, c1) => [].concat( // 0
+ (f === 0) ? [a, 5, c0] :
+ (f === 1) ? [a, 1, c1] : [],
+ [a, 6, c0],
+ (t === 0) ? [b, 5, c0] :
+ [b, 4, c0],
+ (t === 3) ? [b, 0, c0] : []
+ ),
+ (f, t, a, b, c0, c1) => [].concat( // 1
+ (f === 0) ? [a, 4, c0, a, 3, c1] :
+ (f === 1) ? [a, 2, c1] :
+ (f === 2) ? [a, 3, c1] :
+ [a, 0, c0, a, 3, c1],
+ (t === 1) ? [b, 3, c1] :
+ [b, 1, c1],
+ (t === 3) ? [b, 0, c0] : []
+ ),
+ (f, t, a, b, c0, c1) => [].concat( // 2
+ (f === 0) ? [a, 4, 0] :
+ (f === 1) ? [a, 1, 0] :
+ [a, 0, 0],
+
+ (t === 0) ? [b, 6, 0, b, 6, c0] :
+ (t === 1) ? [b, 3, 0, b, 3, c1, b, 4, c0] :
+ [b, 0, 0, b, 0, c0, b, 4, c0],
+
+ (f === 0) ? [a, 4, c0, a, 3, c1] :
+ (f === 1) ? [a, 6, c0, a, 1, c1] :
+ [a, 6, c0, a, 0, c0, a, 3, c1],
+
+ (t === 0) ? [b, 1, c1, b, 6, c0] :
+ (t === 1) ? [b, 3, c1] :
+ [b, 1, c1, b, 0, c0]
+ )
+];
+
+
+// switch (val) {
+// case 0:
+// if (i === 0) {
+// if (y1 === y2) {
+// vertices.push(
+// t1, y1ShiftIndex, renderParam1.color, 0,
+// t1, y1ShiftIndex, renderParam1.color, 4
+// );
+// }
+// }
+
+// break;
+// case 1: // 0 1
+// vertices.push(...bar[val](f[1], t[1], t1, t2, 2, 3));
+// break;
+// case 2: case 3: // x X
+// vertices.push(...bar[2](f[1], t[1], t1, t2, 4, 4));
+// break;
+// case 4: case 5: // z Z
+// vertices.push(...bar[2](f[1], t[1], t1, t2, 1, 1));
+// break;
+// case 6: case 7: // u U uninitialized
+// vertices.push(...bar[2](f[1], t[1], t1, t2, 6, 6));
+// break;
+// case 8: case 9: // w W weak unknown
+// vertices.push(...bar[2](f[1], t[1], t1, t2, 10, 10));
+// break;
+// case 10: case 11: // l L
+// vertices.push(...bar[0](f[1], t[1], t1, t2, 8, 9));
+// break;
+// case 12: case 13: // h H
+// vertices.push(...bar[1](f[1], t[1], t1, t2, 8, 9));
+// break;
+// default:
+// vertices.push(...bar[2](f[1], t[1], t1, t2, 7, 7));
+// }
\ No newline at end of file
diff --git a/public/vcd.css b/public/vcd.css
index 2fbc8d9..8bd3c58 100644
--- a/public/vcd.css
+++ b/public/vcd.css
@@ -7,6 +7,9 @@
--sidebar-width: 280px;
--right-nav-width: 60px;
--time-scale-height: 50px;
+ --sidebar-padding: 10px;
+ --vcd-render-padding: 24px;
+ /* 需要满足如下公式 --time-scale-height + --sidebar-padding = --vcd-render-padding + canvas预留 高度 */
--render-scale-x: 1;
}
diff --git a/src/App.vue b/src/App.vue
index e62f431..3dd30de 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -57,7 +57,11 @@ export default {
document.body.style.setProperty('--el-color-info-light-8', 'var(--vscode-focusBorder)');
document.body.style.setProperty('--el-bg-color-overlay', 'transplant');
// document.body.style.setProperty('--el-color-white', 'var(--background)');
-
+
+ // 设置全局宏
+ document.body.style.setProperty('--time-scale-height', '50px');
+ document.body.style.setProperty('--sidebar-padding', '10px');
+ document.body.style.setProperty('--vcd-render-padding', '24px');
// signal height
document.body.style.setProperty('--display-signal-info-height', globalSetting.displaySignalHeight + 'px');
diff --git a/src/components/render/cursor.vue b/src/components/render/cursor.vue
index 4046632..a00050f 100644
--- a/src/components/render/cursor.vue
+++ b/src/components/render/cursor.vue
@@ -59,7 +59,7 @@ export default {
* @param { PointerEvent } event
*/
function makeCursor(event) {
- console.log(event.x);
+ // console.log(event.x);
showFixedOne.value = true;
fixedLeft.value = event.x - boxShift;
globalLookup.view.currentX = event.x - boxShift;
diff --git a/src/components/render/index.vue b/src/components/render/index.vue
index d7ca9a5..b20a5de 100644
--- a/src/components/render/index.vue
+++ b/src/components/render/index.vue
@@ -60,20 +60,20 @@ export default {
.vcd-view {
position: absolute;
- top: 24px;
- bottom: 24px;
+ top: var(--vcd-render-padding);
+ bottom: var(--vcd-render-padding);
}
.vcd-values {
position: absolute;
- top: 24px;
- bottom: 24px;
+ top: var(--vcd-render-padding);
+ bottom: var(--vcd-render-padding);
}
.wd-waveql {
position: absolute;
- top: 24px;
- bottom: 24px;
+ top: var(--vcd-render-padding);
+ bottom: var(--vcd-render-padding);
width: 100%;
transition: width .3s ease-out;
}
diff --git a/src/components/right-nav.vue b/src/components/right-nav.vue
index cfcd1a3..03fe5dd 100644
--- a/src/components/right-nav.vue
+++ b/src/components/right-nav.vue
@@ -18,7 +18,7 @@
+ >
>} wave
* @param {number} time
*/
-function findCurrentSignalValue(wave, time) {
+function findCurrentSignalValue(kind, wave, time) {
const times = wave.map(p => p[0]);
// 二分查找,并将结果存入 i
@@ -185,7 +195,7 @@ function findCurrentSignalValue(wave, time) {
if (times[i] === time) {
break;
}
- if (times[j] === time) {
+ if (times[j] <= time) {
i = j;
break;
}
@@ -200,8 +210,7 @@ function findCurrentSignalValue(wave, time) {
}
}
- const value = getWireValueCaption(wave[i][1], wave[i][2]);
- console.log(wave[i][1], wave[i][2]);
+ const value = getWireValueCaption(kind, wave[i][1], wave[i][2]);
return value;
}
diff --git a/src/hook/wave-view/dom-container.js b/src/hook/wave-view/dom-container.js
index 5334fe8..9d5b384 100644
--- a/src/hook/wave-view/dom-container.js
+++ b/src/hook/wave-view/dom-container.js
@@ -31,7 +31,7 @@ const mouseMoveHandler = (cursor, content, pstate /* , render */) => {
const getFullView = desc => {
- console.log(desc);
+ // console.log(desc);
if (desc.waveql) {
return;
}
diff --git a/src/hook/wave-view/gen-on-wheel.js b/src/hook/wave-view/gen-on-wheel.js
index b4c9190..eb1e9f2 100644
--- a/src/hook/wave-view/gen-on-wheel.js
+++ b/src/hook/wave-view/gen-on-wheel.js
@@ -1,30 +1,38 @@
'use strict';
const genOnWheel = (element, pstate, deso, keyBindo, plugins) =>
- event => {
- const {deltaY} = event;
- if (event.ctrlKey) {
- const key = (deltaY < 0)
- ? 'Ctrl+icon:scrollUp'
- : ((deltaY > 0) ? 'Ctrl+icon:scrollDown' : 'nop');
- if (keyBindo[key].fn(pstate)) {
- if (plugins != undefined) {
- plugins.map(fn => fn(key, event));
+ event => {
+ const { deltaX, deltaY } = event;
+ if (event.ctrlKey) {
+ const key = (deltaY < 0)
+ ? 'Ctrl+icon:scrollUp'
+ : ((deltaY > 0) ? 'Ctrl+icon:scrollDown' : 'nop');
+ if (keyBindo[key].fn(pstate)) {
+ if (plugins != undefined) {
+ plugins.map(fn => fn(key, event));
+ }
+ deso.render();
+ }
+ event.preventDefault();
+ } else if (event.shiftKey) {
+ const key = (deltaY < 0) ? 'Shift+icon:scrollUp' : ((deltaY > 0) ? 'Shift+icon:scrollDown' : 'nop');
+ if (keyBindo[key].fn(pstate)) {
+ if (plugins != undefined) {
+ plugins.map(fn => fn(key, event));
+ }
+ deso.render();
+ }
+ event.preventDefault();
+ } else if (deltaX !== 0 && deltaY === 0) {
+ const key = (deltaX < 0) ? 'icon:scrollLeft': 'icon:scrollRight';
+ if (keyBindo[key].fn(pstate)) {
+ if (plugins != undefined) {
+ plugins.map(fn => fn(key, event));
+ }
+ deso.render();
+ }
+ event.preventDefault();
}
- deso.render();
- }
- event.preventDefault();
- } else
- if (event.shiftKey) {
- const key = (deltaY < 0) ? 'Shift+icon:scrollUp' : ((deltaY > 0) ? 'Shift+icon:scrollDown' : 'nop');
- if (keyBindo[key].fn(pstate)) {
- if (plugins != undefined) {
- plugins.map(fn => fn(key, event));
- }
- deso.render();
- }
- event.preventDefault();
- }
- };
+ };
module.exports = genOnWheel;
diff --git a/src/hook/wave-view/gen-render-waves-gl.js b/src/hook/wave-view/gen-render-waves-gl.js
index 239edbb..8a09552 100644
--- a/src/hook/wave-view/gen-render-waves-gl.js
+++ b/src/hook/wave-view/gen-render-waves-gl.js
@@ -1,29 +1,32 @@
'use strict';
-const cColors = new Float32Array([
- 0, 0, 0, 0, // 0:
- 0, 0, 1, 1, // 1: (Z) high impedance
+// 控制颜色
+const gl_Colors = new Float32Array([
+ 0, 0, 0, 0, // 0: 空
- 0.2, 0.847, 0.1, 1, // 2: strong 0
- 0.2, 0.847, 0.1, 1, // 3: strong 1
- 0.9, 0.2, 0.2, 1, // 4: (x X) strong unknown
+ 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
- .5, 1, 1, 1, // 5: vec
- 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, 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, 0, 0, // 11:
- 0, 0, 0, 0, // 12:
- 0, 0, 0, 0, // 13:
- 0, 0, 0, 0, // 14:
- 0, 0, 0, 0 // 15:
+ 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 cTilts = new Float32Array([ // 14
+
+// 控制方向
+const gl_Shifts = new Float32Array([ // 14
0, 0, // 0
1, -1, // 1
1, 0, // 2
@@ -33,92 +36,79 @@ const cTilts = new Float32Array([ // 14
-1, 1 // 6
]);
-const shaderer = (kind, src) => webgl2 => {
- const vShader = webgl2.createShader(webgl2[kind]);
- webgl2.shaderSource(vShader, src);
- webgl2.compileShader(vShader);
- return vShader;
-};
+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 vertexShaderScalar = shaderer('VERTEX_SHADER', `#version 300 es
-in uvec3 pos;
+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 tilts[7];
-uniform float tilt;
+uniform vec2 shifts[7]; // 基础八位图偏移量,为了性能,pos 只传入整数,需要的坐标负数由该值提供
+uniform vec2 widthShifts[8]; // 用于构造线宽的偏移
+
void main() {
v_color = colors[pos.z];
- vec2 node = tilts[pos.y];
+ vec2 shift = shifts[pos.y];
+ vec2 widthShift = widthShifts[pos.w];
gl_Position = vec4(
- float(pos.x) * scale.x + offset.x + node[1] * tilt,
- float(node[0]) * scale.y + offset.y,
+ float(pos.x) * scale.x + offset.x + float(widthShift.x),
+ float(shift.x) * scale.y + offset.y + float(widthShift.y),
1, 1
);
-}
-`);
+}`);
-const fragmentShader = shaderer('FRAGMENT_SHADER', `#version 300 es
+const fragmentShader = new ShaderMaker('FRAGMENT_SHADER', `#version 300 es
precision mediump float;
in vec4 v_color;
-out vec4 myOutputColor;
+out vec4 outColor;
void main() {
- myOutputColor = v_color;
-}
-`);
-
-
-const bar = [
- (f, t, a, b, c0, c1) => [].concat( // 0
- (f === 1) ? [a, 2, c1] : [],
- [a, 5, c0],
- [b, 5, c0],
- (t > 1) ? [b, 0, c0] : []
- ),
- (f, t, a, b, c0, c1) => [].concat( // 1
- (f === 0) ? [a, 5, c0] :
- (f > 1) ? [a, 0, c0] : [],
- [a, 2, c1],
- [b, 2, c1],
- (t > 1) ? [b, 0, c0] : []
- ),
- (f, t, a, b, c0, c1) => [a, 0, c0, b, 0, c1] // 2 = Z
-];
-
-const brick = [
- (f, t, a, b, c0, c1) => [].concat( // 0
- (f === 0) ? [a, 5, c0] :
- (f === 1) ? [a, 1, c1] : [],
- [a, 6, c0],
- (t === 0) ? [b, 5, c0] :
- [b, 4, c0],
- (t === 3) ? [b, 0, c0] : []
- ),
- (f, t, a, b, c0, c1) => [].concat( // 1
- (f === 0) ? [a, 4, c0, a, 3, c1] :
- (f === 1) ? [a, 2, c1] :
- (f === 2) ? [a, 3, c1] :
- [a, 0, c0, a, 3, c1],
- (t === 1) ? [b, 3, c1] :
- [b, 1, c1],
- (t === 3) ? [b, 0, c0] : []
- ),
- (f, t, a, b, c0, c1) => [].concat( // 2
- (f === 0) ? [a, 4, 0] :
- (f === 1) ? [a, 1, 0] :
- [a, 0, 0],
- (t === 0) ? [b, 6, 0, b, 6, c0] :
- (t === 1) ? [b, 3, 0, b, 3, c1, b, 4, c0] :
- [b, 0, 0, b, 0, c0, b, 4, c0],
- (f === 0) ? [a, 4, c0, a, 3, c1] :
- (f === 1) ? [a, 6, c0, a, 1, c1] :
- [a, 6, c0, a, 0, c0, a, 3, c1],
- (t === 0) ? [b, 1, c1, b, 6, c0] :
- (t === 1) ? [b, 3, c1] :
- [b, 1, c1, b, 0, c0]
- )
-];
+ outColor = v_color;
+}`);
class WebGL2WaveRender {
@@ -133,7 +123,8 @@ class WebGL2WaveRender {
* chango: Record,
- * vao: WebGLVertexArrayObject
+ * lineVao: WebGLVertexArrayObject
+ * maskVao: WebGLVertexArrayObject
* }>
* view: Array<{ ref: string }>,
* currentWires: Set<{
@@ -167,47 +158,49 @@ class WebGL2WaveRender {
this.pstate = pstate;
this.plugins = plugins;
- const webgl2 = canvas.getContext('webgl2', {
+ const gl = canvas.getContext('webgl2', {
premultipliedAlpha: false,
alpha: true,
antialias: false,
depth: false
});
- this.webglLocation = this.initProgram(webgl2);
- this.verticesMap = this.makeVertex();
- this.initData();
+ this.webglLocation = this.initProgram(gl);
+ const { lineVerticesMap, maskVerticesMap } = this.makeVertex();
+ this.lineVerticesMap = lineVerticesMap;
+ this.maskVerticesMap = maskVerticesMap;
+ this.initData();
this.animationHandler = undefined;
}
/**
*
- * @param {WebGL2RenderingContext} webgl2
+ * @param {WebGL2RenderingContext} gl
* @returns {{
* colors: WebGLUniformLocation,
- * tilts: WebGLUniformLocation,
+ * shifts: WebGLUniformLocation,
* scale: WebGLUniformLocation,
* offset: WebGLUniformLocation,
- * tilt: WebGLUniformLocation,
* pos: number,
- * webgl2: WebGL2RenderingContext
+ * widthShifts: WebGLUniformLocation,
+ * gl: WebGL2RenderingContext
* }}
*/
- initProgram(webgl2) {
- const program = webgl2.createProgram();
- webgl2.attachShader(program, vertexShaderScalar(webgl2));
- webgl2.attachShader(program, fragmentShader(webgl2));
- webgl2.linkProgram(program);
- webgl2.useProgram(program);
+ 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: webgl2.getUniformLocation(program, 'colors'),
- tilts: webgl2.getUniformLocation(program, 'tilts'),
- scale: webgl2.getUniformLocation(program, 'scale'),
- offset: webgl2.getUniformLocation(program, 'offset'),
- tilt: webgl2.getUniformLocation(program, 'tilt'),
- pos: webgl2.getAttribLocation(program, 'pos'),
- webgl2
+ 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;
@@ -215,68 +208,215 @@ class WebGL2WaveRender {
/**
*
- * @returns {Map}
+ * @returns {{
+ * lineVerticesMap: Map,
+ * maskVerticesMap: Map
+ * }}
*/
makeVertex() {
const globalLookup = this.globalLookup;
const time = globalLookup.time;
- const verticesMap = new Map();
+ 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 vertices = this.makeBitVertex(wave, time);
- verticesMap.set(id, vertices);
+ 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);
- verticesMap.set(id, vertices);
+ // 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;
+ }
}
}
- return verticesMap;
}
/**
*
* @param {Array} wave
* @param { number } time
- * @returns {Uint32Array}
+ * @returns {{
+ * lineVertices: Uint32Array
+ * maskVertices: Uint32Array
+ * }}
*/
- makeBitVertex(wave, time) {
- const vertices = [];
- const ilen = wave.length;
- for (let i = 0; i < ilen; i++) {
- const f = wave[(i === 0) ? 0 : (i - 1)];
- const [tim, val] = wave[i];
- const t = wave[(i === (ilen - 1)) ? i : (i + 1)];
- const tt = (i === (ilen - 1)) ? time : wave[i + 1][0];
- switch (val) {
- case 0: case 1: // 0 1
- vertices.push(...bar[val](f[1], t[1], tim, tt, 2, 3));
- break;
- case 2: case 3: // x X
- vertices.push(...bar[2](f[1], t[1], tim, tt, 4, 4));
- break;
- case 4: case 5: // z Z
- vertices.push(...bar[2](f[1], t[1], tim, tt, 1, 1));
- break;
- case 6: case 7: // u U uninitialized
- vertices.push(...bar[2](f[1], t[1], tim, tt, 6, 6));
- break;
- case 8: case 9: // w W weak unknown
- vertices.push(...bar[2](f[1], t[1], tim, tt, 10, 10));
- break;
- case 10: case 11: // l L
- vertices.push(...bar[0](f[1], t[1], tim, tt, 8, 9));
- break;
- case 12: case 13: // h H
- vertices.push(...bar[1](f[1], t[1], tim, tt, 8, 9));
- break;
- default:
- vertices.push(...bar[2](f[1], t[1], tim, tt, 7, 7));
+ 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 });
+ }
}
}
- return new Uint32Array(vertices);
+ // 确保最后一个点延申到了 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
+ };
}
/**
@@ -287,13 +427,24 @@ class WebGL2WaveRender {
*/
makeVecVertex(wave, time) {
const vertices = [];
- const ilen = wave.length;
- for (let i = 0; i < ilen; i++) {
+ const length = wave.length;
+ for (let i = 0; i < length; ++ i) {
const [t1, val, msk] = wave[i];
- const t2 = (i === (ilen - 1)) ? time : wave[i + 1][0];
+ const t2 = (i === (length - 1)) ? time : wave[i + 1][0];
+ // t1(val) --- t2(val)
if (msk) {
- vertices.push(...brick[2](2, 2, t1, t2, 4, 4)); // x,z?
+ 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,
@@ -315,26 +466,48 @@ class WebGL2WaveRender {
initData() {
const webglLocation = this.webglLocation;
- const webgl2 = webglLocation.webgl2;
+ const gl = webglLocation.gl;
const globalLookup = this.globalLookup;
- const verticesMap = this.verticesMap;
+ const lineVerticesMap = this.lineVerticesMap;
+ const maskVerticesMap = this.maskVerticesMap;
for (const id of Reflect.ownKeys(globalLookup.chango)) {
const signalItem = globalLookup.chango[id];
- const vertices = verticesMap.get(id);
+
+ const lineVertices = lineVerticesMap.get(id);
+ if (lineVertices === undefined) {
+ // console.warn(`无法找到 link 为 ${id} 的顶点数据`);
+ continue;
+ }
- const vertexBuffer = webgl2.createBuffer();
- webgl2.bindBuffer(webgl2.ARRAY_BUFFER, vertexBuffer);
- // 将初始化的顶点数据复制到 buffer 区域
- webgl2.bufferData(webgl2.ARRAY_BUFFER, vertices, webgl2.STATIC_DRAW);
-
- signalItem.vao = webgl2.createVertexArray();
- webgl2.bindVertexArray(signalItem.vao);
- webgl2.vertexAttribIPointer(webglLocation.pos, 3, webgl2.UNSIGNED_INT, 0, 0);
- webgl2.enableVertexAttribArray(webglLocation.pos);
- webgl2.uniform4fv(webglLocation.colors, cColors);
- webgl2.uniform2fv(webglLocation.tilts, cTilts);
+ // 创建并设置 绘制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() {
@@ -344,42 +517,74 @@ class WebGL2WaveRender {
const canvas = this.canvas;
const webglLocation = this.webglLocation;
- const webgl2 = webglLocation.webgl2;
+ const gl = webglLocation.gl;
const globalLookup = this.globalLookup;
- const verticesMap = this.verticesMap;
+ 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 - 40;
- canvas.width = width;
+ const canvasHeight = height - canvasPaddingTop;
+ const canvasWidth = width;
+
+ // 默认 1594
+ canvas.width = canvasWidth;
+
+ // 默认 1260
canvas.height = canvasHeight;
-
+
// 设置 glsl 变量
- webgl2.uniform1f(webglLocation.tilt, 3 / width);
- webgl2.uniform2f(webglLocation.scale, 2 * xScale / width, yStep * yDuty / canvasHeight);
+ gl.uniform2f(webglLocation.scale,
+ 2 * xScale / canvasWidth,
+ yStep * yDuty / canvasHeight
+ );
+ console.log(yStep, yDuty, canvasHeight);
// 设置 webgl 和 canvas 大小位置一致
- webgl2.viewport(0, 0, width, canvasHeight);
+ gl.viewport(0, 0, canvasWidth, canvasHeight);
// 清楚颜色缓冲区,也就是删除上一次的渲染结果
- webgl2.clear(webgl2.COLOR_BUFFER_BIT);
+ 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;
}
- webgl2.bindVertexArray(signalItem.vao);
- webgl2.uniform2f(webglLocation.offset,
+ // TODO: 将此处的 offset 计算中的参数和 globalSetting 的数字形成关联
+ gl.uniform2f(webglLocation.offset,
(2 * xOffset / width) - 1,
(2 * yOffset - 2 * yStep * (index + .7)) / canvasHeight + 1
);
- // 根据 vao 进行绘制
- const vertices = verticesMap.get(signal.link);
- // console.log(vertices);
- webgl2.drawArrays(webgl2.LINE_STRIP, 0, vertices.length / 3);
+
+
+ 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 ++;
}
diff --git a/src/hook/wave-view/gen-resize-handler.js b/src/hook/wave-view/gen-resize-handler.js
index 954c0c5..7cd2cdf 100644
--- a/src/hook/wave-view/gen-resize-handler.js
+++ b/src/hook/wave-view/gen-resize-handler.js
@@ -18,12 +18,8 @@ const genResizeHandler = pstate =>
yOffset = yOffsetMax;
}
pstate.yOffset = yOffset;
- // console.log('resize handler', pstate);
- // X
- // const xScaleMin = pstate.xScaleMin = (width - sidebarWidth) / time;
+
const xScaleMin = pstate.xScaleMin = 0.001;
- console.log(width, sidebarWidth);
- console.log('xScaleMin', xScaleMin);
pstate.xScale = (xScale < xScaleMin) ? xScaleMin : xScale;
xOffsetUpdate(pstate, xOffset);
};
diff --git a/src/hook/wave-view/get-label.js b/src/hook/wave-view/get-label.js
index c29d318..4e5abcb 100644
--- a/src/hook/wave-view/get-label.js
+++ b/src/hook/wave-view/get-label.js
@@ -11,7 +11,6 @@ const getLabel = (lane) => {
const formatter = format(fmt, width);
return (value, mask, x, w) => {
- console.log(value, mask, x, w);
if (mask) {
if (value) {
diff --git a/src/hook/wave-view/key-bindo.js b/src/hook/wave-view/key-bindo.js
index b8b51e3..d674482 100644
--- a/src/hook/wave-view/key-bindo.js
+++ b/src/hook/wave-view/key-bindo.js
@@ -52,9 +52,8 @@ const editable = {
module.exports = {
// Alt + <, >. left / right
- 'Alt+,': scroll.left, 'Shift+icon:scrollUp': scroll.left,
-
- 'Alt+.': scroll.right, 'Shift+icon:scrollDown': scroll.right,
+ 'Alt+,': scroll.left,
+ 'Alt+.': scroll.right,
// Alt + [ ] home / end
'Alt+[': { desc: 'Jump to beginning of time', fn: pstate => xOffsetUpdate(pstate, pstate.sidebarWidth) }, // Home
@@ -63,15 +62,18 @@ module.exports = {
// ALT + - +
'Alt+=': pluso, // '+': pluso, '=': pluso,
- 'Ctrl+icon:scrollUp': pluso,
-
'Alt+-': minuso, // '-': minuso, '_': minuso,
- 'Ctrl+icon:scrollDown': minuso,
-
'Alt+0': fullo, // 'Shift+f': fullo, F: fullo, 'Shift+F': fullo,
-
'Alt+/': editable,
+ // wheel
+ 'icon:scrollLeft': scroll.left,
+ 'icon:scrollRight': scroll.right,
+ 'Shift+icon:scrollUp': scroll.left,
+ 'Shift+icon:scrollDown': scroll.right,
+ 'Ctrl+icon:scrollUp': pluso,
+ 'Ctrl+icon:scrollDown': minuso,
+
// CAN'T DO: Alt + e, d, f, l
// ArrowUp: scroll.up, 'Shift+ArrowUp': scroll.up,
diff --git a/src/hook/wave-view/render-values.js b/src/hook/wave-view/render-values.js
index 999d9d5..d7a0f83 100644
--- a/src/hook/wave-view/render-values.js
+++ b/src/hook/wave-view/render-values.js
@@ -91,7 +91,7 @@ function* renderValues(desc, pstate) {
const [tCur, vCur, mCur] = (mark || [desc.time, 0, 0]);
const xCur = getX(pstate, tCur);
- console.log(mark, vPre, mPre, vCur, mCur);
+ // console.log(mark, vPre, mPre, vCur, mCur);
if (vPre !== undefined || mPre !== undefined) {
if (xPre > width && xCur > width) { // both time stamps to the right
@@ -111,9 +111,7 @@ function* renderValues(desc, pstate) {
}
xPre = xCur;
vPre = vCur;
- console.log(mPre, mCur);
mPre = mCur;
- console.log(mPre, mCur);
}
}
ml.push(mLane);
diff --git a/src/hook/wave-view/water.js b/src/hook/wave-view/water.js
index 5cf7359..1ac7b88 100644
--- a/src/hook/wave-view/water.js
+++ b/src/hook/wave-view/water.js
@@ -33,7 +33,7 @@ const progress = (lane, desc, pstate) => {
if (desc.chango[clock.ref] === undefined) {
- console.log(desc.chango, clock, clock.ref);
+ // console.log(desc.chango, clock, clock.ref);
return pco;
}
const clockWave = desc.chango[clock.ref].wave;
diff --git a/src/i18n/index.js b/src/i18n/index.js
index e291d8e..1ef008b 100644
--- a/src/i18n/index.js
+++ b/src/i18n/index.js
@@ -6,6 +6,7 @@ import zh from './zh.json';
const i18n = createI18n({
legacy: false,
locale: 'en',
+ warnHtmlMessage: false,
messages: { en, zh }
});
diff --git a/test/render-line/delaunator.min.js b/test/render-line/delaunator.min.js
new file mode 100644
index 0000000..1f63da5
--- /dev/null
+++ b/test/render-line/delaunator.min.js
@@ -0,0 +1 @@
+!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).Delaunator=i()}(this,(function(){"use strict";const t=134217729;function i(t,i,s,e,n){let h,r,l,o,a=i[0],f=e[0],c=0,u=0;f>a==f>-a?(h=a,a=i[++c]):(h=f,f=e[++u]);let _=0;if(ca==f>-a?(r=a+h,l=h-(r-a),a=i[++c]):(r=f+h,l=h-(r-f),f=e[++u]),h=r,0!==l&&(n[_++]=l);ca==f>-a?(r=h+a,o=r-h,l=h-(r-o)+(a-o),a=i[++c]):(r=h+f,o=r-h,l=h-(r-o)+(f-o),f=e[++u]),h=r,0!==l&&(n[_++]=l);for(;c0!=d>0)return g;const y=Math.abs(_+d);return Math.abs(g)>=33306690738754716e-32*y?g:-function(s,o,a,f,c,u,_){let d,g,y,w,b,A,k,M,p,x,S,T,z,U,m,K,L,v;const F=s-c,P=a-c,E=o-u,H=f-u;U=F*H,A=t*F,k=A-(A-F),M=F-k,A=t*H,p=A-(A-H),x=H-p,m=M*x-(U-k*p-M*p-k*x),K=E*P,A=t*E,k=A-(A-E),M=E-k,A=t*P,p=A-(A-P),x=P-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,e[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,e[1]=z-(S+b)+(b-K),v=T+S,b=v-T,e[2]=T-(v-b)+(S-b),e[3]=v;let I=function(t,i){let s=i[0];for(let e=1;e=N||-I>=N)return I;if(b=s-F,d=s-(F+b)+(b-c),b=a-P,y=a-(P+b)+(b-c),b=o-E,g=o-(E+b)+(b-u),b=f-H,w=f-(H+b)+(b-u),0===d&&0===g&&0===y&&0===w)return I;if(N=11093356479670487e-47*_+33306690738754706e-32*Math.abs(I),I+=F*w+H*d-(E*y+P*g),I>=N||-I>=N)return I;U=d*H,A=t*d,k=A-(A-d),M=d-k,A=t*H,p=A-(A-H),x=H-p,m=M*x-(U-k*p-M*p-k*x),K=g*P,A=t*g,k=A-(A-g),M=g-k,A=t*P,p=A-(A-P),x=P-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,l[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,l[1]=z-(S+b)+(b-K),v=T+S,b=v-T,l[2]=T-(v-b)+(S-b),l[3]=v;const j=i(4,e,4,l,n);U=F*w,A=t*F,k=A-(A-F),M=F-k,A=t*w,p=A-(A-w),x=w-p,m=M*x-(U-k*p-M*p-k*x),K=E*y,A=t*E,k=A-(A-E),M=E-k,A=t*y,p=A-(A-y),x=y-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,l[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,l[1]=z-(S+b)+(b-K),v=T+S,b=v-T,l[2]=T-(v-b)+(S-b),l[3]=v;const q=i(j,n,4,l,h);U=d*w,A=t*d,k=A-(A-d),M=d-k,A=t*w,p=A-(A-w),x=w-p,m=M*x-(U-k*p-M*p-k*x),K=g*y,A=t*g,k=A-(A-g),M=g-k,A=t*y,p=A-(A-y),x=y-p,L=M*x-(K-k*p-M*p-k*x),S=m-L,b=m-S,l[0]=m-(S+b)+(b-L),T=U+S,b=T-U,z=U-(T-b)+(S-b),S=z-K,b=z-S,l[1]=z-(S+b)+(b-K),v=T+S,b=v-T,l[2]=T-(v-b)+(S-b),l[3]=v;const D=i(q,h,4,l,r);return r[D-1]}(s,o,a,f,c,u,y)}const a=Math.pow(2,-52),f=new Uint32Array(512);class c{static from(t,i=w,s=b){const e=t.length,n=new Float64Array(2*e);for(let h=0;h>1;if(i>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const s=Math.max(2*i-5,0);this._triangles=new Uint32Array(3*s),this._halfedges=new Int32Array(3*s),this._hashSize=Math.ceil(Math.sqrt(i)),this._hullPrev=new Uint32Array(i),this._hullNext=new Uint32Array(i),this._hullTri=new Uint32Array(i),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(i),this._dists=new Float64Array(i),this.update()}update(){const{coords:t,_hullPrev:i,_hullNext:s,_hullTri:e,_hullHash:n}=this,h=t.length>>1;let r=1/0,l=1/0,f=-1/0,c=-1/0;for(let i=0;if&&(f=s),e>c&&(c=e),this._ids[i]=i}const _=(r+f)/2,y=(l+c)/2;let w,b,A,k=1/0;for(let i=0;i0&&(b=i,k=s)}let x=t[2*b],S=t[2*b+1],T=1/0;for(let i=0;ie&&(i[s++]=n,e=this._dists[n])}return this.hull=i.subarray(0,s),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(o(M,p,x,S,z,U)<0){const t=b,i=x,s=S;b=A,x=z,S=U,A=t,z=i,U=s}const m=function(t,i,s,e,n,h){const r=s-t,l=e-i,o=n-t,a=h-i,f=r*r+l*l,c=o*o+a*a,u=.5/(r*a-l*o);return{x:t+(a*f-l*c)*u,y:i+(r*c-o*f)*u}}(M,p,x,S,z,U);this._cx=m.x,this._cy=m.y;for(let i=0;i0&&Math.abs(c-h)<=a&&Math.abs(u-r)<=a)continue;if(h=c,r=u,f===w||f===b||f===A)continue;let _=0;for(let t=0,i=this._hashKey(c,u);t=0;)if(g=d,g===_){g=-1;break}if(-1===g)continue;let y=this._addTriangle(g,f,s[g],-1,-1,e[g]);e[f]=this._legalize(y+2),e[g]=y,K++;let k=s[g];for(;d=s[k],o(c,u,t[2*k],t[2*k+1],t[2*d],t[2*d+1])<0;)y=this._addTriangle(k,f,d,e[f],-1,e[k]),e[f]=this._legalize(y+2),s[k]=k,K--,k=d;if(g===_)for(;d=i[g],o(c,u,t[2*d],t[2*d+1],t[2*g],t[2*g+1])<0;)y=this._addTriangle(d,f,g,-1,e[g],e[d]),this._legalize(y+2),e[d]=y,s[g]=g,K--,g=d;this._hullStart=i[f]=g,s[g]=i[k]=f,s[f]=k,n[this._hashKey(c,u)]=f,n[this._hashKey(t[2*g],t[2*g+1])]=g}this.hull=new Uint32Array(K);for(let t=0,i=this._hullStart;t0?3-s:1+s)/4}(t-this._cx,i-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:i,_halfedges:s,coords:e}=this;let n=0,h=0;for(;;){const r=s[t],l=t-t%3;if(h=l+(t+2)%3,-1===r){if(0===n)break;t=f[--n];continue}const o=r-r%3,a=l+(t+1)%3,c=o+(r+2)%3,u=i[h],d=i[t],g=i[a],y=i[c];if(_(e[2*u],e[2*u+1],e[2*d],e[2*d+1],e[2*g],e[2*g+1],e[2*y],e[2*y+1])){i[t]=y,i[r]=u;const e=s[c];if(-1===e){let i=this._hullStart;do{if(this._hullTri[i]===c){this._hullTri[i]=t;break}i=this._hullPrev[i]}while(i!==this._hullStart)}this._link(t,e),this._link(r,s[h]),this._link(h,c);const l=o+(r+1)%3;n=s&&i[t[r]]>h;)t[r+1]=t[r--];t[r+1]=e}else{let n=s+1,h=e;y(t,s+e>>1,n),i[t[s]]>i[t[e]]&&y(t,s,e),i[t[n]]>i[t[e]]&&y(t,n,e),i[t[s]]>i[t[n]]&&y(t,s,n);const r=t[n],l=i[r];for(;;){do{n++}while(i[t[n]]l);if(h=h-s?(g(t,i,n,e),g(t,i,s,h-1)):(g(t,i,s,h-1),g(t,i,n,e))}}function y(t,i,s){const e=t[i];t[i]=t[s],t[s]=e}function w(t){return t[0]}function b(t){return t[1]}return c}));
diff --git a/test/render-line/fragment.shader.glsl b/test/render-line/fragment.shader.glsl
new file mode 100644
index 0000000..5301de0
--- /dev/null
+++ b/test/render-line/fragment.shader.glsl
@@ -0,0 +1,9 @@
+#version 300 es
+
+precision mediump float;
+uniform vec4 a_color;
+out vec4 outColor;
+
+void main(){
+ outColor = a_color;
+}
\ No newline at end of file
diff --git a/test/render-line/index.html b/test/render-line/index.html
new file mode 100644
index 0000000..fb16177
--- /dev/null
+++ b/test/render-line/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/render-line/main.js b/test/render-line/main.js
new file mode 100644
index 0000000..00555e4
--- /dev/null
+++ b/test/render-line/main.js
@@ -0,0 +1,278 @@
+/**
+ * @description 创建着色器对象
+ * @param {WebGL2RenderingContext} gl
+ * @param {'VERTEX_SHADER' | 'FRAGMENT_SHADER'} type
+ * @param {string} source
+ * @return { WebGLShader | undefined }
+ */
+function makeShader(gl, type, source) {
+ // 创建 shader
+ const shader = gl.createShader(gl[type]);
+ // 给 shader 提供 glsl 代码
+ gl.shaderSource(shader, source);
+ // 编译 glsl 生成 shader
+ gl.compileShader(shader);
+ const ok = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
+ // 如果成功,则返回 shader,否则返回 undefined
+ return ok ? shader : gl.deleteShader(shader);
+}
+
+
+/**
+ *
+ * @param {string} path
+ * @return {string}
+ */
+async function getSource(path) {
+ const response = await fetch(path);
+ const source = await response.text();
+ return source;
+}
+
+
+window.onload = async () => {
+ const canvas = document.getElementById('canvas');
+ const gl = canvas.getContext('webgl2');
+
+ if (!(gl instanceof WebGL2RenderingContext)) {
+ console.log('浏览器不支持 webgl2');
+ return;
+ }
+
+ const vertexSource = await getSource('./vertex.shader.glsl');
+ const fragementSource = await getSource('./fragment.shader.glsl');
+ const vertexShader = makeShader(gl, 'VERTEX_SHADER', vertexSource);
+ const fragmentShader = makeShader(gl, 'FRAGMENT_SHADER', fragementSource);
+ if (!vertexShader || !fragmentShader) {
+ console.log('着色器创建失败');
+ return;
+ }
+
+ const program = createProgram(gl, vertexShader, fragmentShader);
+ if (!program) {
+ console.log('着色程序创建失败');
+ return;
+ }
+
+ gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ const points = [
+ 0, -0.5,
+ 0.5, -0.5,
+ ];
+
+ drawMask(gl, program, points, { width: 0.06 });
+ drawLine2D(gl, program, points, { width: 0.06 });
+}
+
+function onTheSameLine(x0, y0, x1, y1, x2, y2) {
+ const slope1 = (x1 - x0) !== 0 ? (y1 - y0) / (x1 - x0) : Infinity;
+ const slope2 = (x2 - x0) !== 0 ? (y2 - y0) / (x2 - x0) : Infinity;
+
+ return slope1 === slope2;
+}
+
+function sgn(value) {
+ if (value === 0) return 0;
+ return value > 0 ? 1 : -1;
+}
+
+/**
+ *
+ * @param {WebGL2RenderingContext} gl
+ * @param {WebGLProgram} program
+ * @param {number[]} points
+ * @param {{
+ * width: number
+ * color: string
+ * }} config
+ */
+function drawLine2D(gl, program, points, config) {
+ const pointNum = points.length >> 1;
+ const w = config.width;
+ const shift = w / 2;
+ const positions = [];
+ for (let i = 0; i < pointNum; ++ i) {
+ const x1 = points[(i << 1)];
+ const y1 = points[(i << 1) + 1];
+
+ if (i === 0) {
+ const x2 = points[((i + 1) << 1)];
+ const y2 = points[((i + 1) << 1) + 1];
+ if (y1 === y2) {
+ // console.log('enter i = 0');
+ positions.push(
+ x1, y1 + shift, // v0
+ x1, y1 - shift // v1
+ );
+ } else if (x1 === x2) {
+ positions.push(
+ x1 + shift, y1, // v1
+ x1 - shift, y1, // v0
+ );
+ }
+ } else if (i === pointNum - 1) {
+ const x0 = points[((i - 1) << 1)];
+ const y0 = points[((i - 1) << 1) + 1];
+ if (y1 === y0) {
+ // console.log('enter i = pointNum - 1');
+ positions.push(
+ x1, y1 + shift, // v0
+ x1, y1 - shift // v1
+ );
+ } else if (x1 === x0) {
+ positions.push(
+ x1 + shift, y1, // v1
+ x1 - shift, y1, // v0
+ );
+ }
+ } else {
+ const x0 = points[((i - 1) << 1)];
+ const y0 = points[((i - 1) << 1) + 1];
+ const x2 = points[((i + 1) << 1)];
+ const y2 = points[((i + 1) << 1) + 1];
+
+ if (x0 !== x1 && y0 === y1 && x1 === x2 && y1 !== y2) {
+ const d = sgn(y2 - y1);
+ if (y2 > y1) {
+ positions.push(
+ x1 - shift, y1 + shift,
+ x1 + shift, y1 - shift
+ );
+ } else {
+ positions.push(
+ x1 + shift, y1 + shift,
+ x1 - shift, y1 - shift,
+ );
+ }
+
+ } else if (x0 === x1 && y0 !== y1 && x1 !== x2 && y1 === y2) {
+ const d = sgn(y1 - y0);
+ if (y1 > y0) {
+ positions.push(
+ x1 - shift, y1 + shift,
+ x1 + shift, y1 - shift,
+ );
+ } else {
+ positions.push(
+ x1 + shift, y1 + shift,
+ x1 - shift, y1 - shift,
+ );
+ }
+
+ }
+ }
+ }
+
+ const positionFloat32 = new Float32Array(positions);
+ const buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, positionFloat32, gl.STATIC_DRAW);
+ gl.useProgram(program);
+ const positionLocation = gl.getAttribLocation(program, 'a_position');
+ const colorLocation = gl.getUniformLocation(program, 'a_color');
+ gl.uniform4f(colorLocation, 0.4, 0.0, 1.0, 1.0);
+ gl.enableVertexAttribArray(positionLocation);
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, positionFloat32.length >> 1);
+}
+
+
+
+
+/**
+ *
+ * @param {WebGL2RenderingContext} gl
+ * @param {WebGLProgram} program
+ * @param {number[]} times
+ * @param {{
+* width: number
+* color: string
+* }} config
+* @param {number} yshift
+*/
+function drawBlock(gl, program, times, config, yshift) {
+
+}
+
+/**
+ *
+ * @param {WebGL2RenderingContext} gl
+ * @param {WebGLProgram} program
+ * @param {number[]} points
+ * @param {{
+* width: number
+* color: string
+* }} config
+*/
+function drawMask(gl, program, points, config) {
+ const positions = [];
+ const pointNum = points.length >> 1;
+ const w = config.width;
+ const shift = w / 2;
+ for (let i = 0; i < pointNum; ++ i) {
+ const x1 = points[(i << 1)];
+ const y1 = points[(i << 1) + 1];
+ if (i + 1 < pointNum) {
+ const x2 = points[((i + 1) << 1)];
+ const y2 = points[((i + 1) << 1) + 1];
+ if (y2 > y1 && i + 2 < pointNum) {
+ const x3 = points[((i + 2) << 1)];
+ const y3 = points[((i + 2) << 1) + 1];
+ const p1 = [x1, y1 - shift];
+ const p2 = [x2, y2 - shift];
+ const p3 = [x3, y3 - shift];
+ const p4 = [x3, y1 - shift];
+
+ positions.push(
+ ...p1,
+ ...p2,
+ ...p3,
+ ...p1,
+ ...p3,
+ ...p4
+ );
+ }
+ }
+ }
+
+ const positionFloat32 = new Float32Array(positions);
+ const buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, positionFloat32, gl.STATIC_DRAW);
+ gl.useProgram(program);
+
+ const aPostionLocation = gl.getAttribLocation(program, 'a_position');
+ const colorLocation = gl.getUniformLocation(program, 'a_color');
+ gl.uniform4f(colorLocation, 0.4, 0.0, 1.0, 0.2);
+
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+
+ gl.enableVertexAttribArray(aPostionLocation);
+ gl.vertexAttribPointer(aPostionLocation, 2, gl.FLOAT, false, 0, 0);
+ gl.drawArrays(gl.TRIANGLES, 0, positionFloat32.length >> 1);
+}
+
+
+/**
+ * @description 创建着色器
+ * @param {WebGL2RenderingContext} gl
+ * @param {WebGLShader} vertexShader
+ * @param {WebGLShader} fragmentShader
+ * @return { WebGLProgram | undefined }
+ */
+function createProgram(gl, vertexShader, fragmentShader) {
+ // 创建着色程序
+ const program = gl.createProgram();
+ // 加载着色器到着色程序
+ gl.attachShader(program, vertexShader);
+ gl.attachShader(program, fragmentShader);
+ // 连接着色程序
+ gl.linkProgram(program);
+ const ok = gl.getProgramParameter(program, gl.LINK_STATUS);
+
+ return ok ? program : gl.deleteProgram(program);
+}
diff --git a/test/render-line/vertex.shader.glsl b/test/render-line/vertex.shader.glsl
new file mode 100644
index 0000000..181a6a8
--- /dev/null
+++ b/test/render-line/vertex.shader.glsl
@@ -0,0 +1,7 @@
+#version 300 es
+
+in vec4 a_position;
+
+void main() {
+ gl_Position = a_position;
+}
\ No newline at end of file
diff --git a/test/render-line/webgl.drawio b/test/render-line/webgl.drawio
new file mode 100644
index 0000000..11e7f20
--- /dev/null
+++ b/test/render-line/webgl.drawio
@@ -0,0 +1,655 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/shader/fragment.glsl b/test/shader/fragment.glsl
new file mode 100644
index 0000000..fcd4e6c
--- /dev/null
+++ b/test/shader/fragment.glsl
@@ -0,0 +1,7 @@
+#version 300 es
+precision mediump float;
+in vec4 v_color;
+out vec4 outColor;
+void main() {
+ outColor = v_color;
+}
\ No newline at end of file
diff --git a/test/vertex.glsl b/test/shader/vertex.glsl
similarity index 75%
rename from test/vertex.glsl
rename to test/shader/vertex.glsl
index 1878564..8d10330 100644
--- a/test/vertex.glsl
+++ b/test/shader/vertex.glsl
@@ -7,11 +7,11 @@ uniform vec4 colors[16];
uniform vec2 tilts[7];
uniform float tilt;
void main() {
- v_color = colors[pos.z];
- vec2 node = tilts[pos.y];
- gl_Position = vec4(
+ v_color = colors[pos.z];
+ vec2 node = tilts[pos.y];
+ gl_Position = vec4(
float(pos.x) * scale.x + offset.x + node[1] * tilt,
float(node[0]) * scale.y + offset.y,
1, 1
- );
+ );
}
\ No newline at end of file