adjust to webgl2

This commit is contained in:
锦恢 2024-03-24 04:31:33 +08:00
parent 284bb4bd1a
commit f200110407
47 changed files with 2927 additions and 114 deletions

239
package-lock.json generated
View File

@ -10,10 +10,14 @@
"dependencies": {
"core-js": "^3.8.3",
"element-plus": "^2.5.6",
"lodash.get": "^4.4.2",
"mitt": "^3.0.1",
"onml": "^2.1.0",
"stream": "^0.0.2",
"vue": "^3.2.13",
"vue-i18n": "^9.9.1"
"vue-i18n": "^9.9.1",
"w3c-keyname": "^2.2.8",
"waveql": "^1.8.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
@ -1820,6 +1824,62 @@
"node": ">=6.9.0"
}
},
"node_modules/@codemirror/autocomplete": {
"version": "6.15.0",
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.15.0.tgz",
"integrity": "sha512-G2Zm0mXznxz97JhaaOdoEG2cVupn4JjPaS4AcNvZzhOsnnG9YVN68VzfoUw6dYTsIxT6a/cmoFEN47KAWhXaOg==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
},
"peerDependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0"
}
},
"node_modules/@codemirror/commands": {
"version": "6.3.3",
"resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.3.3.tgz",
"integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.1.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.10.1",
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.10.1.tgz",
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"node_modules/@codemirror/state": {
"version": "6.4.1",
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.4.1.tgz",
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A=="
},
"node_modules/@codemirror/view": {
"version": "6.26.0",
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.26.0.tgz",
"integrity": "sha512-nSSmzONpqsNzshPOxiKhK203R6BvABepugAe34QfQDbNDslyjkqBuKgrK5ZBvqNXpfxz5iLrlGTmEfhbQyH46A==",
"dependencies": {
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@ -2047,6 +2107,27 @@
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
"dev": true
},
"node_modules/@lezer/common": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.2.1.tgz",
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
},
"node_modules/@lezer/highlight": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.0.tgz",
"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.0.tgz",
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmmirror.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -7410,6 +7491,11 @@
"integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==",
"dev": true
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmmirror.com/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
},
"node_modules/lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
@ -8227,6 +8313,14 @@
"node": ">=6"
}
},
"node_modules/onml": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/onml/-/onml-2.1.0.tgz",
"integrity": "sha512-fvaSZRzprpwLFge/mcwE0CItfniNisVNamDdMK1FQUjh4ArQZ8ZWSkDaJbZc3XaANKZHq0xIa8NJpZ2HSe3oXA==",
"dependencies": {
"sax": "^1.2.1"
}
},
"node_modules/open": {
"version": "8.4.2",
"resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz",
@ -9593,6 +9687,11 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"node_modules/sax": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
},
"node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
@ -10166,6 +10265,11 @@
"acorn": "^8.10.0"
}
},
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz",
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
},
"node_modules/stylehacks": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz",
@ -11087,6 +11191,11 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.0.tgz",
@ -11100,6 +11209,22 @@
"node": ">=10.13.0"
}
},
"node_modules/waveql": {
"version": "1.8.0",
"resolved": "https://registry.npmmirror.com/waveql/-/waveql-1.8.0.tgz",
"integrity": "sha512-TDC4rzwLAGm5qCXV2bBw0TLMlCZ7mNnYqQE/V428E2mAiU8NZrZH0aiTX91Nptfz5sSzn42dQwkJZKqbx2cZFQ==",
"dependencies": {
"@codemirror/autocomplete": "^6.12.0",
"@codemirror/commands": "^6.3.3",
"@codemirror/language": "^6.10.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.23.0",
"@lezer/highlight": "^1.2.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/wbuf": {
"version": "1.7.3",
"resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz",
@ -12948,6 +13073,56 @@
"to-fast-properties": "^2.0.0"
}
},
"@codemirror/autocomplete": {
"version": "6.15.0",
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.15.0.tgz",
"integrity": "sha512-G2Zm0mXznxz97JhaaOdoEG2cVupn4JjPaS4AcNvZzhOsnnG9YVN68VzfoUw6dYTsIxT6a/cmoFEN47KAWhXaOg==",
"requires": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
}
},
"@codemirror/commands": {
"version": "6.3.3",
"resolved": "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.3.3.tgz",
"integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==",
"requires": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.1.0"
}
},
"@codemirror/language": {
"version": "6.10.1",
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.10.1.tgz",
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
"requires": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"@codemirror/state": {
"version": "6.4.1",
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.4.1.tgz",
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A=="
},
"@codemirror/view": {
"version": "6.26.0",
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.26.0.tgz",
"integrity": "sha512-nSSmzONpqsNzshPOxiKhK203R6BvABepugAe34QfQDbNDslyjkqBuKgrK5ZBvqNXpfxz5iLrlGTmEfhbQyH46A==",
"requires": {
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@ -13136,6 +13311,27 @@
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==",
"dev": true
},
"@lezer/common": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.2.1.tgz",
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
},
"@lezer/highlight": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.0.tgz",
"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
"requires": {
"@lezer/common": "^1.0.0"
}
},
"@lezer/lr": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.0.tgz",
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
"requires": {
"@lezer/common": "^1.0.0"
}
},
"@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmmirror.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -17458,6 +17654,11 @@
"integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==",
"dev": true
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmmirror.com/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
},
"lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
@ -18118,6 +18319,14 @@
"mimic-fn": "^2.1.0"
}
},
"onml": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/onml/-/onml-2.1.0.tgz",
"integrity": "sha512-fvaSZRzprpwLFge/mcwE0CItfniNisVNamDdMK1FQUjh4ArQZ8ZWSkDaJbZc3XaANKZHq0xIa8NJpZ2HSe3oXA==",
"requires": {
"sax": "^1.2.1"
}
},
"open": {
"version": "8.4.2",
"resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz",
@ -19132,6 +19341,11 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sax": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.3.0.tgz",
"integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA=="
},
"schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
@ -19622,6 +19836,11 @@
"acorn": "^8.10.0"
}
},
"style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz",
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
},
"stylehacks": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz",
@ -20335,6 +20554,11 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
},
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.0.tgz",
@ -20345,6 +20569,19 @@
"graceful-fs": "^4.1.2"
}
},
"waveql": {
"version": "1.8.0",
"resolved": "https://registry.npmmirror.com/waveql/-/waveql-1.8.0.tgz",
"integrity": "sha512-TDC4rzwLAGm5qCXV2bBw0TLMlCZ7mNnYqQE/V428E2mAiU8NZrZH0aiTX91Nptfz5sSzn42dQwkJZKqbx2cZFQ==",
"requires": {
"@codemirror/autocomplete": "^6.12.0",
"@codemirror/commands": "^6.3.3",
"@codemirror/language": "^6.10.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.23.0",
"@lezer/highlight": "^1.2.0"
}
},
"wbuf": {
"version": "1.7.3",
"resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz",

View File

@ -13,7 +13,11 @@
"mitt": "^3.0.1",
"stream": "^0.0.2",
"vue": "^3.2.13",
"vue-i18n": "^9.9.1"
"vue-i18n": "^9.9.1",
"waveql": "^1.8.0",
"lodash.get": "^4.4.2",
"onml": "^2.1.0",
"w3c-keyname": "^2.2.8"
},
"devDependencies": {
"@babel/core": "^7.12.16",

View File

@ -2,9 +2,12 @@
--display-signal-info-height: 50px;
--signal-default-color: #4CAF50;
--main-color: #CB81DA;
--main-dark-color: #2D323B;
--main-light-color: var(--main-color);
--sidebar-width: 280px;
--right-nav-width: 60px;
--time-scale-height: 50px;
--render-scale-x: 1;
}
html, body {
@ -87,4 +90,112 @@ body::-webkit-scrollbar {
a {
color: var(--main-color);
}
.wd-container {
--right-panel-width: 0px;
width: calc(100% - var(--right-panel-width));
transition: width .3s ease-out;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.wd-grid {
position: absolute;
}
.wd-view {
position: absolute;
top: 24px;
bottom: 24px;
}
.wd-values {
position: absolute;
top: 24px;
bottom: 24px;
}
.wd-waveql {
position: absolute;
top: 24px;
bottom: 24px;
width: 100%;
transition: width .3s ease-out;
}
.wd-cursor {
position: absolute;
pointer-events: none;
}
.wd-values text {
font-size: 14px;
text-anchor: middle;
fill: #fff;
}
line.wd-cursor-line {
stroke-dasharray: 12 4;
stroke: var(--main-color);
stroke-width: 1px;
}
line.wd-grid-time {
stroke: #333;
stroke-width: 1px;
}
line.gap {
stroke: #fff;
stroke-dasharray: 4 3;
}
text.wd-grid-time {
text-anchor: middle;
fill: var(--sidebar-item-text);
}
text.wd-cursor-time {
font-size: 20px;
fill: var(--sidebar);
text-anchor: middle;
z-index: 55;
}
rect.wd-cursor-time {
fill: var(--main-color);
}
.wd-progress {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
text.xred {
fill: hsl(0, 100%, 50%);
}
text.zxviolet {
fill: hsl(287, 100%, 67%);
}
text.pc {
text-anchor: start;
}
tspan.pc-addr {
fill: hsl(202 100% 71%);
}
tspan.pc-opcode {
}

View File

@ -16,8 +16,9 @@
<script>
import { onMounted, reactive } from 'vue';
import { getVcdStream, readVcdFile } from '@/hook/utils';
import { getVcdStream, readVcdFile, numberOrString, gcd, normaliseUsingGCD, parseTimescale } from '@/hook/utils';
import { emitter, globalLookup, globalSetting, signalValues } from '@/hook/global';
import { makeWaveView } from '@/hook/render';
import Sidebar from '@/components/sidebar.vue';
import RightNav from '@/components/right-nav.vue';
@ -56,20 +57,36 @@ export default {
document.body.style.setProperty('--el-color-info-light-8', 'var(--vscode-focusBorder)');
document.body.style.setProperty('--el-bg-color-overlay', 'transplant');
// signal height
document.body.style.setProperty('--display-signal-info-height', globalSetting.displaySignalHeight + 'px');
const uint8array = await readVcdFile();
const vcdstream = await getVcdStream();
let tgcd;
// 使 vcdstream any
// https://github.com/wavedrom/vcd vcd-pipe-deso.js 58
//
vcdstream.change.any((id, time, cmd, value, mask) => {
if (signalValues[id] === undefined) {
signalValues[id] = [];
}
const time53 = Number(time);
tgcd = gcd(tgcd, time53);
signalValues[id] = signalValues[id] || { kind: '', wave: [] };
if (cmd >= 14 && cmd <= 28) {
signalValues[id].kind = 'bit';
signalValues[id].wave.push([time53, cmd - 14]);
} else {
signalValues[id].kind = 'vec';
const point = [time53, numberOrString(value)];
if (mask !== 0n) {
point.push(numberOrString(mask));
}
signalValues[id].wave.push(point);
}
});
vcdstream.on('$enddefinitions', () => {
signalValues[id].push({time, cmd, value, mask});
});
const maxChunkLength = 1 << 17;
@ -81,14 +98,20 @@ export default {
for (const topModule of vcdstream.info.wires.body) {
VcdInfo.topModules.push(topModule);
}
globalLookup.status = vcdstream.info.status;
globalLookup.version = vcdstream.info.version;
globalLookup.timescale = vcdstream.info.timescale;
globalLookup.timescale = parseTimescale(vcdstream.info.timescale);
globalLookup.date = vcdstream.info.date;
globalLookup.t0 = vcdstream.info.t0;
globalLookup.t0 = vcdstream.info.t0 || 0;
globalLookup.tgcd = tgcd;
globalLookup.time = Number(vcdstream.getTime());
globalLookup.chango = signalValues;
normaliseUsingGCD(globalLookup, signalValues);
makeWaveView();
console.log('duration time', globalLookup.time);
emitter.emit('meta-ready', null);
//

View File

@ -1,28 +1,30 @@
<template>
<div class="vertical-cursor-wrapper"
@click="makeCursor"
:style="`left: ${currentX}px`">
<div class="current-display-cursor-up">
<div class="current-time-value">{{ currentX }} px</div>
<div class="cursor-down-arrow"></div>
<div>
<div class="vertical-cursor-wrapper"
@click="makeCursor"
:style="`left: ${currentX}px`">
<div class="current-display-cursor-up">
<div class="current-time-value">{{ currentX }} px</div>
<div class="cursor-down-arrow"></div>
</div>
<div class="vertical-line"></div>
<div class="current-display-cursor-down">
<div class="current-time-value">{{ currentX }} px</div>
<div class="cursor-up-arrow"></div>
</div>
</div>
<div class="vertical-line"></div>
<div class="current-display-cursor-down">
<div class="current-time-value">{{ currentX }} px</div>
<div class="cursor-up-arrow"></div>
</div>
</div>
<div v-show="showFixedOne"
class="vertical-cursor-wrapper"
@click="makeCursor"
:style="`left: ${fixedLeft}px`">
<div class="fix-current-display-cursor-up">
{{ globalLookup.view.currentX }} px
</div>
<div class="fix-vertical-line"></div>
<div class="fix-current-display-cursor-down">
{{ globalLookup.view.currentX }} px
<div v-show="showFixedOne"
class="vertical-cursor-wrapper"
@click="makeCursor"
:style="`left: ${fixedLeft}px`">
<div class="fix-current-display-cursor-up">
{{ globalLookup.view.currentX }} px
</div>
<div class="fix-vertical-line"></div>
<div class="fix-current-display-cursor-down">
{{ globalLookup.view.currentX }} px
</div>
</div>
</div>
</template>

View File

@ -1,8 +1,9 @@
<template>
<VerticalCursor></VerticalCursor>
<TimeScale></TimeScale>
<div class="vcd-render-wrapper" >
<!-- <VerticalCursor></VerticalCursor> -->
<!-- <TimeScale></TimeScale> -->
<div
class="vcd-render-wrapper"
>
<div style="height: var(--time-scale-height);"></div>
</div>
</template>
@ -18,14 +19,15 @@ import TimeScale from '@/components/render/time-scale.vue';
export default {
name: 'main-render',
components: {
VerticalCursor,
TimeScale
// VerticalCursor,
// TimeScale
},
setup() {
const cursorX = ref(0);
return {
cursorX
cursorX,
globalLookup
}
}
}
@ -38,7 +40,7 @@ export default {
width: 98vw;
cursor: crosshair;
padding: 10px;
transition: .3s ease-out;
}
.render-canvas {

View File

@ -24,6 +24,7 @@
import { onMounted, reactive } from 'vue';
import { globalLookup, emitter } from '@/hook/global';
import { handleTimeScale } from '@/hook/utils';
import { wheelScale } from '@/hook/wheel';
export default {
name: 'time-scale',
@ -42,30 +43,29 @@ export default {
//
emitter.on('meta-ready', () => {
const { scale, unit } = handleTimeScale(globalLookup.timescale);
timeScaleManage.scale = scale;
timeScaleManage.unit = unit;
// const { scale, unit } = handleTimeScale(globalLookup.timescale);
// timeScaleManage.scale = scale;
// timeScaleManage.unit = unit;
console.log(timeScaleManage.unit);
// console.log(timeScaleManage.unit);
const viewInfo = globalLookup.view;
// const viewInfo = globalLookup.view;
});
document.addEventListener('wheel', event => {
if ((event.wheelDelta && event.ctrlKey) || event.detail) {
event.preventDefault();
}
// document.addEventListener('wheel', event => {
// if ((event.wheelDelta && event.ctrlKey) || event.detail) {
// event.preventDefault();
// }
if (event.ctrlKey) {
// console.log(event.wheelDelta);
console.log('deltaX', event.deltaX);
console.log('deltaY', event.deltaY);
} else if (event.shiftKey) {
// console.log(event.wheelDelta);
console.log('deltaX', event.deltaX);
console.log('deltaY', event.deltaY);
}
}, { capture: false, passive: false });
// if (event.ctrlKey) {
// // console.log(event.wheelDelta);
// wheelScale(event);
// } else if (event.shiftKey) {
// // console.log(event.wheelDelta);
// console.log('deltaX', event.deltaX);
// console.log('deltaY', event.deltaY);
// }
// }, { capture: false, passive: false });
const currentX = 300;

View File

@ -62,9 +62,9 @@ export default {
.add-signal-button {
margin-top: 10px;
padding: 10px 25px;
background-color: var(--color-deepPurple);
background-color: var(--main-color);
border-radius: 0 0 .8em .8em;
color: var(--sidebar-item-text);
color: var(--sidebar);
cursor: pointer;
display: flex;
justify-content: space-around;

View File

@ -7,15 +7,17 @@
v-model="searchManage.content"
input-style="font-size: 16px;"
@input="safeSearch"
@focus="searchManage.displayResult = true"
@blur="searchManage.displayResult = false"
/>
</div>
<div class="search-result-wrapper" v-if="searchManage.content.trim().length > 0">
<div class="search-result-wrapper" v-if="searchManage.displayResult">
<div class="search-result">
<div v-if="searchManage.searchResult.length > 0">
<div v-for="(searchResult, index) in searchManage.searchResult"
:key="index"
:class="globalLookup.currentWires.has(searchResult.module) ? 'vcd-treeview-selected' : ''"
@click="clickItem(searchResult.module)">
@click="toggleRender(searchResult.module)">
<div v-html="searchResult.htmlString" class="search-result-item"></div>
</div>
@ -34,6 +36,7 @@ import { reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { globalSetting, globalLookup, emitter } from '@/hook/global';
import { debounceWrapper, makeSearchResultItem } from '@/hook/utils';
import { toggleRender } from '@/hook/render';
export default {
name: 'tree-view-search',
@ -42,15 +45,20 @@ export default {
const searchManage = reactive({
content: '',
displayResult: false,
searchResult: []
});
function search() {
const searchString = searchManage.content.trim();
if (searchString.length === 0) {
searchManage.displayResult = false;
return;
}
searchManage.displayResult = true;
searchManage.searchResult = [];
const stacks = [ { name: '', body: globalLookup.topModules } ];
@ -75,18 +83,6 @@ export default {
searchManage.searchResult.push(searchResultItem);
});
function clickItem(signal) {
console.log(signal);
if (!signal.link) {
return;
}
if (globalLookup.currentWires.has(signal)) {
globalLookup.currentWires.delete(signal);
} else {
globalLookup.currentWires.add(signal);
}
}
const safeSearch = debounceWrapper(search, 500);
return {
@ -95,7 +91,7 @@ export default {
globalLookup,
debounceWrapper,
t,
clickItem
toggleRender
}
}
}

View File

@ -4,7 +4,7 @@
<hr>
<div class="vcd-signal-signals-display">
<div v-for="(signal, index) in signals.content" :key="index"
@click="clickItem(signal)"
@click="toggleRender(signal)"
class="vcd-signal-signal-item"
:class="globalLookup.currentWires.has(signal) ? 'vcd-treeview-selected' : ''">
<div><span :class="`iconfont ${makeIconClass(signal)}`"></span>&ensp;{{ signal.name }}</div>
@ -24,7 +24,7 @@ import { useI18n } from 'vue-i18n';
import { emitter, globalLookup } from '@/hook/global';
import { makeIconClass } from '@/hook/utils';
import { makeWaveCanvas, removeWaveCanvas } from '@/hook/render';
import { toggleRender } from '@/hook/render';
/* eslint-disable */
export default {
@ -47,19 +47,9 @@ export default {
return signal.size === 1 ? '' : `${signal.size - 1}:0`;
}
function clickItem(signal) {
if (globalLookup.currentWires.has(signal)) {
globalLookup.currentWires.delete(signal);
removeWaveCanvas(signal);
} else {
globalLookup.currentWires.add(signal);
makeWaveCanvas(signal);
}
}
return {
signals,
clickItem,
toggleRender,
globalLookup,
makeSignalCaption,
t,

48
src/hook/canvas.js Normal file
View File

@ -0,0 +1,48 @@
class LineDrawer {
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
constructor(ctx) {
this.ctx = ctx;
this.lastColor = null;
}
beginPath() {
this.ctx.beginPath();
}
/**
*
* @param {number} x
* @param {number} y
*/
moveTo(x, y) {
this.ctx.moveTo(x, y);
}
/**
*
* @param {number} x
* @param {number} y
* @param {string} color
*/
lineTo(x, y, color) {
const ctx = this.ctx;
ctx.lineTo(x, y);
if (this.lastColor === null) {
ctx.strokeStyle = color;
this.lastColor = color;
} else if (this.lastColor !== color) {
ctx.stroke();
ctx.strokeStyle = color;
this.lastColor = color;
}
}
}
export {
LineDrawer
}

View File

@ -22,16 +22,34 @@ const globalLookup = reactive({
timescale: '',
// 初始时间,一般都是 0
t0: 0,
// 最长持续时间
maxTime: 0n,
time: 0,
// 当前视图的左上角的坐标
view : {
x: 0,
y: 0,
gridGap: 300,
currentBaseUnit: 100,
currentX: 0,
currentScale: 1
},
// 优化视图的最大公约数
tgcd: null,
// 其实就是 signalValues
chango: {},
// 初始化时会被定义
render: () => {},
hasHistory: false,
isRO: false,
// 当前展示的波形
view: [],
xScale: 1,
// // 当前视图的左上角的坐标
// view : {
// x: 0,
// y: 0,
// gridGap: 300,
// currentBaseUnit: 100,
// currentX: 0,
// currentScaleX: 1
// },
initcurrentModule(module) {
if (this.currentModule === undefined && module) {

View File

@ -1,7 +1,12 @@
import { globalLookup, globalSetting, signalValues } from "./global";
import { LineDrawer } from './canvas';
import { domContainer, pluginRenderTimeGrid, pluginRenderValues, mountTree,
genOnWheel, genKeyHandler, keyBindo } from './wave-view';
import { createCodeMirrorState, mountCodeMirror6 } from 'waveql';
let mainRenderEl = null;
const canvasMap = {};
const canvasMap = new Map();
/**
*
@ -59,7 +64,7 @@ function makeWaveCanvas(signal) {
canvas.height = globalSetting.displaySignalHeight;
mainRender.appendChild(canvas);
canvasMap[signal] = canvas;
canvasMap.set(signal, canvas);
const ctx = canvas.getContext('2d');
const datas = signalValues[signal.link];
@ -71,6 +76,101 @@ function makeWaveCanvas(signal) {
}
}
/**
* @param {{
* kind: 'var' | 'scope',
* type: 'wire' | 'reg' | 'module',
* name: string,
* size: number,
* link: string
* }} signal
*/
function makeWaveSvg(signal) {
const mainRender = getMainRenderEl();
const height = globalSetting.displaySignalHeight;
const width = document.body.clientWidth;
const draw = SVG().addTo(mainRender).size(width, height);
const datas = signalValues[signal.link];
if (signal.size === 1) {
drawSimpleSignalSvg(draw, datas);
} else {
// adwa
}
}
/**
*
* @param {Svg} ctx
* @param {Array<{
* time: BigInt,
* cmd: number,
* value?: BigInt,
* mask?: BigInt
* }>} signals
*/
function drawSimpleSignalSvg(ctx, signals) {
let lastData = null;
let lastX = 300;
let lastColor = '';
// ctx.lineWidth = 3;
// ctx.lineJoin = 'round';
// const drawer = new LineDrawer(ctx);
// drawer.beginPath();
for (const data of signals) {
if (lastData === null) {
lastData = data;
lastX = 300;
lastColor = getDataRenderColor(data);
let lastY = Math.max((15 - data.cmd) * 30, 0) + 10;
// drawer.moveTo(last_x, last_y);
ctx.move(lastX, lastY);
} else {
// 高阻态
const start = lastData.time;
const end = data.time;
console.log(lastData, isBlocked(lastData));
let lastY = Math.max((15 - lastData.cmd) * 30, 0) + 10;
const duration = parseInt(end - start);
const currentX = lastX + 100;
const currentColor = getDataRenderColor(data);
console.log(currentX, lastY, duration);
const line = ctx.line(lastX, lastY, currentX, lastY);
if (currentColor !== lastColor) {
ctx.stroke({ color: lastColor, linecap: 'round', width: 3 });
}
// drawer.lineTo(current_x, last_y, color);
if (lastData.cmd !== data.cmd) {
let currentY = Math.max((15 - data.cmd) * 30, 0) + 10;
const color = getDataRenderColor(data);
ctx.line(currentX, lastY, currentX, currentY);
// drawer.lineTo(current_x, current_y, color);
}
lastData = data;
lastX = currentX;
lastColor = currentColor;
}
}
// 如果 last_data 的时间不足最长时间,就用最后一个时刻的去补足
ctx.stroke({ color: lastColor, linecap: 'round', width: 3 });
}
function getDataRenderColor(data) {
return isBlocked(data) ? 'rgb(224,108,117)' : 'rgb(10, 200, 10)';
}
/**
* @param {{
* kind: 'var' | 'scope',
@ -81,7 +181,7 @@ function makeWaveCanvas(signal) {
* }} signal
*/
function removeWaveCanvas(signal) {
const canvas = canvasMap[signal];
const canvas = canvasMap.get(signal);
if (canvas instanceof HTMLCanvasElement) {
canvas.parentNode.removeChild(canvas);
}
@ -101,46 +201,133 @@ function removeWaveCanvas(signal) {
function drawSimpleSignal(ctx, signals) {
let last_data = null;
let last_x = 300;
ctx.lineWidth = 3;
ctx.lineJoin = 'round';
const drawer = new LineDrawer(ctx);
ctx.beginPath();
drawer.beginPath();
for (const data of signals) {
console.log(data);
if (last_data === null) {
last_data = data;
last_x = 300;
let last_y = Math.max((15 - last_data.cmd) * 30, 0) + 10;
ctx.moveTo(last_x, last_y);
drawer.moveTo(last_x, last_y);
} else {
// 高阻态
const start = last_data.time;
const end = data.time;
ctx.strokeStyle = isBlocked(last_data) ? 'rgb(224,108,117)' : 'rgb(10, 200, 10)';
console.log(last_data, isBlocked(last_data));
let last_y = Math.max((15 - last_data.cmd) * 30, 0) + 10;
const duration = parseInt(end - start);
const current_x = last_x + 100;
console.log(current_x, last_y, duration);
ctx.lineTo(current_x, last_y);
const color = getDataRenderColor(last_data);
drawer.lineTo(current_x, last_y, color);
if (last_data.cmd !== data.cmd) {
ctx.strokeStyle = isBlocked(data) ? 'rgb(224,108,117)' : 'rgb(10, 200, 10)';
let current_y = Math.max((15 - data.cmd) * 30, 0) + 10;
ctx.lineTo(current_x, current_y);
const color = getDataRenderColor(data);
drawer.lineTo(current_x, current_y, color);
}
last_data = data;
last_x = current_x;
}
}
// 如果 last_data 的时间不足最长时间,就用最后一个时刻的去补足
ctx.stroke();
}
function pluginLocalStore(desc, pstate /* , els */) {
const pstateJsonString = JSON.stringify({
// yOffset: pstate.yOffset,
xOffset: pstate.xOffset,
xScale: pstate.xScale
});
localStorage.setItem('dide', pstateJsonString);
}
/**
*
* @param {HTMLElement} parentElement
*/
function makeWaveView(parentElement) {
if (!parentElement) {
parentElement = getMainRenderEl();
}
const container = domContainer({
elemento: mountTree.defaultElemento,
layers: mountTree.defaultLayers,
renderPlugins: [
pluginRenderTimeGrid,
pluginRenderValues,
pluginLocalStore
]
});
parentElement.appendChild(container.pstate.container);
container.start(globalLookup);
globalLookup.hasHistory = true;
globalLookup.isRO = true;
globalLookup.updater = ( /* str */ ) => {
console.log('updater');
};
const cmState = createCodeMirrorState(globalLookup, container.pstate);
const cm = mountCodeMirror6(
cmState,
container.elo.waveqlPanel,
globalLookup,
container.pstate
);
cm.view.dispatch({changes: {from: 0, insert: ' '}});
cm.view.dispatch({changes: {from: 0, to: 1, insert: ''}});
container.elo.container.addEventListener('keydown', genKeyHandler.genKeyHandler(parentElement, container.pstate, globalLookup, cm, keyBindo));
container.elo.container.addEventListener('wheel', genOnWheel(parentElement, container.pstate, globalLookup, cm, keyBindo));
// console.log(cm);
cm.view.focus();
}
/**
* @param {{
* kind: 'var' | 'scope',
* type: 'wire' | 'reg' | 'module',
* name: string,
* size: number,
* link: string
* }} signal
*/
function toggleRender(signal) {
if (globalLookup.currentWires.has(signal)) {
globalLookup.currentWires.delete(signal);
// removeWaveCanvas(signal);
globalLookup.render();
} else {
// makeWaveSvg(signal);
const lane = { ref: signal.link };
globalLookup.currentWires.add(signal);
globalLookup.view.push(lane);
globalLookup.render();
}
}
export {
getMainRenderEl,
makeWaveCanvas,
removeWaveCanvas
makeWaveSvg,
removeWaveCanvas,
makeWaveView,
toggleRender
}

View File

@ -158,6 +158,81 @@ function handleTimeScale(timescale) {
}
}
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
/**
*
* @param {string} timescale
* @returns {string}
*/
function parseTimescale(timescale) {
if (typeof timescale !== 'string') {
return;
}
const str1 = timescale.trim();
const m = str1.match(/^(\d+)\s*(\w+)$/);
const res1 = ({ 1: 0, 10: 1, 100: 2 })[m[1]];
const res2 = ({ s: 0, ms: -3, us: -6, ns: -9, ps: -12, fs: -15 })[m[2]];
return res1 + res2;
}
/**
*
* @param {BigInt} val
* @returns {string | number}
*/
function numberOrString(val) {
if (val < MAX_SAFE_INTEGER) {
return Number(val);
}
return '0x' + val.toString(16);
}
/**
*
* @param {number} a
* @param {number} b
* @returns {number}
*/
function gcd(a, b) {
if (a === undefined) {
return b;
}
let r;
while (b !== 0) {
r = a % b;
a = b;
b = r;
}
return (a < 0) ? -a : a;
}
function normaliseUsingGCD(globalLookup, signalValues) {
// const { tgcd, chango } = o;
const tgcd = globalLookup.tgcd;
// 使用全局最大公约数缩小时间倍率
globalLookup.t0 /= tgcd;
globalLookup.time /= tgcd;
for (const id of Reflect.ownKeys(signalValues)) {
// point[0] 是当前这个点的时间点
signalValues[id].wave.map(point => { point[0] /= tgcd });
}
const exp = Math.log10(tgcd) | 0;
if (exp > 0) {
const scale = Math.pow(10, exp);
const scaleGcd = tgcd / scale;
if (scaleGcd === (scaleGcd | 0)) {
globalLookup.tgcd = scaleGcd;
globalLookup.timescale += exp;
}
}
}
const scopes = new Set(['begin', 'fork', 'function', 'module', 'task']);
const variables = new Set(['event', 'integer', 'parameter', 'real', 'realtime', 'reg', 'supply0',
'supply1', 'time', 'tri', 'triand', 'trior', 'trireg', 'tri0', 'tri1',
@ -172,6 +247,8 @@ function isVariable(wire) {
}
export {
getVcdStream,
readVcdFile,
@ -182,5 +259,9 @@ export {
isVariable,
makeIconClass,
scopes,
variables
variables,
parseTimescale,
numberOrString,
gcd,
normaliseUsingGCD
};

View File

@ -0,0 +1,107 @@
'use strict';
const getX = require('./get-x.js');
const surferer = require('./surferer.js');
const skipT = (wave, iStart, tLimit) => {
let iRes = iStart;
let [tRes, vRes] = wave[iStart];
for (let i = iStart; i < wave.length; i++) {
const [t, v] = wave[i];
if (t >= tLimit) {
break;
}
iRes = i;
tRes = t;
vRes = v;
}
return [iRes, vRes, tRes];
};
const bracer = (lane, desc, pstate) => {
const body = lane.body;
const { yStep } = pstate;
let { clock, valid, ready, up } = body;
up = up || 1;
const res = [];
let count = 0;
if (clock && clock.ref) {
const clockWave = desc.chango[clock.ref].wave;
const clockEr = surferer(clockWave, pstate);
let tPrevClock = 0;
if (valid && valid.ref) {
const validWave = desc.chango[valid.ref].wave;
let iValid = 0;
let vValid;
if (ready && ready.ref) {
const readyWave = desc.chango[ready.ref].wave;
let iReady = 0;
let vReady;
for (const iClock of clockEr) {
const [tClock, vClock] = clockWave[iClock];
if (vClock) {
[iValid, vValid] = skipT(validWave, iValid, tClock);
[iReady, vReady] = skipT(readyWave, iReady, tClock);
const xClock = getX(pstate, tClock);
const xPrevClock = getX(pstate, tPrevClock);
const width = xClock - xPrevClock;
const height = up * yStep;
if (xClock > 0 && xPrevClock > 0 && width > 0) {
if (vValid) {
if (width >= 1) {
if (vReady) {
res.push(['rect', {fill: 'url(\'#valid&ready\')', width: Math.round(width), height, x: Math.round(xPrevClock), y: -height}]);
} else {
res.push(['rect', {fill: 'url(\'#valid&~ready\')', width: Math.round(width), height, x: Math.round(xPrevClock), y: -height}]);
}
count++;
} else {
count += 10;
}
}
}
tPrevClock = tClock;
if (count > 500) {
return res;
}
}
}
} else {
for (const iClock of clockEr) {
const [tClock, vClock] = clockWave[iClock];
if (vClock) {
[iValid, vValid] = skipT(validWave, iValid, tClock);
const xClock = getX(pstate, tClock);
const xPrevClock = getX(pstate, tPrevClock);
const width = xClock - xPrevClock;
const height = up * yStep;
if (xClock > 0 && xPrevClock > 0 && width > 0) {
if (vValid) {
if (width >= 1) {
res.push(['rect', {fill: 'url(\'#valid\')', width: Math.round(width), height, x: Math.round(xPrevClock), y: -height}]);
count++;
} else {
count += 10;
}
}
}
tPrevClock = tClock;
if (count > 500) {
return res;
}
}
}
}
}
}
return res;
};
module.exports = bracer;
/* eslint complexity: [1, 55] */

View File

@ -0,0 +1,171 @@
'use strict';
const genRenderWavesGL = require('./gen-render-waves-gl.js');
const renderCursor = require('./render-cursor.js');
const genResizeHandler = require('./gen-resize-handler.js');
const mTree = require('./mount-tree.js');
const setTime = (pstate, str) => {
// const { sidebarOffset, time, timescale, tgcd, xOffset } = pstate;
// pstate.xOffset = .5; // (2 * (pstate.width - pstate.sidebarWidth)) / pstate.time;
const m = str.match(/(\d+)(\w+)/); if (m) {
// const time1 = parseInt(m[1]);
// const timescale1 = ({s: 0, ms: -3, us: -6, ns: -9, ps: -12, fs: -15})[m[2]] || 0;
// pstate.xOffset = -1;
}
};
const mouseMoveHandler = (cursor, content, pstate /* , render */) => {
const xmargin = 160;
const fontHeight = 20;
const fontWidth = fontHeight / 2;
const handler = event => {
const x = pstate.xCursor = event.clientX;
cursor.style.left = (x - xmargin) + 'px';
cursor.innerHTML = renderCursor({ xmargin, fontWidth, fontHeight }, pstate);
};
handler({ clientX: pstate.width / 2 });
content.addEventListener('mousemove', handler);
};
const getFullView = desc => {
console.log(desc);
if (desc.waveql) {
return;
}
const arr = [];
const rec = ero => {
if (ero.kind === 'scope') {
arr.push(ero.name);
ero.body.map(rec);
arr.push('..');
return;
}
if (ero.kind === 'var') {
arr.push(ero.name);
return;
}
console.error(ero);
throw new Error();
// Object.keys(obj).map(name => {
// const ref = obj[name];
// if (typeof ref === 'object') {
// arr.push(name);
// rec(ref);
// arr.push('..');
// return;
// }
// if (typeof ref !== 'string') {
// throw new Error();
// }
// arr.push(name);
// });
};
rec(desc.wires);
desc.waveql = arr.join('\n');
};
const domContainer = (obj) => {
const sidebarWidth = 256;
const fontHeight = 16;
const elo = mTree.createElemento(obj.elemento);
const container = mTree.createContainer(elo, obj.layers);
elo.container.tabIndex = '0';
if (obj.pluginRightPanel) {
obj.pluginRightPanel(elo);
}
const pstate = {
fontHeight,
width: 1024, // [px] window width
height: 1024, // [px] window height
xScaleMax: 1000,
xScaleMin: 0.001,
topBarHeight: fontHeight * 1.5, // [px]
botBarHeight: fontHeight * 1.5, // [px]
xOffset: sidebarWidth,
yOffset: 0, // [px]
yStep: fontHeight * 1.5, // = 24 // [px] wave lane height
yDuty: 0.7,
sidebarWidth,
container
};
return {
elo,
pstate,
start: (deso /* , opt */) => {
// content.appendChild(container);
// content.appendChild(elo.rightPanel);
// getFullView(deso);
deso.t0 = deso.t0 || 0;
// desc.xScale |= 8;
Object.assign(pstate, {
tgcd: deso.tgcd,
timescale: deso.timescale,
xScale: deso.xScale || 8,
numLanes: deso.view.length,
t0: deso.t0,
time: deso.time
});
try {
const str = localStorage.getItem('dide');
const obj = JSON.parse(str);
Object.assign(pstate, obj);
} catch (err) {
console.error(err);
}
// const {timetopSVG, timebotSVG} = timeline(deso);
if (deso.timeOpt) {
setTime(pstate, deso.timeOpt.value);
}
let render2 = genRenderWavesGL(elo);
let render1 = render2(deso);
let render = render1(pstate, obj.renderPlugins);
deso.render = render;
const resizeHandler = genResizeHandler(pstate);
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
// TODO : 使用更新的属性替换 contentRect
// 未来版本 contentRect 可能会被丢弃
let { width, height } = entry.contentRect;
// height = height || 888;
// console.log('resizeObserver', width, height);
resizeHandler(width, height);
}
deso.render();
});
console.log(elo.container);
resizeObserver.observe(document.body);
// pinchAndZoom(els.container, content, pstate, render);
resizeHandler(elo.container.clientWidth, elo.container.clientHeight);
mouseMoveHandler(elo.cursor, elo.container, pstate, deso.render);
deso.render();
}
};
};
module.exports = domContainer;
/* eslint-env browser */

View File

@ -0,0 +1,11 @@
'use strict';
const formatTime = (t, expo) => {
const prefixes = ['T', 'G', 'M', 'k', '', 'm', 'µ', 'n', 'p', 'f', 'a', 'z', 'y'];
const ts1 = 14 - expo;
const prefix = prefixes[(ts1 / 3) |0];
const mult = ([100, 10, 1])[ts1 % 3];
return (t * mult).toLocaleString() + ' ' + prefix + 's';
};
module.exports = formatTime;

View File

@ -0,0 +1,143 @@
'use strict';
const format = (fmt, len) => {
len = BigInt(len);
{
const m = fmt.match(/^%(?<sign>[s])?(?<radix>[bodh])(?<elen>\d+)?$/);
if (m) {
const radix = ({b: 2, o: 8, d: 10, h: 16, H: 16})[m.groups.radix];
if (m.groups.sign) {
return (val, pos) => {
if ((val >> (len - 1n)) & 1n) {
val = val - (2n ** len);
}
let txtOrig = val.toString(radix);
if (txtOrig.length <= pos) {
return txtOrig;
}
const sign = (val < 0) ? '-' : '+';
if (pos === 1) {
return sign;
}
if (pos === 2) {
return sign + '\u205D';
}
return sign + '\u205D' + txtOrig.slice(2 - pos);
};
}
if (Number(m.groups.elen) > 0) {
const elen = BigInt(m.groups.elen);
return (val, pos) => {
let txtRes = [];
for (let i = 0n; i < len; i += elen) {
const chunk = (val >> i) & (2n ** elen - 1n);
txtRes.unshift(chunk.toString(radix));
}
txtRes = '{' + txtRes.join(', ') + '}';
if (txtRes.length <= pos) {
return txtRes;
}
if (pos === 1) {
return '\u205D';
}
return '\u205D' + txtRes.slice(1 - pos);
};
}
return (val, pos) => {
let txtOrig = val.toString(radix);
if (txtOrig.length <= pos) {
return txtOrig;
}
if (pos === 1) {
return '\u205D';
}
return '\u205D' + txtOrig.slice(1 - pos);
};
}
} {
const m = fmt.match(/^%a$/);
if (m) {
return (val, pos) => {
let txtRes = '';
for (let i = 0n; i < len; i += 8n) {
txtRes = String.fromCharCode(Number((val >> i) & 0xffn)) + txtRes;
}
txtRes = txtRes.trim();
if (txtRes.length <= pos) {
return txtRes;
}
txtRes = txtRes.slice(0, pos - 1) + '\u205D';
return txtRes;
};
}
} {
const m = fmt.match(/^%(?<form>[fe])(?<elen>32|64)$/);
if (m) {
const buf = new ArrayBuffer(8);
const bufBInt = new BigInt64Array(buf);
const elen = BigInt(m.groups.elen);
const bufFloat = new ((elen === 32n) ? Float32Array : Float64Array)(buf);
const mask = (elen === 32n) ? 0xffffffffn : 0xffffffffffffffffn;
if (m.groups.form === 'f') {
if (len > elen) {
return (val, pos) => {
let txtRes = [];
for (let i = 0n; i < len; i += elen) {
bufBInt[0] = (val >> i) & mask;
const fval = bufFloat[0];
txtRes.unshift(fval.toString());
}
txtRes = '{' + txtRes.join(', ') + '}';
if (txtRes.length <= pos) {
return txtRes;
}
if (pos === 1) {
return '\u205D';
}
return '\u205D' + txtRes.slice(1 - pos);
};
}
return (val, pos) => {
bufBInt[0] = val & mask;
const fval = bufFloat[0];
let txtOrig;
txtOrig = fval.toString();
if (txtOrig.length <= pos) {
return txtOrig;
}
for (let i = pos; i >= 0; i--) {
txtOrig = fval.toPrecision(i + 1);
if (txtOrig.length <= pos) {
return txtOrig;
}
txtOrig = fval.toExponential(i);
if (txtOrig.length <= pos) {
return txtOrig;
}
}
return (val < 0) ? '-' : '+';
};
} else {// e
return (val, pos) => {
bufBInt[0] = val & mask;
const fval = bufFloat[0];
let txtOrig;
txtOrig = fval.toExponential();
if (txtOrig.length <= pos) {
return txtOrig;
}
for (let i = 4; i <= pos; i++) {
txtOrig = fval.toExponential(pos - i);
if (txtOrig.length <= pos) {
return txtOrig;
}
}
return (val < 0) ? '-' : '+';
};
}
}
}
return () => '?';
};
module.exports = format;

View File

@ -0,0 +1,31 @@
'use strict';
const {keyName} = require('w3c-keyname');
const executeKeyHandler = (key, keyBindo, pstate, cm) => {
return (keyBindo[key] || keyBindo.nop).fn(pstate, cm);
};
const genKeyHandler = (div, pstate, deso, cm, keyBindo, plugins) => {
return event => {
const modifier = (
(event.ctrlKey ? 'Ctrl+' : '') +
(event.shiftKey ? 'Shift+' : '') +
(event.altKey ? 'Alt+' : '')
);
// const key = modifier + event.key;
const key = modifier + keyName(event);
if (executeKeyHandler(key, keyBindo, pstate, cm)) {
event.stopPropagation();
if (plugins != undefined) {
plugins.map(fn => fn(key, event));
}
deso.render();
}
};
};
exports.executeKeyHandler = executeKeyHandler;
exports.genKeyHandler = genKeyHandler;

View File

@ -0,0 +1,30 @@
'use strict';
const genOnWheel = (element, pstate, deso, cm, 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));
}
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;

View File

@ -0,0 +1,353 @@
'use strict';
const cColors = new Float32Array([
0, 0, 0, 0, // 0:
0, 0, 1, 1, // 1: (Z) high impedance
0, 1, 0, 1, // 2: strong 0
0, 1, 1, 1, // 3: strong 1
1, 0, 0, 1, // 4: (x X) strong unknown
.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, 0, 0, 0, // 11:
0, 0, 0, 0, // 12:
0, 0, 0, 0, // 13:
0, 0, 0, 0, // 14:
0, 0, 0, 0 // 15:
]);
const cTilts = 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 shaderer = (kind, src) => gl => {
const vShader = gl.createShader(gl[kind]);
gl.shaderSource(vShader, src);
gl.compileShader(vShader);
return vShader;
};
const vertexShaderScalar = shaderer('VERTEX_SHADER', `#version 300 es
in uvec3 pos;
out vec4 v_color;
uniform vec2 scale;
uniform vec2 offset;
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(
float(pos.x) * scale.x + offset.x + node[1] * tilt,
float(node[0]) * scale.y + offset.y,
1, 1
);
}
`);
const fragmentShader = shaderer('FRAGMENT_SHADER', `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 myOutputColor;
void main() {
myOutputColor = v_color;
}
`);
const initProgram = gl => {
const program = gl.createProgram();
gl.attachShader(program, vertexShaderScalar(gl));
gl.attachShader(program, fragmentShader(gl));
gl.linkProgram(program);
gl.useProgram(program);
return 'colors tilts scale offset tilt'
.split(/\s+/)
.reduce((res, name) => Object.assign(res, {
[name]: gl.getUniformLocation(program, name)
}), {
pos: gl.getAttribLocation(program, 'pos'),
gl
});
};
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]
)
];
const wave2vertex = desc => {
Object.keys(desc.chango).map(ref => {
const chang = desc.chango[ref];
const { kind, wave } = chang;
// desc.view.map(lane => {
// if (!lane || lane.ref === undefined) { return; }
// const chang = desc.chango[lane.ref];
// if (chang === undefined) { return; }
// const {kind, wave} = chang;
// lane.kind = kind;
// lane.wave = wave;
if (kind === 'bit') {
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)) ? desc.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));
// throw new Error('val is: ' + val);
}
}
chang.vertices = new Uint32Array(vertices); // Uint16Array // 16bit
// lane.vertices = new Uint16Array(vertices);
}
if (kind === 'vec') {
const vertices = [];
const ilen = wave.length;
for (let i = 0; i < ilen; i++) {
// const f = wave[(i === 0) ? 0 : (i - 1)];
const [tim, val, msk] = wave[i];
// const t = wave[(i === (ilen - 1)) ? i : (i + 1)];
const tt = (i === (ilen - 1)) ? desc.time : wave[i + 1][0];
if (val) {
if (msk) {
vertices.push(...brick[2](2, 2, tim, tt, 4, 4)); // x,z?
} else {
// vertices.push(...brick[2](2, 2, tim, tt, 5, 5)); // 2
vertices.push(
tim, 0, 0,
tt, 0, 0, tt, 0, 5, tt, 4, 5,
tim, 6, 5, tim, 0, 5, tim, 3, 5,
tt, 1, 5, tt, 0, 5
);
}
} else {
if (msk) {
vertices.push(...brick[2](2, 2, tim, tt, 4, 4)); // x
} else {
// vertices.push(...brick[0](2, 2, tim, tt, 2, 3)); // 0
vertices.push(tim, 6, 2, tt, 4, 2);
}
}
}
chang.vertices = new Uint32Array(vertices); // Uint16Array // 16bit
// lane.vertices = new Uint16Array(vertices);
}
});
};
const initData = (loco, desc) => {
Object.keys(desc.chango).map(ref => {
const chang = desc.chango[ref];
const { vertices } = chang;
// desc.view.map(lane => {
// if (!lane) {
// return;
// }
// const vertices = desc.chango[lane.ref].vertices;
//
// if (!vertices) {
// return;
// }
// if (!lane || lane.vertices === undefined) {
// return;
// }
// const vertices = lane.vertices;
const gl = loco.gl;
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 将初始化的顶点数据复制到 buffer 区域
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
chang.vao = gl.createVertexArray();
gl.bindVertexArray(chang.vao);
gl.vertexAttribIPointer(
loco.pos,
3, // number of conponents (x, y, z)
gl.UNSIGNED_INT, // UNSIGNED_SHORT, // 16bit
0 /* stride */, 0 /* offset */
);
gl.enableVertexAttribArray(loco.pos);
gl.uniform4fv(loco.colors, cColors);
gl.uniform2fv(loco.tilts, cTilts);
});
};
const genRenderWavesGL = (els) => {
const canvas = document.createElement('canvas');
els.view0.replaceChildren(canvas);
// canvas.width = 1024;
// canvas.height = 1024;
// canvas.style = 'background-color: #111;';
const glProps = {
premultipliedAlpha: false,
alpha: true,
antialias: false,
depth: false
};
const gl = canvas.getContext('webgl2', glProps);
const loco = initProgram(gl);
return desc => {
wave2vertex(desc);
initData(loco, desc);
return (pstate, plugins) => {
let aReq;
return () => {
console.log('[wave render] render');
if (aReq !== undefined) {
cancelAnimationFrame(aReq);
}
aReq = window.requestAnimationFrame(() => {
const { width, height, xScale, xOffset, yOffset, yStep, yDuty } = pstate;
const cHeight = height - 40; // FIXME 40
// console.log(pstate);
canvas.width = width;
canvas.height = cHeight;
// const xs = pstate.scale;
// const xo = -1;
gl.uniform1f(loco.tilt, 3 / width);
gl.uniform2f(loco.scale, 2 * xScale / width, yStep * yDuty / cHeight); // FIXME 40
gl.viewport(0, 0, width, cHeight);
gl.clear(gl.COLOR_BUFFER_BIT);
desc.view.map((lane, idx) => {
// console.log(lane, idx);
if (!lane) { // || lane.vertices === undefined) {
return;
}
const ref = lane.ref;
if (ref === undefined) {
return;
}
const chang = desc.chango[ref];
if (chang === undefined) {
return;
}
// console.log(chang);
gl.bindVertexArray(chang.vao);
gl.uniform2f(loco.offset,
(2 * xOffset / width) - 1,
(2 * yOffset - 2 * yStep * (idx + .7)) / cHeight + 1
);
gl.drawArrays(
gl.LINE_STRIP, // mode
0, // first
chang.vertices.length / 3 // count
);
});
plugins.map(fn => fn(desc, pstate, els));
aReq = undefined;
});
};
};
};
};
module.exports = genRenderWavesGL;
/* eslint-env browser */
/* eslint complexity: [1, 30] */

View File

@ -0,0 +1,27 @@
'use strict';
const xOffsetUpdate = require('./x-offset-update.js');
const genResizeHandler = pstate =>
(width, height) => {
let { xOffset, yOffset, xScale, yStep, time, sidebarWidth, numLanes } = pstate;
pstate.width = width;
pstate.height = height;
// Y
const yOffsetMax = (numLanes + 2) * 2 * yStep;
if (yOffsetMax < 0) {
yOffset = 0;
} else if (yOffset > yOffsetMax) {
yOffset = yOffsetMax;
}
pstate.yOffset = yOffset;
console.log('resize handler', pstate);
// X
const xScaleMin = pstate.xScaleMin = (width - sidebarWidth) / time;
pstate.xScale = (xScale < xScaleMin) ? xScaleMin : xScale;
pstate.xScaleMin = 0.001;
xOffsetUpdate(pstate, xOffset);
};
module.exports = genResizeHandler;

View File

@ -0,0 +1,16 @@
'use strict';
const getElement = divName => {
if (typeof divName === 'string') {
const c = document.getElementById(divName);
if (c === null) {
throw new Error('<div> element width Id: "' + divName + '" not found');
}
return c;
}
return divName;
};
module.exports = getElement;
/* eslint-env browser */

View File

@ -0,0 +1,33 @@
'use strict';
const format = require('./format.js');
const getLabel = (lane) => {
if (typeof lane !== 'object') {
lane = {};
}
const fmt = lane.format || '%h';
const width = Number(lane.width || 1);
const formatter = format(fmt, width);
return (vPre, mPre, x, w) => {
if (mPre) {
if (vPre) {
return ['text', {x, class: 'zxviolet'}, '?'];
} else {
return ['text', {x, class: 'xred'}, 'x'];
}
}
const pos = (w / 8) |0;
vPre = BigInt(vPre);
const txtShort = formatter(vPre, pos, width);
return ['text', {x}, txtShort];
};
};
module.exports = getLabel;

View File

@ -0,0 +1,26 @@
'use strict';
const lister = require('./lister.js');
const getListing = async (readers) => {
let listing = [];
const r = readers.find(reader => reader.ext === 'lst');
if (r) {
const utf8Decoder = new TextDecoder('utf-8');
const list = lister();
for (let i = 0; i < 10000; i++) {
let { done, value } = await r.reader.read();
if (typeof value !== 'string') {
value = utf8Decoder.decode(value, {stream: true});
}
list.onChunk(value);
if (done) {
listing = list.getTrace();
break;
}
}
}
return listing;
};
module.exports = getListing;

View File

@ -0,0 +1,13 @@
'use strict';
const getT = (x, pstate) => {
const {xOffset, xScale, tgcd } = pstate;
return Math.round((x / xScale) - (xOffset / xScale)) * tgcd;
// t = ((x / xScale * 2) - (xOffset * width / xScale)) * tgcd;
// t = (x / xScale * 2) * tgcd - (xOffset * width / xScale) * tgcd;
// t + (xOffset * width / xScale) * tgcd = (x / xScale * 2) * tgcd;
// (t / tgcd) + (xOffset * width / xScale) = (x / xScale * 2);
// (t / tgcd) * xScale / 2 + (xOffset * width / 2) = x;
};
module.exports = getT;

View File

@ -0,0 +1,6 @@
'use strict';
const getX = (pstate, time) =>
time * pstate.xScale + pstate.xOffset;
module.exports = getX;

View File

@ -0,0 +1,15 @@
'use strict';
// Fletcher Style checksum
const hash = str => {
let sum1 = 599;
let sum2 = 173;
for (let i = 0; i < str.length; i++) {
sum1 = (sum1 + str.charCodeAt(i)) & 255;
sum2 = (sum2 + sum1) & 255;
}
return (sum2 ^ sum1 ^ (sum2 >> 5) ^ (sum1 >> 5)) % 36;
};
module.exports = hash;

View File

@ -0,0 +1,27 @@
'use strict';
const getElement = require('./get-element.js');
const getListing = require('./get-listing.js');
const domContainer = require('./dom-container.js');
const pluginRenderValues = require('./plugin-render-values.js');
const pluginRenderTimeGrid = require('./plugin-render-time-grid.js');
const keyBindo = require('./key-bindo.js');
const mountTree = require('./mount-tree.js');
const genKeyHandler = require('./gen-key-handler.js');
const genOnWheel = require('./gen-on-wheel.js');
const xOffsetUpdate = require('./x-offset-update.js');
const getX = require('./get-x.js');
const getT = require('./get-t.js');
exports.getListing = getListing;
exports.getElement = getElement;
exports.domContainer = domContainer;
exports.pluginRenderValues = pluginRenderValues;
exports.pluginRenderTimeGrid = pluginRenderTimeGrid;
exports.keyBindo = keyBindo;
exports.mountTree = mountTree;
exports.genKeyHandler = genKeyHandler;
exports.genOnWheel = genOnWheel;
exports.xOffsetUpdate = xOffsetUpdate;
exports.getX = getX;
exports.getT = getT;

View File

@ -0,0 +1,84 @@
'use strict';
const xOffsetUpdate = require('./x-offset-update.js');
const xScaleUpdate = require('./x-scale-update.js');
const yScroll = delta => (pstate, cm) => {
const info = cm.getScrollInfo();
cm.scrollTo(null, info.top + info.clientHeight * delta);
return false;
};
const pluso = {
desc: 'Zoom in time',
fn: pstate => xScaleUpdate(pstate, 3 / 2 * pstate.xScale)
};
const minuso = {
desc: 'Zoom out time',
fn: pstate => xScaleUpdate(pstate, 2 / 3 * pstate.xScale)
};
const fullo = {
desc: 'All of time',
fn: pstate => xScaleUpdate(pstate, pstate.xScaleMin)
};
const scroll = {
left: {
desc: 'Scroll into the past',
fn: pstate => xOffsetUpdate(pstate, pstate.xOffset + .2 * pstate.width)
},
right: {
desc: 'Scroll into the future',
fn: pstate => xOffsetUpdate(pstate, pstate.xOffset - .2 * pstate.width)
},
up: {
desc: 'scroll up',
fn: yScroll(-.1)
},
down: {
desc: 'scroll down',
fn: yScroll(.1)
}
};
const editable = {
desc: 'Toggle edit mode',
fn: (pstate, cm) => {
console.log('editable', pstate, cm);
}
};
module.exports = {
// Alt + <, >. left / right
'Alt+,': scroll.left, 'Shift+icon:scrollUp': scroll.left,
'Alt+.': scroll.right, 'Shift+icon:scrollDown': scroll.right,
// Alt + [ ] home / end
'Alt+[': { desc: 'Jump to beginning of time', fn: pstate => xOffsetUpdate(pstate, pstate.sidebarWidth) }, // Home
'Alt+]': { desc: 'Jump to end time', fn: pstate => xOffsetUpdate(pstate, pstate.width - pstate.xScale * pstate.time) }, // End
// 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,
// CAN'T DO: Alt + e, d, f, l
// ArrowUp: scroll.up, 'Shift+ArrowUp': scroll.up,
// ArrowDown: scroll.down, 'Shift+ArrowDown': scroll.down,
// PageUp: {desc: 'scroll page up', fn: yScroll(-1)},
// PageDown: {desc: 'scroll page down', fn: yScroll(1)},
nop: { fn: () => false }
};

View File

@ -0,0 +1,23 @@
'use strict';
module.exports = () => {
const trace = {};
let tail = '';
return {
onChunk: (chunk) => {
const rows = (tail + chunk).split('\n');
// console.log('chunk:', chunk.length, 'tail:', tail.length, 'rows:', rows.length);
tail = rows.pop();
rows.map(row => {
const m = row.match(/\s*([0-9a-f]+):\s*([0-9a-f]+)\s+(.+)/);
if (m) {
const pc = parseInt(m[1], 16);
const op = m[2];
const asm = m[3].replace(/\t/, ' ');
trace[pc] = {op, asm};
}
});
},
getTrace: () => trace
};
};

View File

@ -0,0 +1,46 @@
'use strict';
const defaultElemento = {
container: ['div', { class: 'wd-container', id: 'wd-container' }],
grid: ['div', { class: 'wd-grid', id: 'wd-grid' }],
view0: ['div', { class: 'wd-view', id: 'wd-view' }],
values: ['div', { class: 'wd-values', id: 'wd-values' }],
cursor: ['div', { class: 'wd-cursor', id: 'wd-cursor' }],
};
const defaultLayers = [
'grid',
'view0',
'values',
'cursor'
];
const createElemento = elemento => {
const names = Object.keys(elemento);
return names.reduce((res, name) => {
const ml = elemento[name];
const el = document.createElement(ml[0]);
const attr = (typeof ml[1] === 'object') ? ml[1] : {};
Object.keys(attr).map(key => {
if (key === 'class') {
el.classList.add(attr.class);
} else {
el.setAttribute(key, attr[key]);
}
});
res[name] = el;
return res;
}, {});
};
const createContainer = (els, layers) => {
layers.map(layer => els.container.appendChild(els[layer]));
return els.container;
};
exports.defaultElemento = defaultElemento;
exports.defaultLayers = defaultLayers;
exports.createElemento = createElemento;
exports.createContainer = createContainer;
/* eslint-env browser */

View File

@ -0,0 +1,121 @@
'use strict';
const onml = require('onml');
const format = require('./format');
const getElement = divName => {
if (typeof divName === 'string') {
const c = document.getElementById(divName);
if (c === null) {
throw new Error('<div> element width Id: "' + divName + '" not found');
}
return c;
}
return divName;
};
const fp64_2bigint = (val) => {
const buf = new ArrayBuffer(8);
const bufFloat = new Float64Array(buf);
const bufBInt = new BigInt64Array(buf);
bufFloat[0] = val;
return bufBInt[0];
};
const fp32_2bigint = (val) => {
const buf = new ArrayBuffer(8);
const bufFloat = new Float32Array(buf);
const bufUInt = new BigInt64Array(buf);
bufFloat[0] = val;
return bufUInt[0];
};
const rnda = 40 * (Math.random() - 0.5);
const specs = [
{fmt: '%b', val: 255n, len: 24},
{fmt: '%o', val: 255n, len: 24},
{fmt: '%d', val: 255n, len: 24},
{fmt: '%h', val: 255n, len: 24},
{fmt: '%b', val: 12345678n, len: 16},
{fmt: '%o', val: 12345678n, len: 16},
{fmt: '%d', val: 12345678n, len: 16},
{fmt: '%h', val: 12345678n, len: 16},
{fmt: '%b', val: fp32_2bigint(rnda), len: 32},
{fmt: '%o', val: fp32_2bigint(rnda), len: 32},
{fmt: '%d', val: fp32_2bigint(rnda), len: 32},
{fmt: '%h', val: fp32_2bigint(rnda), len: 32},
{fmt: '%sb', val: 12345678n, len: 24},
{fmt: '%so', val: 12345678n, len: 24},
{fmt: '%sd', val: 12345678n, len: 24},
{fmt: '%sh', val: 12345678n, len: 24},
{fmt: '%sb', val: 255n, len: 16},
{fmt: '%so', val: 255n, len: 16},
{fmt: '%sd', val: 255n, len: 16},
{fmt: '%sh', val: 255n, len: 16},
{fmt: '%sb', val: 12345678n, len: 25},
{fmt: '%so', val: 12345678n, len: 25},
{fmt: '%sd', val: 12345678n, len: 25},
{fmt: '%sh', val: 12345678n, len: 25},
{fmt: '%b8', val: 12345678n, len: 24},
{fmt: '%o8', val: 12345678n, len: 24},
{fmt: '%d8', val: 12345678n, len: 24},
{fmt: '%h8', val: 12345678n, len: 24},
{fmt: '%h8', val: 0x4142434445n, len: 40},
{fmt: '%a', val: 0x4142434445n, len: 40},
{fmt: '%f64', val: -0x27c0aad9be68b8fbn, len: 64},
{fmt: '%e64', val: -0x27c0aad9be68b8fbn, len: 64},
{fmt: '%f64', val: -0x3d23edde7c882195n, len: 64},
{fmt: '%e64', val: -0x3d23edde7c882195n, len: 64},
{fmt: '%f64', val: 0x3ee9e0fcaf9380fcn, len: 64},
{fmt: '%e64', val: 0x3ee9e0fcaf9380fcn, len: 64},
{fmt: '%f64', val: 0x405edd2f1a9fbe77n, len: 64},
{fmt: '%e64', val: 0x405edd2f1a9fbe77n, len: 64},
{fmt: '%f64', val: fp64_2bigint(rnda), len: 64},
{fmt: '%e64', val: fp64_2bigint(rnda), len: 64},
{fmt: '%f32', val: 0x42f6e979n, len: 32},
{fmt: '%e32', val: 0x42f6e979n, len: 32},
{fmt: '%f32', val: 0x374f07e5n, len: 32},
{fmt: '%e32', val: 0x374f07e5n, len: 32},
{fmt: '%f32', val: fp32_2bigint(rnda), len: 32},
{fmt: '%e32', val: fp32_2bigint(rnda), len: 32},
];
const getFmtTable = () => {
const poss = [1, 2, 3, 4, 5, 6, 7, 8, 16, 48];
const header = [
'', 'pos\u2192', ...poss,
'\u2B10fmt', '\u2B10len', ...poss.map(e => '*'.repeat(e))
].map(e => ['div', {class: 'tab-header'}, e]);
const rows = specs.flatMap(spec => [
spec.fmt, spec.len,
...poss.map(pos => format(spec.fmt, spec.len)(spec.val, pos))
].map(e => ['div', {class: 'tab-cell'}, e]));
const res = ['div', {class: 'tab-container'}, ...header, ...rows];
return res;
};
const getBody = () =>
['div',
['h1', 'WaveQL'],
['span', 'WaveQL = Wave Query Language'],
['h2', 'Data label formats'],
`Text label of multibit values can be formated using one of standard formats.
The label may be shortened to fit the space available in waveform.
Some examples of labels shown in the table below.`,
getFmtTable()
];
global.pageFormat = async (div) => {
const root = getElement(div);
onml.renderer(root)(getBody());
};
/* eslint-env browser */

View File

@ -0,0 +1,69 @@
'use strict';
const genSVG = require('onml/gen-svg.js');
const stringify = require('onml/stringify.js');
const formatTime = require('./format-time.js');
const getT = require('./get-t.js');
const round10 = n =>
([
/*0 1 2 3 4 5 6 7 8 9 */
0, 1, 2, 4, 4, 5, 5, 5, 10, 10,
/*10 11 12 13 14 15 16 17 18 19 */
10, 10, 10, 15, 15, 15, 15, 20, 20, 20
])[Math.round(n)]
|| Math.round(n);
const getTimeGrid = pstate => {
const { sidebarWidth, width, height, timescale, xScale, tgcd, xOffset, topBarHeight, botBarHeight } = pstate;
const fontHeight = 16;
// const timeLineStart = (xOffset * width / 2) |0;
const timeGrid = ['g', {}];
const xStartExact = getT(sidebarWidth, pstate);
const xFinishExact = getT(width, pstate);
const density = 1;
const xLines = Math.round(density * width / sidebarWidth);
const xStep = ((xFinishExact - xStartExact) / xLines);
const xExp = Math.pow(10, Math.log10(xStep) |0);
const xDelta = round10(xStep / xExp) * xExp;
const xStart = Math.ceil(xStartExact / xDelta) * xDelta;
const xFinish = Math.floor(xFinishExact / xDelta) * xDelta;
for (let t = xStart; t <= xFinish; t += xDelta) {
const x = Math.round(t / tgcd * xScale + xOffset);
timeGrid.push(['g', {},
['line', {
class: 'wd-grid-time',
x1: x,
x2: x,
y2: height
}],
['text', {
class: 'wd-grid-time',
x: x,
y: (topBarHeight + fontHeight) / 2
}, formatTime(t, timescale)],
['text', {
class: 'wd-grid-time',
x: x,
y: height - (botBarHeight - fontHeight) / 2
}, formatTime(t, timescale)]
]);
}
return timeGrid;
};
const pluginRenderTimeGrid = (desc, pstate, els) => {
const {width, height} = pstate;
const ml = genSVG(width, height);
ml.push(getTimeGrid(pstate));
els.grid.innerHTML = stringify(ml);
};
module.exports = pluginRenderTimeGrid;

View File

@ -0,0 +1,16 @@
'use strict';
const renderValues = require('./render-values.js');
const pluginRenderValues = (desc, pstate, els) => {
const gen = renderValues(desc, pstate);
for (let i = 0; i < 1e6; i++) {
const iter = gen.next();
if (iter.done) {
els.values.innerHTML = iter.value;
break;
}
}
};
module.exports = pluginRenderValues;

View File

@ -0,0 +1,56 @@
'use strict';
const genSVG = require('onml/gen-svg.js');
const stringify = require('onml/stringify.js');
const formatTime = require('./format-time.js');
const renderCursor = (cfg, pstate) => {
const { xmargin, fontWidth, fontHeight } = cfg;
const { height, xScale, xOffset, tgcd, timescale, xCursor } = pstate;
const xx = Math.round((xCursor - xOffset) / xScale) * tgcd;
const label = formatTime(xx, timescale);
const lWidth = (label.length + 1) * fontWidth;
const body = [
// vertical line
['line', {
class: 'wd-cursor-line',
x1: xmargin + 0.5,
x2: xmargin + 0.5,
y1: 0,
y2: height
}],
// top time label
['rect', {
class: 'wd-cursor-time',
x: xmargin - lWidth / 2,
y: 0,
rx: 9,
ry: 9,
width: lWidth,
height: fontHeight * 1.25
}],
['text', {
class: 'wd-cursor-time',
x: xmargin,
y: fontHeight
}, label],
// bottom time label
['rect', {
class: 'wd-cursor-time',
x: xmargin - lWidth / 2,
y: height - fontHeight * 1.25,
width: lWidth,
height: fontHeight * 1.25
}],
['text', {
class: 'wd-cursor-time',
x: xmargin,
y: height - fontHeight * .25
}, label]
];
return stringify(genSVG(2 * xmargin, height).concat(body));
};
module.exports = renderCursor;

View File

@ -0,0 +1,127 @@
'use strict';
const tt = require('onml/tt.js');
const genSVG = require('onml/gen-svg.js');
const stringify = require('onml/stringify.js');
// const getPco = require('./get-pco.js');
const water = require('./water.js');
const bracer = require('./bracer.js');
const vline = require('./vline.js');
const getX = require('./get-x.js');
const vlineStylo = require('./vline-stylo.js');
const getLabel = require('./get-label.js');
const defs = ['defs',
['linearGradient', {id: 'valid'},
['stop', {offset: '30%', 'stop-color': 'hsla(100, 100%, 100%, 0)'}],
['stop', {offset: '90%', 'stop-color': 'hsla(100, 100%, 100%, .3)'}]
],
['linearGradient', {id: 'valid&ready'},
['stop', {offset: '30%', 'stop-color': 'hsla(100, 100%, 50%, 0)'}],
['stop', {offset: '90%', 'stop-color': 'hsla(100, 100%, 50%, .5)'}]
],
['linearGradient', {id: 'valid&~ready'},
['stop', {offset: '30%', 'stop-color': 'hsla(50, 100%, 50%, 0)'}],
['stop', {offset: '90%', 'stop-color': 'hsla(50, 100%, 50%, .5)'}]
],
...Object.keys(vlineStylo).map(key => {
const e = vlineStylo[key];
return ['filter', {id: 'neonGlow-' + key, width: 7, x: -3},
['feGaussianBlur', {stdDeviation: 3, in: 'SourceAlpha', result: 'ablur'}],
['feFlood', {'flood-color': `hsl(${e.h},100%,${e.l}%)`, result: 'xf'}],
['feComposite', {in: 'xf', in2: 'ablur', operator: 'in'}]
];
})
];
const renderValues = function* (desc, pstate) {
const { width, height, sidebarWidth, yOffset, yStep, topBarHeight, botBarHeight } = pstate;
const { view } = desc;
const ilen = height / yStep;
const iskip = yOffset / yStep;
// console.log(iskip, ilen, view.length);
const ml = genSVG(width, height - topBarHeight - botBarHeight);
let ifirst = 0;
for (let i = 0; i < ilen; i += 1) {
const lane = view[i];
if (lane && (lane.name || lane.kind)) {
if (i > iskip) {
break;
}
ifirst = i;
}
}
ml.push(defs);
yield;
const markers = ['g'];
ml.push(markers);
for (let i = 0; i < (iskip + ilen); i++) {
const lane = view[i + (ifirst |0)];
if (lane && lane.kind === 'DIZ') {
markers.push(['g', tt(0, Math.round((i - (iskip - ifirst) + 1.18) * yStep))].concat(water(lane, desc, pstate)));
} else
if (lane && lane.kind === 'brace') {
markers.push(['g', tt(0, Math.round((i - (iskip - ifirst) + 1.18) * yStep))].concat(bracer(lane, desc, pstate)));
} else
if (lane && lane.ref) {
const chango = desc.chango[lane.ref];
if (chango && chango.kind === 'vec') {
const mLane = ['g', tt(0, Math.round((i - (iskip - ifirst) + .9) * yStep))];
const { wave } = chango;
const jlen = wave.length;
perLane: {
let [tPre, vPre, mPre] = wave[0];
let xPre = getX(pstate, tPre);
const labeler = getLabel(lane);
for (let j = 1; j <= jlen; j++) {
const mark = wave[j];
const [tCur, vCur, mCur] = (mark || [desc.time, 0, 0]);
const xCur = getX(pstate, tCur);
if (vPre || mPre) {
if (xPre > width && xCur > width) { // both time stamps to the right
break perLane;
}
if (!((xPre < sidebarWidth) && (xCur < sidebarWidth))) { // both time stamps to the left
const xPreNorm = ((xPre > sidebarWidth) ? xPre : sidebarWidth) |0;
const xCurNorm = ((xCur < width) ? xCur : width) |0;
const w = xCurNorm - xPreNorm;
if (w > 8) {
const x = Math.round((xPreNorm + xCurNorm) / 2);
mLane.push(labeler(vPre, mPre, x, w));
}
}
}
xPre = xCur;
vPre = vCur;
mPre = mCur;
}
}
ml.push(mLane);
}
yield;
}
}
// console.log(view);
for (let i = 0; i < view.length; i++) {
const lane = view[i];
if (lane && lane.vlines) {
markers.push(...vline(lane, pstate, i));
}
}
yield;
return stringify(ml);
};
module.exports = renderValues;
/* eslint complexity: [1, 55] */

View File

@ -0,0 +1,22 @@
'use strict';
function* sampler (wave) {
let t1, v0, v1;
[t1, v1] = wave[0]; // initial state
let t = 0;
for (let i = 1; i < wave.length; i++) {
v0 = v1;
[t1, v1] = wave[i]; // next change
while (true) {
if (t >= t1) {
break;
}
t = yield v0;
}
}
while(true) { // to the end of time
yield v1;
}
}
module.exports = sampler;

View File

@ -0,0 +1,24 @@
'use strict';
const getT = require('./get-t.js');
function* surferer (wave, pstate) {
const { sidebarWidth, width } = pstate;
const xStartExact = getT(sidebarWidth, pstate);
const xFinishExact = getT(width, pstate);
let i = 0;
for (i = 0; i < wave.length; i++) { // seek to the start of viewport
if ((wave[i] === undefined) || (wave[i][0] >= xStartExact)) {
break;
}
}
for (; i < wave.length; i++) { // render viewport
if ((wave[i] === undefined) || (wave[i][0] > xFinishExact)) {
break;
}
yield i;
}
}
module.exports = surferer;

View File

@ -0,0 +1,14 @@
'use strict';
const vlineStylo = {
w: {h:0, l:100}, // w
r: {h:10, l:45}, // r
o: {h:33, l:65}, // o
y: {h:55, l:50}, // y
g: {h:120, l:75}, // g
c: {h:190, l:50}, // c
b: {h:215, l:70}, // b
v: {h:280, l:55} // v
};
module.exports = vlineStylo;

View File

@ -0,0 +1,57 @@
'use strict';
const getX = require('./get-x.js');
const onml = require('onml');
const vlineStylo = require('./vline-stylo.js');
/* Zones
1 2 3
4 5 6
7 8 9
*/
const vline = (lane, pstate, i) => {
const {width, height, timescale, yStep, yOffset, topBarHeight, botBarHeight} = pstate;
const y = (i + .7) * yStep - yOffset;
const yMax = height - topBarHeight - botBarHeight;
return lane.vlines.map(vline => {
const t = vline.value * Math.pow(10, vline.mult - timescale);
const x = Math.round(getX(pstate, t));
const style = (vlineStylo[vline.style] || vlineStylo.w);
const color = `hsl(${style.h},100%,${style.l}%)`;
const symbol = ['path', {style: 'stroke-width: 1px; stroke: ' + color}];
if ((x < 0) || (x > width)) {
if ((y < 0) || (y > yMax)) { // Zones: 1, 7, 3, 9
return ['g'];
}
if (x < 0) { // Zone: 4
symbol[1].d = `M ${0} ${y} l 8 -4 v 8 z`;
return symbol;
}
// Zone: 6
symbol[1].d = `M ${width - 16} ${y} l -8 -4 v 8 z`;
return symbol;
}
const line = ['g', onml.tt(x, 0),
['rect', {x: -1, width: 3, height, fill: '#fff', filter: 'url(#neonGlow-' + vline.style + ')'}],
['rect', {width: 1, height, fill: color}]
];
if (y < 0) { // Zone: 2
symbol[1].d = `M ${x} ${topBarHeight} l 4 8 h -8 z`;
return ['g', line, symbol];
}
if (y > yMax) { // Zone: 8
symbol[1].d = `M ${x} ${yMax} l -4 -8 h 8 z`;
return ['g', line, symbol];
}
// Zone: 5
symbol[1].d = `M ${x} ${y} m-4 0 l 4 -4 l 4 4 l -4 4 z`;
return ['g', line, symbol];
});
};
module.exports = vline;

140
src/hook/wave-view/water.js Normal file
View File

@ -0,0 +1,140 @@
'use strict';
// PC based pipeline view
const tt = require('onml/tt.js');
const getX = require('./get-x.js');
const surferer = require('./surferer.js');
const sampler = require('./sampler.js');
const hash = require('./hash.js');
const pushBrick = (listing, pco, v, name, label, t) => {
let pc = v;
let tail = false;
let le = listing[pc];
if (le === undefined) {
pc = v - 2;
le = listing[pc];
tail = true;
}
if (le) {
if (pco[pc] === undefined) {
pco[pc] = {op: le.op, asm: le.asm, bricks: []};
}
pco[pc].bricks.push({name, label, t, tail});
}
};
const progress = (lane, desc, pstate) => {
const pco = {};
const { listing } = desc;
const { clock, othero } = lane;
if (desc.chango[clock.ref] === undefined) {
console.log(desc.chango, clock, clock.ref);
return pco;
}
const clockWave = desc.chango[clock.ref].wave;
const clockEr = surferer(clockWave, pstate);
// create samplers
for (const id of Object.keys(othero)) {
['pc', 'go'].map(role => {
const obj = othero[id][role];
obj.sampler = sampler(desc.chango[obj.ref].wave);
obj.sampler.next(0); // dry run
});
}
let count = 8000; // max number of pipeline bricks
outerLoop: for (const iClock of clockEr) {
const [tClock, vClock] = clockWave[iClock];
if (vClock) {
for (const key of Object.keys(othero)) {
const id = othero[key];
const go = id.go.sampler.next(tClock).value;
const pc = id.pc.sampler.next(tClock).value;
const KEY = key.toUpperCase();
if (go) {
pushBrick(listing, pco, pc, KEY, KEY, tClock);
if (count-- === 0) {
break outerLoop;
}
}
}
}
}
for (const [idx, key] of Object.keys(pco).entries()) {
if (pco[key] !== undefined) {
pco[key].idx = idx;
}
}
return pco;
};
const water = (lane, desc, pstate) => {
const { width, yStep } = pstate;
const brickWidth = getX(pstate, 2) - getX(pstate, 0);
const pco = progress(lane, desc, pstate);
const pcs = Object.keys(pco).map(e => Number(e)).sort();
const mLanes = [];
// const pco = getPco(desc, pstate, lane); // signals, dats, cycles, listing, state.cursor);
for (let j = 0; j < lane.len - 2; j++) {
const pc = pcs[j];
const pcd = pco[pc];
if (pc === undefined || pcd === undefined) {
break;
}
const asm = pcd.asm.replace(/<.+>/, '\u25C6');
const mLane = ['g', tt(0, Math.round(j * yStep))];
// striped background
if (j & 1) {
mLane.push(['rect', {width: width, height: yStep - 1, class: 'pc-odd'}]);
// ['rect', {width: width, height: yStep - 2, class: (j & 1) ? 'pc-odd' : 'pc-even'}],
}
// dotted separator
if ((j > 0) && (pc - pcs[j - 1] > 4)) {
mLane.push(['line', {class: 'gap', x1: 0, y1: 0, x2: width, y2: 0}]);
}
// row header
mLane.push(['text', {class: 'pc', 'xml:space': 'preserve', y: Math.round(yStep * .7)},
['tspan', {class: 'pc-addr'}, parseInt(pc, 10).toString(16).padStart(12, ' ')],
['tspan', {class: 'pc-opcode'}, pcd.op.padStart(9, ' ')],
' ',
['tspan', {class: 'pc-asm'}, asm]
]);
// bricks in row
pcd.bricks.map(e => {
mLane.push(['g', tt(Math.round(getX(pstate, e.t))),
['rect', {
class: (e.label === '') ? e.name : 'ct' + hash(e.name).toString(36),
width: brickWidth,
height: (e.tail ? (yStep >> 1) : yStep) - 3,
y: e.tail ? ((yStep >> 1) + 1) : 1,
'data-stage': e.label
}],
...((brickWidth > 20) ? [['text', {class: e.name, width: brickWidth, x: brickWidth / 2, y: 16}, e.label]] : [])
]);
});
mLanes.push(mLane);
}
return mLanes;
};
module.exports = water;

View File

@ -0,0 +1,202 @@
'use strict';
const get = require('lodash.get');
const diz = (cols, wires, path, rowIdx) => {
if (cols[0] !== 'DIZ') {
return;
}
const levelo = get(wires, path, false);
const arg1 = cols[1]; // pipeline stages
// find all potential candidates
const othero0 = Object.keys(levelo).reduce((res, name) => {
const m = name.match(arg1);
if (m) {
const {id, pc, go} = m.groups;
const stage = res[id] = (res[id] || {});
const desc = {name, ref: levelo[name]};
if (pc) {
stage[pc] = desc;
} else
if (go) {
stage[go] = desc;
} else {
console.log(desc);
}
}
return res;
}, {});
// filter out singles
const othero = Object.keys(othero0).reduce((res, name) => {
const val = othero0[name];
if (val.pc && val.go) {
res[name] = val;
}
return res;
}, {});
return {kind: 'DIZ', idx: rowIdx, othero, clock: {name: 'clock', ref: levelo.clock}};
};
exports.parser = wires => str => {
const arr = str.split('\n');
const path = [];
const labelo = {};
let nRow;
let mat;
const res = arr.map((row, rowIdx) => {
row = row.trim();
// Section with Parentheses
if (row[0] === '(') {
const cols = row.slice(1).split(/\s+/);
const res = diz(cols, wires, path, rowIdx); // || other commands
if (res) {
nRow = res;
return res;
}
}
if (row[0] === ')') {
if (nRow !== undefined) {
nRow.len = rowIdx - nRow.idx + 1;
nRow = undefined;
}
return {};
}
const rowo = {};
const cols = row.split(/\s+/);
cols.map(col => {
if (col === '...') { path.pop(); path.pop(); return; }
if (col === '..') { path.pop(); return; }
if (col === '.') { return; }
if (col === '/') { path.length = 0; return; }
mat = col.match(/^:(\S+)$/); if (mat) {
labelo[mat[1]] = rowo;
return;
}
mat = col.match(/^(\{)([^}]+)(\})$/); if (mat) {
const a = mat[2];
const b = a.split(',');
rowo.kind = 'brace';
rowo.body = b.reduce((res, e) => {
const ee = e.split(':');
if (ee.length === 2) {
const key = ee[0];
const val = labelo[ee[1]] || Number(ee[1]);
res[key] = val;
} else
if (ee.length === 1) {
res[ee[0]] = labelo[ee[0]] || {};
} else {
console.error(ee);
}
return res;
}, {});
return;
}
mat = col.match(/^%([<>^])?([+-])?([su])?([bodhHac])$/); if (mat) {
rowo.format = {
align: mat[1],
plus: mat[2],
sign: mat[3],
radix: mat[4]
};
return;
}
const newPath = path.concat(col);
const ref = get(wires, newPath, false);
if (typeof ref === 'string') {
rowo.name = col;
rowo.ref = ref;
} else
if (typeof ref === 'object') {
path.push(col);
}
});
return rowo;
});
return res;
};
exports.cmMode = function(CodeMirror, desc) {
const { wires } = desc;
CodeMirror.defineMode('waveql', function() {
return {
startState: function() {
return {path: []};
},
token: function (stream, stt) {
// const line = stream.lineOracle.line;
let mat;
if (stream.eatSpace()) { return null; }
mat = stream.match(/^\.\.\.(\s+|$)/); if (mat) { stt.path.pop(); stt.path.pop(); return 'punct'; }
mat = stream.match(/^\.\.(\s+|$)/); if (mat) { stt.path.pop(); return 'punct'; }
mat = stream.match(/^\.(\s+|$)/); if (mat) { return 'punct'; }
mat = stream.match(/^\/(\s+|$)/); if (mat) { stt.path.length = 0; return 'punct'; }
mat = stream.match(/^:(\S+)(\s+|$)/); if (mat) {
return 'label';
}
mat = stream.match(/^\{[^}]+\}(\s+|$)/); if (mat) {
return 'mark';
}
mat = stream.match(/^%([<>^])?([+-])?([su])?([bodhHac])(\s+|$)/); if (mat) {
return 'format';
}
mat = stream.match(/^(\S+)(\s+|$)/); if (mat) {
const sigName = mat[1];
const newPath = stt.path.concat(sigName);
const ref = get(wires, newPath, false);
if (typeof ref === 'string') {
return 'signal';
}
if (typeof ref === 'object') {
stt.path = newPath;
return 'path';
}
return 'comment';
}
}
};
});
CodeMirror.defineMIME('text/x-waveql', 'waveql');
};
exports.cmHint = (CodeMirror, desc) => {
const { wires } = desc;
return async (cm /*, options */) => {
const cursor = cm.getCursor();
const token = cm.getTokenAt(cursor);
const line = cm.getLine(cursor.line);
let start = cursor.ch;
let end = cursor.ch;
while (start && /\w/.test(line.charAt(start - 1))) --start;
while (end < line.length && /\w/.test(line.charAt(end))) ++end;
const cur = token.string.trim();
const list = get(wires, token.state.path, {});
const alls = ['/', '..'].concat(Object.keys(list));
const hints = alls.filter(e => e.match(cur));
return {
list: hints,
from: CodeMirror.Pos(cursor.line, start),
to: CodeMirror.Pos(cursor.line, end)
};
};
};

View File

@ -0,0 +1,20 @@
'use strict';
const xOffsetUpdate = (pstate, xOffsetNext) => {
let {width, xOffset, xScale, time, sidebarWidth} = pstate;
const xOffsetMax = sidebarWidth; // maximum offset
xOffsetNext = (xOffsetNext > xOffsetMax) ? xOffsetMax : xOffsetNext;
const xOffsetMin = width - xScale * time; // minimum offset
xOffsetNext = (xOffsetNext < xOffsetMin) ? xOffsetMin : xOffsetNext;
if (xOffsetNext === xOffset) {
return false; // exit without scroll
}
pstate.xOffset = xOffsetNext;
return true;
};
module.exports = xOffsetUpdate;

View File

@ -0,0 +1,24 @@
'use strict';
const xOffsetUpdate = require('./x-offset-update.js');
const xScaleUpdate = (pstate, xScaleNext) => {
const { xOffset, xCursor, xScale, xScaleMin, xScaleMax } = pstate;
xScaleNext = (xScaleNext > xScaleMax) ? xScaleMax : xScaleNext;
xScaleNext = (xScaleNext < xScaleMin) ? xScaleMin : xScaleNext;
console.log('pstate', pstate);
console.log('scale next', xScaleNext);
if (xScaleNext === xScale) {
return false; // exit without scale change
}
pstate.xScale = xScaleNext;
xOffsetUpdate(pstate, xCursor - (xCursor - xOffset) * xScaleNext / xScale);
return true;
};
module.exports = xScaleUpdate;

43
src/hook/wheel.js Normal file
View File

@ -0,0 +1,43 @@
import { globalLookup } from './global';
let changeView = globalLookup.view;
const config = {
maxScale: 1000,
minScale: 0.01,
scaleInterval: 0.1
}
/**
*
* @param {WheelEvent} event
*/
function wheelScale(event) {
// 鼠标滚轮往下是收缩, 往上是变大
if (event.deltaY > 0) {
const laterScale = changeView.currentScaleX + config.scaleInterval;
if (laterScale <= config.maxScale) {
changeView.currentScaleX = laterScale;
document.body.style.setProperty('--render-scale-x', laterScale);
}
} else if (event.deltaY < 0) {
const laterScale = changeView.currentScaleX - config.scaleInterval;
if (laterScale >= config.minScale) {
changeView.currentScaleX = laterScale;
document.body.style.setProperty('--render-scale-x', laterScale);
}
}
}
/**
*
* @param {WheelEvent} event
*/
function wheelTranslate(event) {
}
export {
wheelScale,
wheelTranslate
};

17
test/vertex.glsl Normal file
View File

@ -0,0 +1,17 @@
#version 300 es
in uvec3 pos;
out vec4 v_color;
uniform vec2 scale;
uniform vec2 offset;
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(
float(pos.x) * scale.x + offset.x + node[1] * tilt,
float(node[0]) * scale.y + offset.y,
1, 1
);
}