mirror of
https://github.com/enso-org/enso.git
synced 2024-12-19 13:51:45 +03:00
Wip/wd/dev (https://github.com/enso-org/ide/pull/492)
Original commit: 18971b05f2
This commit is contained in:
parent
2389718495
commit
8dbab93116
191
gui/src/js/package-lock.json
generated
191
gui/src/js/package-lock.json
generated
@ -10,27 +10,27 @@
|
||||
"integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA=="
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
|
||||
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
|
||||
"integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.8.3"
|
||||
"@babel/highlight": "^7.10.1"
|
||||
}
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
|
||||
"integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz",
|
||||
"integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
|
||||
"integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz",
|
||||
"integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.9.0",
|
||||
"@babel/helper-validator-identifier": "^7.10.1",
|
||||
"chalk": "^2.0.0",
|
||||
"js-tokens": "^4.0.0"
|
||||
}
|
||||
@ -1251,9 +1251,9 @@
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-4.0.2.tgz",
|
||||
"integrity": "sha512-+4X6qfhT/fk/5FD66395NrFLxCzD6FsGlpPwfwvnukdyfYbhiZB/FJltiT1XM5Q63rGGBSf9FPaNV3WpNHm54A==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-4.1.1.tgz",
|
||||
"integrity": "sha512-gOuIVmMCfHzSv3QBwLZjGDEDtYC73A5+tYcccEoq+Jd/h9rG/Mfc+h0+GEU+mpGhx86n7eBw6J/0BZ0zAmLkKg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": ">= 8"
|
||||
@ -1289,9 +1289,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz",
|
||||
"integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==",
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
"integrity": "sha512-B42Sxuaz09MhC3DDeW5kubRcQ5by4iuVQ0cRRWM2lggLzAa/KVom0Aft/208NgMvNQQZ86s5rVcqDdn/SH0/mg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@ -1307,6 +1307,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
|
||||
"integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA=="
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
@ -1727,25 +1732,25 @@
|
||||
"integrity": "sha512-NSjtqZ3x2kYiDp3Qezsgukx/AUzKPr3Xgf9by4cYt05ILWGAptepeeu0Uv+7MO+41o6ujhLixTou8979JGg2Kg=="
|
||||
},
|
||||
"app-builder-lib": {
|
||||
"version": "22.6.1",
|
||||
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.6.1.tgz",
|
||||
"integrity": "sha512-ENL7r+H7IBfDb4faeLASgndsXrAT7AV7m7yJjcpbFDXYma6an7ZWGFIvR0HJrsfiC5TIB8kdLJ/aMSImrrSi/Q==",
|
||||
"version": "22.7.0",
|
||||
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.7.0.tgz",
|
||||
"integrity": "sha512-blRKwV8h0ztualXS50ciCTo39tbuDGNS+ldcy8+KLvKXuT6OpYnSJ7M6MSfPT+xWatshMHJV1rJx3Tl+k/Sn/g==",
|
||||
"requires": {
|
||||
"7zip-bin": "~5.0.3",
|
||||
"@develar/schema-utils": "~2.6.5",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"bluebird-lst": "^1.0.9",
|
||||
"builder-util": "22.6.1",
|
||||
"builder-util-runtime": "8.7.0",
|
||||
"builder-util": "22.7.0",
|
||||
"builder-util-runtime": "8.7.1",
|
||||
"chromium-pickle-js": "^0.2.0",
|
||||
"debug": "^4.1.1",
|
||||
"ejs": "^3.1.2",
|
||||
"electron-publish": "22.6.1",
|
||||
"debug": "^4.2.0",
|
||||
"ejs": "^3.1.3",
|
||||
"electron-publish": "22.7.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"hosted-git-info": "^3.0.4",
|
||||
"is-ci": "^2.0.0",
|
||||
"isbinaryfile": "^4.0.6",
|
||||
"js-yaml": "^3.13.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
"lazy-val": "^1.0.4",
|
||||
"minimatch": "^3.0.4",
|
||||
"normalize-package-data": "^2.5.0",
|
||||
@ -1756,11 +1761,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
@ -2512,21 +2517,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"builder-util": {
|
||||
"version": "22.6.1",
|
||||
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.6.1.tgz",
|
||||
"integrity": "sha512-A9cF+bSHqRTSKIUHEyE92Tl0Uh12N7yZRH9bccIL3gRUwtp6ulF28LsjNIWTSQ1clZo2M895cT5PCrKzjPQFVg==",
|
||||
"version": "22.7.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.7.0.tgz",
|
||||
"integrity": "sha512-UV3MKL0mwjMq2y9JlBf28Cegpj0CrIXcjGkO0TXn+QZ6Yy9rY6lHOuUvpQ19ct2Qh1o+QSwH3Q1nKUf5viJBBg==",
|
||||
"requires": {
|
||||
"7zip-bin": "~5.0.3",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/fs-extra": "^8.1.0",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"app-builder-bin": "3.5.9",
|
||||
"bluebird-lst": "^1.0.9",
|
||||
"builder-util-runtime": "8.7.0",
|
||||
"builder-util-runtime": "8.7.1",
|
||||
"chalk": "^4.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"debug": "^4.2.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"is-ci": "^2.0.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
"source-map-support": "^0.5.19",
|
||||
"stat-mode": "^1.0.0",
|
||||
"temp-file": "^3.3.7"
|
||||
@ -2564,11 +2569,11 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
@ -2626,20 +2631,20 @@
|
||||
}
|
||||
},
|
||||
"builder-util-runtime": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.0.tgz",
|
||||
"integrity": "sha512-G1AqqVM2vYTrSFR982c1NNzwXKrGLQjVjaZaWQdn4O6Z3YKjdMDofw88aD9jpyK9ZXkrCxR0tI3Qe9wNbyTlXg==",
|
||||
"version": "8.7.1",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.1.tgz",
|
||||
"integrity": "sha512-uEBH1nAnTvzjcsrh2XI3qOzJ39h0+9kuIuwj+kCc3a07TZNGShfJcai8fFzL3mNgGjEFxoq+XMssR11r+FOFSg==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"debug": "^4.2.0",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3176,6 +3181,17 @@
|
||||
"webpack-sources": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
|
||||
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"cacache": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
|
||||
@ -3226,11 +3242,12 @@
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
|
||||
"integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"requires": {
|
||||
"ajv": "^6.12.0",
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
@ -4184,15 +4201,15 @@
|
||||
}
|
||||
},
|
||||
"dmg-builder": {
|
||||
"version": "22.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.6.1.tgz",
|
||||
"integrity": "sha512-jUTN0acP15puzevtQASj7QEPgUGpedWSuSnOwR/++JbeYRTwU2oro09h/KZnaeMcxgxjdmT3tYLJeY1XUfPbRg==",
|
||||
"version": "22.7.0",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.7.0.tgz",
|
||||
"integrity": "sha512-5Ea2YEz6zSNbyGzZD+O9/MzmaXb6oa15cSKWo4JQ1xP4rorOpte7IOj2jcwYjtc+Los2gu1lvT314OC1OZIWgg==",
|
||||
"requires": {
|
||||
"app-builder-lib": "22.6.1",
|
||||
"builder-util": "22.6.1",
|
||||
"app-builder-lib": "22.7.0",
|
||||
"builder-util": "22.7.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"iconv-lite": "^0.5.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
"sanitize-filename": "^1.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -4349,17 +4366,17 @@
|
||||
}
|
||||
},
|
||||
"electron-builder": {
|
||||
"version": "22.6.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.6.1.tgz",
|
||||
"integrity": "sha512-3/VNg9GfXKHM53TilFtfF1+bsAR8THK1XHgeqCpsiequa02J9jTPc/DhpCUKQPkrs6/EIGxP7uboop7XYoew0Q==",
|
||||
"version": "22.7.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.7.0.tgz",
|
||||
"integrity": "sha512-t6E3oMutpST64YWbZCg7HodEwJOsnjUF1vnDIHm2MW6CFZPX8tlCK6efqaV66LU0E0Nkp/JH6TE5bCqQ1+VdPQ==",
|
||||
"requires": {
|
||||
"@types/yargs": "^15.0.5",
|
||||
"app-builder-lib": "22.6.1",
|
||||
"app-builder-lib": "22.7.0",
|
||||
"bluebird-lst": "^1.0.9",
|
||||
"builder-util": "22.6.1",
|
||||
"builder-util-runtime": "8.7.0",
|
||||
"builder-util": "22.7.0",
|
||||
"builder-util-runtime": "8.7.1",
|
||||
"chalk": "^4.0.0",
|
||||
"dmg-builder": "22.6.1",
|
||||
"dmg-builder": "22.7.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"is-ci": "^2.0.0",
|
||||
"lazy-val": "^1.0.4",
|
||||
@ -4446,14 +4463,14 @@
|
||||
"integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw=="
|
||||
},
|
||||
"electron-publish": {
|
||||
"version": "22.6.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.6.1.tgz",
|
||||
"integrity": "sha512-/MkS47ospdSfAFW5Jp52OzYou14HhGJpZ51uAc3GJ5rCfACeqpimC/n1ajRLE3hcXxTWfd3t9MCuClq5jrUO5w==",
|
||||
"version": "22.7.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.7.0.tgz",
|
||||
"integrity": "sha512-hmU69xlb6vvAV3QfpHYDlkdZMFdBAgDbptoxbLFrnTq5bOkcL8AaDbvxeoZ4+lvqgs29NwqGpkHo2oN+p/hCfg==",
|
||||
"requires": {
|
||||
"@types/fs-extra": "^8.1.0",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"bluebird-lst": "^1.0.9",
|
||||
"builder-util": "22.6.1",
|
||||
"builder-util-runtime": "8.7.0",
|
||||
"builder-util": "22.7.0",
|
||||
"builder-util-runtime": "8.7.1",
|
||||
"chalk": "^4.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"lazy-val": "^1.0.4",
|
||||
@ -4517,9 +4534,9 @@
|
||||
}
|
||||
},
|
||||
"mime": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz",
|
||||
"integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w=="
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
|
||||
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.1.0",
|
||||
@ -6495,9 +6512,9 @@
|
||||
}
|
||||
},
|
||||
"global-agent": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.8.tgz",
|
||||
"integrity": "sha512-VpBe/rhY6Rw2VDOTszAMNambg+4Qv8j0yiTNDYEXXXxkUNGWLHp8A3ztK4YDBbFNcWF4rgsec6/5gPyryya/+A==",
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.9.tgz",
|
||||
"integrity": "sha512-hylHMbK50BzJbUmQ0LPYK1+AY862Xkvje4DAaonQDfMewGIe7/+XHfG90FORsEsrfw7pskhwV4cFAYgVAPNrdw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"boolean": "^3.0.0",
|
||||
@ -8255,9 +8272,9 @@
|
||||
}
|
||||
},
|
||||
"min-indent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
|
||||
"integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
|
||||
"dev": true
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
@ -8560,9 +8577,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.17.0.tgz",
|
||||
"integrity": "sha512-dFRAA0ACk/aBo0TIXQMEWMLUTyWYYT8OBYIzLmEUrQTElGRjxDCvyBZIsDL0QA7QCaj9PrawhOmTEdsuLY4uOQ==",
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz",
|
||||
"integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
},
|
||||
@ -11635,9 +11652,9 @@
|
||||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.3.tgz",
|
||||
"integrity": "sha512-r5ImcL6QyzQGVimQoov3aL2ZScywrOgBXGndbWrdehKoSvGe/RmiE5Jpw/v+GvxODt6l2tpBXwA7n+qZVlHBMA==",
|
||||
"version": "3.9.4",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.4.tgz",
|
||||
"integrity": "sha512-8RZBJq5smLOa7KslsNsVcSH+KOXf1uDU8yqLeNuVKwmT0T3FA0ZoXlinQfRad7SDcbZZRZE4ov+2v71EnxNyCA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
@ -8,14 +8,15 @@ use crate::animation;
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Position ===
|
||||
// ================
|
||||
// =============
|
||||
// === Value ===
|
||||
// =============
|
||||
|
||||
/// The type of the position of the simulation. In particular, the Position could be `f32`
|
||||
/// (1-dimensional simulation), or `Vector<f32>` (3-dimensional simulation).
|
||||
pub trait Position
|
||||
= 'static + Copy + Default + Debug + Magnitude + Normalize
|
||||
/// The type of the value of the simulation. In particular, the Value could be `f32`
|
||||
/// (1-dimensional simulation), or `Vector3<f32>` (3-dimensional simulation).
|
||||
pub trait Value
|
||||
= 'static + Copy + Default + Debug + Normalize
|
||||
+ Magnitude <Output=f32>
|
||||
+ Neg< Output=Self>
|
||||
+ Sub<Self , Output=Self>
|
||||
+ Add<Self , Output=Self>
|
||||
@ -93,17 +94,17 @@ impl Thresholds {
|
||||
/// A fixed step physics simulator used to simulate `PhysicsState`.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct SimulationData<T> {
|
||||
position : T,
|
||||
target_position : T,
|
||||
velocity : T,
|
||||
mass : Mass,
|
||||
spring : Spring,
|
||||
drag : Drag,
|
||||
thresholds : Thresholds,
|
||||
active : bool,
|
||||
value : T,
|
||||
target_value : T,
|
||||
velocity : T,
|
||||
mass : Mass,
|
||||
spring : Spring,
|
||||
drag : Drag,
|
||||
thresholds : Thresholds,
|
||||
active : bool,
|
||||
}
|
||||
|
||||
impl<T:Position> SimulationData<T> {
|
||||
impl<T:Value> SimulationData<T> {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
@ -113,30 +114,30 @@ impl<T:Position> SimulationData<T> {
|
||||
fn step(&mut self, delta_seconds:f32) {
|
||||
if self.active {
|
||||
let velocity = self.velocity.magnitude();
|
||||
let distance = (self.position - self.target_position).magnitude();
|
||||
let distance = (self.value - self.target_value).magnitude();
|
||||
let snap_velocity = velocity < self.thresholds.speed;
|
||||
let snap_distance = distance < self.thresholds.distance;
|
||||
let should_snap = snap_velocity && snap_distance;
|
||||
if should_snap {
|
||||
self.position = self.target_position;
|
||||
self.value = self.target_value;
|
||||
self.velocity = default();
|
||||
self.active = false;
|
||||
} else {
|
||||
let force = self.spring_force() + self.drag_force();
|
||||
let acceleration = force / self.mass.value;
|
||||
self.velocity = self.velocity + acceleration * delta_seconds;
|
||||
self.position = self.position + self.velocity * delta_seconds;
|
||||
self.value = self.value + self.velocity * delta_seconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute spring force.
|
||||
fn spring_force(&self) -> T {
|
||||
let position_delta = self.target_position - self.position;
|
||||
let distance = position_delta.magnitude();
|
||||
let value_delta = self.target_value - self.value;
|
||||
let distance = value_delta.magnitude();
|
||||
if distance > 0.0 {
|
||||
let coefficient = distance * self.spring.value;
|
||||
position_delta.normalize() * coefficient
|
||||
value_delta.normalize() * coefficient
|
||||
} else {
|
||||
default()
|
||||
}
|
||||
@ -152,44 +153,44 @@ impl<T:Position> SimulationData<T> {
|
||||
// === Getters ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T:Position> SimulationData<T> {
|
||||
pub fn position (&self) -> T { self.position }
|
||||
pub fn target_position (&self) -> T { self.target_position }
|
||||
pub fn velocity (&self) -> T { self.velocity }
|
||||
pub fn mass (&self) -> Mass { self.mass }
|
||||
pub fn spring (&self) -> Spring { self.spring }
|
||||
pub fn drag (&self) -> Drag { self.drag }
|
||||
pub fn thresholds (&self) -> Thresholds { self.thresholds }
|
||||
pub fn active (&self) -> bool { self.active }
|
||||
impl<T:Value> SimulationData<T> {
|
||||
pub fn value (&self) -> T { self.value }
|
||||
pub fn target_value (&self) -> T { self.target_value }
|
||||
pub fn velocity (&self) -> T { self.velocity }
|
||||
pub fn mass (&self) -> Mass { self.mass }
|
||||
pub fn spring (&self) -> Spring { self.spring }
|
||||
pub fn drag (&self) -> Drag { self.drag }
|
||||
pub fn thresholds (&self) -> Thresholds { self.thresholds }
|
||||
pub fn active (&self) -> bool { self.active }
|
||||
}
|
||||
|
||||
|
||||
// === Setters ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T:Position> SimulationData<T> {
|
||||
impl<T:Value> SimulationData<T> {
|
||||
pub fn set_velocity (&mut self, velocity:T) { self.velocity = velocity; }
|
||||
pub fn set_mass (&mut self, mass:Mass) { self.mass = mass; }
|
||||
pub fn set_spring (&mut self, spring:Spring) { self.spring = spring; }
|
||||
pub fn set_drag (&mut self, drag:Drag) { self.drag = drag; }
|
||||
pub fn set_thresholds (&mut self, thresholds:Thresholds) { self.thresholds = thresholds; }
|
||||
|
||||
pub fn set_position(&mut self, position:T) {
|
||||
pub fn set_value(&mut self, value:T) {
|
||||
self.active = true;
|
||||
self.position = position;
|
||||
self.value = value;
|
||||
}
|
||||
|
||||
pub fn set_target_position(&mut self, target_position:T) {
|
||||
pub fn set_target_value(&mut self, target_value:T) {
|
||||
self.active = true;
|
||||
self.target_position = target_position;
|
||||
self.target_value = target_value;
|
||||
}
|
||||
|
||||
pub fn update_position<F:FnOnce(T)->T>(&mut self, f:F) {
|
||||
self.set_position(f(self.position()));
|
||||
pub fn update_value<F:FnOnce(T)->T>(&mut self, f:F) {
|
||||
self.set_value(f(self.value()));
|
||||
}
|
||||
|
||||
pub fn update_target_position<F:FnOnce(T)->T>(&mut self, f:F) {
|
||||
self.set_target_position(f(self.target_position()));
|
||||
pub fn update_target_value<F:FnOnce(T)->T>(&mut self, f:F) {
|
||||
self.set_target_value(f(self.target_value()));
|
||||
}
|
||||
|
||||
pub fn update_velocity<F:FnOnce(T)->T>(&mut self, f:F) {
|
||||
@ -211,6 +212,12 @@ impl<T:Position> SimulationData<T> {
|
||||
pub fn update_thresholds<F:FnOnce(Thresholds)->Thresholds>(&mut self, f:F) {
|
||||
self.set_thresholds(f(self.thresholds()));
|
||||
}
|
||||
|
||||
pub fn skip(&mut self) {
|
||||
self.active = false;
|
||||
self.value = self.target_value;
|
||||
self.velocity = default();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -226,7 +233,7 @@ pub struct Simulation<T:Copy> {
|
||||
data : Rc<Cell<SimulationData<T>>>
|
||||
}
|
||||
|
||||
impl<T:Position> Simulation<T> {
|
||||
impl<T:Value> Simulation<T> {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
@ -244,17 +251,17 @@ impl<T:Position> Simulation<T> {
|
||||
// === Getters ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T:Position> Simulation<T> {
|
||||
impl<T:Value> Simulation<T> {
|
||||
pub fn active(&self) -> bool {
|
||||
self.data.get().active()
|
||||
}
|
||||
|
||||
pub fn position(&self) -> T {
|
||||
self.data.get().position()
|
||||
pub fn value(&self) -> T {
|
||||
self.data.get().value()
|
||||
}
|
||||
|
||||
pub fn target_position(&self) -> T {
|
||||
self.data.get().target_position()
|
||||
pub fn target_value(&self) -> T {
|
||||
self.data.get().target_value()
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,7 +269,7 @@ impl<T:Position> Simulation<T> {
|
||||
// === Setters ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T:Position> Simulation<T> {
|
||||
impl<T:Value> Simulation<T> {
|
||||
pub fn set_mass(&self, mass:Mass) {
|
||||
self.data.update(|mut sim| {sim.set_mass(mass); sim});
|
||||
}
|
||||
@ -279,16 +286,20 @@ impl<T:Position> Simulation<T> {
|
||||
self.data.update(|mut sim| {sim.set_velocity(velocity); sim});
|
||||
}
|
||||
|
||||
pub fn set_position(&self, position:T) {
|
||||
self.data.update(|mut sim| {sim.set_position(position); sim});
|
||||
pub fn set_value(&self, value:T) {
|
||||
self.data.update(|mut sim| {sim.set_value(value); sim});
|
||||
}
|
||||
|
||||
pub fn set_target_position(&self, target_position:T) {
|
||||
self.data.update(|mut sim| {sim.set_target_position(target_position); sim});
|
||||
pub fn set_target_value(&self, target_value:T) {
|
||||
self.data.update(|mut sim| {sim.set_target_value(target_value); sim});
|
||||
}
|
||||
|
||||
pub fn update_target_position<F:FnOnce(T)->T>(&self, f:F) {
|
||||
self.data.update(|mut sim| {sim.update_target_position(f); sim});
|
||||
pub fn update_target_value<F:FnOnce(T)->T>(&self, f:F) {
|
||||
self.data.update(|mut sim| {sim.update_target_value(f); sim});
|
||||
}
|
||||
|
||||
pub fn skip(&self) {
|
||||
self.data.update(|mut sim| {sim.skip(); sim});
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +321,7 @@ pub type DynSimulator<T> = Simulator<T,Box<dyn Fn(T)>>;
|
||||
#[derive(CloneRef,Derivative,Shrinkwrap)]
|
||||
#[derivative(Clone(bound=""))]
|
||||
#[derivative(Debug(bound=""))]
|
||||
pub struct Simulator<T:Position,Cb> {
|
||||
pub struct Simulator<T:Value,Cb> {
|
||||
#[shrinkwrap(main_field)]
|
||||
simulation : Simulation<T>,
|
||||
animation_loop : Rc<CloneCell<Option<FixedFrameRateAnimationStep<T,Cb>>>>,
|
||||
@ -319,7 +330,7 @@ pub struct Simulator<T:Position,Cb> {
|
||||
callback : Rc<Cb>,
|
||||
}
|
||||
|
||||
impl<T:Position,Cb> Simulator<T,Cb>
|
||||
impl<T:Value,Cb> Simulator<T,Cb>
|
||||
where Cb : Callback<T> {
|
||||
/// Constructor.
|
||||
pub fn new(callback:Cb) -> Self {
|
||||
@ -334,7 +345,7 @@ where Cb : Callback<T> {
|
||||
// === Setters ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T:Position,Cb> Simulator<T,Cb>
|
||||
impl<T:Value,Cb> Simulator<T,Cb>
|
||||
where Cb : Callback<T> {
|
||||
pub fn set_callback(&mut self, callback:Cb) {
|
||||
let callback = Rc::new(callback);
|
||||
@ -343,26 +354,32 @@ where Cb : Callback<T> {
|
||||
self.start();
|
||||
}
|
||||
|
||||
pub fn set_position(&self, position:T) {
|
||||
self.simulation.set_position(position);
|
||||
pub fn set_value(&self, value:T) {
|
||||
self.simulation.set_value(value);
|
||||
self.start();
|
||||
}
|
||||
|
||||
pub fn set_target_position(&self, target_position:T) {
|
||||
self.simulation.set_target_position(target_position);
|
||||
pub fn set_target_value(&self, target_value:T) {
|
||||
self.simulation.set_target_value(target_value);
|
||||
self.start();
|
||||
}
|
||||
|
||||
pub fn update_target_position<F:FnOnce(T)->T>(&self, f:F) {
|
||||
self.simulation.update_target_position(f);
|
||||
pub fn update_target_value<F:FnOnce(T)->T>(&self, f:F) {
|
||||
self.simulation.update_target_value(f);
|
||||
self.start();
|
||||
}
|
||||
|
||||
pub fn skip(&self) {
|
||||
self.simulation.skip();
|
||||
self.stop();
|
||||
(self.callback)(self.simulation.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Private API ===
|
||||
|
||||
impl<T:Position,Cb> Simulator<T,Cb>
|
||||
impl<T:Value,Cb> Simulator<T,Cb>
|
||||
where Cb : Callback<T> {
|
||||
fn init(self) -> Self {
|
||||
self.start();
|
||||
@ -388,14 +405,14 @@ where Cb : Callback<T> {
|
||||
/// Alias for `FixedFrameRateLoop` with specified step callback.
|
||||
pub type FixedFrameRateAnimationStep<T,Cb> = animation::FixedFrameRateLoop<Step<T,Cb>>;
|
||||
pub type Step<T,Cb> = impl Fn(animation::TimeInfo);
|
||||
fn step<T:Position,Cb>(simulator:&Simulator<T,Cb>) -> Step<T,Cb>
|
||||
fn step<T:Value,Cb>(simulator:&Simulator<T,Cb>) -> Step<T,Cb>
|
||||
where Cb : Callback<T> {
|
||||
let this = simulator.clone_ref();
|
||||
move |time:animation::TimeInfo| {
|
||||
let delta_seconds = time.frame / 1000.0;
|
||||
if this.simulation.active() {
|
||||
this.simulation.step(delta_seconds);
|
||||
(this.callback)(this.simulation.position());
|
||||
(this.callback)(this.simulation.value());
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ use nalgebra::Vector4;
|
||||
/// just want it, for example to match the behavior of color mixing in web browsers, which is
|
||||
/// broken for many years already:
|
||||
/// https://stackoverflow.com/questions/60179850/webgl-2-0-canvas-blending-with-html-in-linear-color-space
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
#[derive(Clone,Copy,Debug,Default,PartialEq)]
|
||||
pub struct Color<D> {
|
||||
/// The underlying color representation. It is either `Alpha` or a color space instance.
|
||||
pub data : D
|
||||
@ -225,3 +225,11 @@ impl<C> From<C> for Alpha<C> {
|
||||
Self {alpha,color}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C:Default> Default for Alpha<C> {
|
||||
fn default() -> Self {
|
||||
let alpha = 1.0;
|
||||
let color = default();
|
||||
Self {alpha,color}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ macro_rules! define_color_space {
|
||||
pub type $a_name = Color<Alpha<$data_name>>;
|
||||
|
||||
$(#[$($meta)*])*
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
#[derive(Clone,Copy,Debug,Default,PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct $data_name {
|
||||
$(pub $comp : f32),*
|
||||
@ -26,14 +26,15 @@ macro_rules! define_color_space {
|
||||
|
||||
impl $data_name {
|
||||
/// Constructor.
|
||||
pub fn new($($comp:f32),*) -> Self {
|
||||
pub fn new($($comp:impl Into<f32>),*) -> Self {
|
||||
$(let $comp = $comp.into();)*
|
||||
Self {$($comp),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// Constructor.
|
||||
pub fn new($($comp:f32),*) -> Self {
|
||||
pub fn new($($comp:impl Into<f32>),*) -> Self {
|
||||
let data = $data_name::new($($comp),*);
|
||||
Self {data}
|
||||
}
|
||||
@ -41,7 +42,8 @@ macro_rules! define_color_space {
|
||||
|
||||
impl $a_name {
|
||||
/// Constructor.
|
||||
pub fn new($($comp:f32),*,alpha:f32) -> Self {
|
||||
pub fn new($($comp:impl Into<f32>),*,alpha:impl Into<f32>) -> Self {
|
||||
let alpha = alpha.into();
|
||||
let color = $data_name::new($($comp),*);
|
||||
let data = Alpha {alpha,color};
|
||||
Self {data}
|
||||
|
@ -23,7 +23,7 @@ use events::ZoomEvent;
|
||||
#[derive(Debug)]
|
||||
pub struct Navigator {
|
||||
_events : NavigatorEvents,
|
||||
simulator : physics::inertia::DynSimulator<Point3>,
|
||||
simulator : physics::inertia::DynSimulator<V3>,
|
||||
resize_callback : callback::Handle
|
||||
}
|
||||
|
||||
@ -39,12 +39,12 @@ impl Navigator {
|
||||
Self {simulator,_events,resize_callback}
|
||||
}
|
||||
|
||||
fn create_simulator(camera:&Camera2d) -> physics::inertia::DynSimulator<Point3> {
|
||||
fn create_simulator(camera:&Camera2d) -> physics::inertia::DynSimulator<V3> {
|
||||
let camera_ref = camera.clone_ref();
|
||||
let update = Box::new(move |p:Point3| camera_ref.set_position(p.into()));
|
||||
let update = Box::new(move |p:V3| camera_ref.set_position(p.into()));
|
||||
let simulator = physics::inertia::DynSimulator::new(update);
|
||||
simulator.set_position(camera.position().into());
|
||||
simulator.set_target_position(camera.position().into());
|
||||
simulator.set_value(camera.position().into());
|
||||
simulator.set_target_value(camera.position().into());
|
||||
simulator
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ impl Navigator {
|
||||
, min_zoom : f32
|
||||
, max_zoom : f32
|
||||
, zoom_speed : f32
|
||||
) -> (physics::inertia::DynSimulator<Point3>,callback::Handle,NavigatorEvents) {
|
||||
) -> (physics::inertia::DynSimulator<V3>,callback::Handle,NavigatorEvents) {
|
||||
let simulator = Self::create_simulator(&camera);
|
||||
let panning_callback = enclose!((dom,camera,mut simulator) move |pan: PanEvent| {
|
||||
let fovy_slope = camera.half_fovy_slope();
|
||||
@ -64,15 +64,15 @@ impl Navigator {
|
||||
|
||||
let dx = pan.movement.x * movement_scale_for_distance;
|
||||
let dy = pan.movement.y * movement_scale_for_distance;
|
||||
let diff = Point3::new(dx,dy,0.0);
|
||||
simulator.update_target_position(|p| p + diff);
|
||||
let diff = V3::new(dx,dy,0.0);
|
||||
simulator.update_target_value(|p| p + diff);
|
||||
});
|
||||
|
||||
let resize_callback = camera.add_screen_update_callback(
|
||||
enclose!((mut simulator,camera) move |_:&Vector2<f32>| {
|
||||
let position = camera.position().into();
|
||||
simulator.set_position(position);
|
||||
simulator.set_target_position(position);
|
||||
simulator.set_value(position);
|
||||
simulator.set_target_value(position);
|
||||
simulator.set_velocity(default());
|
||||
})
|
||||
);
|
||||
@ -87,8 +87,8 @@ impl Navigator {
|
||||
let x = -normalized.x * camera.screen().aspect();
|
||||
let y = -normalized.y;
|
||||
let z = half_height / camera.half_fovy_slope();
|
||||
let direction = Point3::new(x,y,z).normalize();
|
||||
let mut position = simulator.target_position();
|
||||
let direction = V3::new(x,y,z).normalize();
|
||||
let mut position = simulator.target_value();
|
||||
let min_zoom = camera.clipping().near + min_zoom;
|
||||
let zoom_amount = zoom.amount * position.z;
|
||||
let direction = direction * zoom_amount;
|
||||
@ -100,7 +100,7 @@ impl Navigator {
|
||||
else if too_close { min_zoom_limit / direction.z }
|
||||
else { 1.0 };
|
||||
position += direction * zoom_factor;
|
||||
simulator.set_target_position(position);
|
||||
simulator.set_target_value(position);
|
||||
});
|
||||
(simulator,resize_callback, NavigatorEvents::new(&dom, panning_callback, zoom_callback, zoom_speed))
|
||||
}
|
||||
|
@ -719,6 +719,13 @@ pub trait ObjectOps : Object {
|
||||
self.display_object().rc.set_position(t);
|
||||
}
|
||||
|
||||
fn set_position_xy(&self, t:Vector2<f32>) {
|
||||
self.mod_position(|p| {
|
||||
p.x = t.x;
|
||||
p.y = t.y;
|
||||
})
|
||||
}
|
||||
|
||||
fn set_scale(&self, t:Vector3<f32>) {
|
||||
self.display_object().rc.set_scale(t);
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ define_sdf_shapes! {
|
||||
PlaneAngle (angle:Angle<Radians>) {
|
||||
float pi_2 = 2.0 * PI;
|
||||
float angle_norm = value(angle) / pi_2;
|
||||
angle_norm = abs(mod(angle_norm,2.0)-1.0);
|
||||
angle_norm = 1.0 - abs(mod(angle_norm,2.0)-1.0);
|
||||
float angle_rad = angle_norm * pi_2;
|
||||
float off = angle_norm - 0.5; // Fixes artifacts with 0 and 360 degrees.
|
||||
float distance = abs(position).x*cos(angle_rad/2.0) - position.y*sin(angle_rad/2.0) - off;
|
||||
@ -263,8 +263,9 @@ define_sdf_shapes! {
|
||||
// === Triangle ===
|
||||
|
||||
Triangle (width:f32, height:f32) {
|
||||
vec2 norm = normalize(vec2(height,width/2.0));
|
||||
float dist = max(abs(position).x*norm.x + position.y*norm.y - height*norm.y, -position.y);
|
||||
vec2 norm = normalize(vec2(height,width/2.0));
|
||||
float pos_y = -position.y - height/2.0;
|
||||
float dist = max(abs(position).x*norm.x + position.y*norm.y - height/2.0*norm.y, pos_y);
|
||||
return bound_sdf(dist,bounding_box(width,height/2.0));
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,23 @@ pub use crate::math::topology::unit::Degrees;
|
||||
// =====================
|
||||
|
||||
/// Provides a `px` method to every unit that can be converted to a pixel distance.
|
||||
#[allow(missing_docs)]
|
||||
pub trait PixelDistance {
|
||||
type Output;
|
||||
/// Distance in pixels.
|
||||
fn px(&self) -> Var<Distance<Pixels>>;
|
||||
fn px(&self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<T: unit::PixelDistance> PixelDistance for T {
|
||||
fn px(&self) -> Var<Distance<Pixels>> {
|
||||
impl PixelDistance for i32 {
|
||||
type Output = Var<Distance<Pixels>>;
|
||||
fn px(&self) -> Self::Output {
|
||||
unit::PixelDistance::px(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelDistance for f32 {
|
||||
type Output = Var<Distance<Pixels>>;
|
||||
fn px(&self) -> Self::Output {
|
||||
unit::PixelDistance::px(self).into()
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::color;
|
||||
use crate::display::shape::primitive::def::unit::PixelDistance;
|
||||
use crate::math::algebra::Acos;
|
||||
use crate::math::algebra::Asin;
|
||||
use crate::math::algebra::Cos;
|
||||
@ -21,6 +22,7 @@ use nalgebra::Scalar;
|
||||
use std::ops::*;
|
||||
|
||||
|
||||
|
||||
// ======================
|
||||
// === VarInitializer ===
|
||||
// ======================
|
||||
@ -55,6 +57,15 @@ impl<T,U,V> VarInitializerMarker<Var<Unit<T,Anything,V>>> for Unit<T,U,V> where
|
||||
impl<T,S1,S2> VarInitializerMarker<Var<Vector2<T>>> for (S1,S2)
|
||||
where T:Scalar, S1:VarInitializerMarkerNested<Var<T>>, S2:VarInitializerMarkerNested<Var<T>> {}
|
||||
|
||||
impl<T:Scalar> VarInitializerMarker<Var<Vector2<T>>> for Var<V2<T>> {}
|
||||
impl<T:Scalar> VarInitializerMarker<Var<Vector3<T>>> for Var<V3<T>> {}
|
||||
impl<T:Scalar> VarInitializerMarker<Var<Vector4<T>>> for Var<V4<T>> {}
|
||||
|
||||
impl<T:Scalar> VarInitializerMarker<Var<Vector2<T>>> for &Var<V2<T>> {}
|
||||
impl<T:Scalar> VarInitializerMarker<Var<Vector3<T>>> for &Var<V3<T>> {}
|
||||
impl<T:Scalar> VarInitializerMarker<Var<Vector4<T>>> for &Var<V4<T>> {}
|
||||
|
||||
|
||||
|
||||
// === Nested ===
|
||||
|
||||
@ -192,6 +203,81 @@ impl<T:Scalar> Dim3 for Var<Vector3<T>> {
|
||||
|
||||
|
||||
|
||||
impl<T:Scalar> HasComponents for Var<V2<T>> {
|
||||
type Component = Var<T>;
|
||||
}
|
||||
|
||||
impl<T:Scalar> HasComponents for Var<V3<T>> {
|
||||
type Component = Var<T>;
|
||||
}
|
||||
|
||||
impl<T:Scalar> Dim1 for Var<V2<T>> {
|
||||
fn x(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(t.x),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.x",t).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Scalar> Dim2 for Var<V2<T>> {
|
||||
fn y(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(t.y),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.y",t).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Scalar> Dim1 for Var<V3<T>> {
|
||||
fn x(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(t.x),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.x",t).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Scalar> Dim2 for Var<V3<T>> {
|
||||
fn y(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(t.y),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.y",t).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Scalar> Dim3 for Var<V3<T>> {
|
||||
fn z(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(t.z),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.z",t).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelDistance for Var<V2<f32>> {
|
||||
type Output = Var<V2<Distance<Pixels>>>;
|
||||
fn px(&self) -> Self::Output {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(V2(Distance::new(t.x),Distance::new(t.y))),
|
||||
Self::Dynamic (t) => Var::Dynamic(t.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelDistance for Var<V3<f32>> {
|
||||
type Output = Var<V3<Distance<Pixels>>>;
|
||||
fn px(&self) -> Self::Output {
|
||||
match self {
|
||||
Self::Static (t) => Var::Static(V3(Distance::new(t.x),Distance::new(t.y),Distance::new(t.z))),
|
||||
Self::Dynamic (t) => Var::Dynamic(t.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Operators ===
|
||||
// =================
|
||||
|
@ -45,17 +45,17 @@ impl TextFieldMouseFrp {
|
||||
let set_cursor_action = Self::set_cursor_lambda(text_field_ptr.clone());
|
||||
let select_action = Self::select_lambda(text_field_ptr);
|
||||
frp::new_network! { text_field
|
||||
def is_inside = mouse.position.map(is_inside);
|
||||
def click_in = mouse.press.gate(&is_inside);
|
||||
def click_in_bool = click_in.constant(true);
|
||||
def mouse_up_bool = mouse.release.constant(false);
|
||||
def selecting = click_in_bool.merge(&mouse_up_bool);
|
||||
def multicursor = keyboard.keyboard.key_mask.map(is_multicursor_mode);
|
||||
def block_selection = keyboard.keyboard.key_mask.map(is_block_selection);
|
||||
def click_in_pos = mouse.position.sample(&click_in);
|
||||
def select_pos = mouse.position.gate(&selecting);
|
||||
def set_cursor_action = click_in_pos.map2(&multicursor,set_cursor_action);
|
||||
def select_action = select_pos.map2(&block_selection,select_action);
|
||||
is_inside <- mouse.position.map(is_inside);
|
||||
click_in <- mouse.press.gate(&is_inside);
|
||||
click_in_bool <- click_in.constant(true);
|
||||
mouse_up_bool <- mouse.release.constant(false);
|
||||
selecting <- any (click_in_bool,mouse_up_bool);
|
||||
multicursor <- keyboard.keyboard.key_mask.map(is_multicursor_mode);
|
||||
block_selection <- keyboard.keyboard.key_mask.map(is_block_selection);
|
||||
click_in_pos <- mouse.position.sample(&click_in);
|
||||
select_pos <- mouse.position.gate(&selecting);
|
||||
set_cursor_action <- click_in_pos.map2(&multicursor,set_cursor_action);
|
||||
select_action <- select_pos.map2(&block_selection,select_action);
|
||||
}
|
||||
let network = text_field;
|
||||
Self {mouse,network,click_in,selecting,multicursor,set_cursor_action,select_action}
|
||||
|
@ -208,12 +208,14 @@ impl<T> From<T> for Value
|
||||
}
|
||||
}
|
||||
|
||||
impl Semigroup for Value {
|
||||
impl PartialSemigroup<&Value> for Value {
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
*self = other.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn concat_mut_take(&mut self, other:Self) {
|
||||
impl PartialSemigroup<Value> for Value {
|
||||
fn concat_mut(&mut self, other:Self) {
|
||||
*self = other
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ impl Theme {
|
||||
}
|
||||
}
|
||||
|
||||
impl Semigroup for Theme {
|
||||
impl PartialSemigroup<&Theme> for Theme {
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
self.tree.concat_mut(&other.tree);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::animation::physics::inertia;
|
||||
use crate::animation::physics::inertia::DynSimulator;
|
||||
use crate::display::object::traits::*;
|
||||
use crate::display::scene::MouseTarget;
|
||||
@ -42,7 +43,7 @@ impl ShapeViewEvents {
|
||||
mouse_over <- source_();
|
||||
mouse_out <- source_();
|
||||
|
||||
is_mouse_over <- [mouse_over,mouse_out].toggle();
|
||||
is_mouse_over <- any (mouse_over,mouse_out).toggle();
|
||||
out_on_drop <- on_drop.gate(&is_mouse_over);
|
||||
eval_ out_on_drop (mouse_out.emit(()));
|
||||
}
|
||||
@ -210,3 +211,13 @@ pub fn animation2(network:&frp::Network) -> (DynSimulator<f32>, frp::Stream<f32>
|
||||
let source = DynSimulator::<f32>::new(Box::new(f!((t) target.emit(t))));
|
||||
(source,target.into())
|
||||
}
|
||||
|
||||
|
||||
/// Define a new animation FRP network.
|
||||
pub fn animator<T:inertia::Value>(network:&frp::Network) -> (DynSimulator<T>, frp::Stream<T>) {
|
||||
frp::extend! { network
|
||||
def target = source::<T>();
|
||||
}
|
||||
let source = DynSimulator::<T>::new(Box::new(f!((t) target.emit(t))));
|
||||
(source,target.into())
|
||||
}
|
||||
|
@ -36,7 +36,12 @@ pub mod types {
|
||||
pub use super::algebra::Matrix4x2;
|
||||
pub use super::algebra::Matrix4x3;
|
||||
|
||||
pub use super::algebra::V2;
|
||||
pub use super::algebra::V3;
|
||||
pub use super::algebra::V4;
|
||||
|
||||
pub use super::algebra::Zero;
|
||||
pub use super::algebra::zero;
|
||||
pub use super::algebra::HasComponents;
|
||||
pub use super::algebra::Dim1;
|
||||
pub use super::algebra::Dim2;
|
||||
@ -44,6 +49,4 @@ pub mod types {
|
||||
pub use super::algebra::Abs;
|
||||
pub use super::algebra::Magnitude;
|
||||
pub use super::algebra::Normalize;
|
||||
pub use super::algebra::Point3;
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,13 @@ pub use nalgebra::Matrix4x2;
|
||||
pub use nalgebra::Matrix4x3;
|
||||
|
||||
use nalgebra;
|
||||
use nalgebra::Scalar;
|
||||
use nalgebra::Matrix;
|
||||
use nalgebra::ComplexField;
|
||||
use nalgebra::Dim;
|
||||
use nalgebra::storage::Storage;
|
||||
|
||||
use std::ops::AddAssign;
|
||||
|
||||
|
||||
|
||||
@ -31,6 +38,11 @@ pub trait Zero {
|
||||
fn zero() -> Self;
|
||||
}
|
||||
|
||||
/// Smart constructor for the `Zero` trait.
|
||||
pub fn zero<T:Zero>() -> T {
|
||||
<T as Zero>::zero()
|
||||
}
|
||||
|
||||
|
||||
// === Impls ===
|
||||
|
||||
@ -46,7 +58,7 @@ macro_rules! gen_zero {
|
||||
|
||||
macro_rules! gen_zero_nalgebra {
|
||||
([$($ty:ident),*]) => {$(
|
||||
impl<T:nalgebra::Scalar+num_traits::Zero> Zero for $ty<T> {
|
||||
impl<T:Scalar+num_traits::Zero> Zero for $ty<T> {
|
||||
fn zero() -> Self {
|
||||
nalgebra::zero()
|
||||
}
|
||||
@ -155,18 +167,28 @@ impl Pow<f32> for f32 {
|
||||
/// Types which have magnitude value.
|
||||
#[allow(missing_docs)]
|
||||
pub trait Magnitude {
|
||||
fn magnitude(&self) -> f32;
|
||||
type Output;
|
||||
fn magnitude(&self) -> Self::Output;
|
||||
}
|
||||
|
||||
|
||||
// === Impls ===
|
||||
|
||||
impl Magnitude for f32 {
|
||||
fn magnitude(&self) -> f32 {
|
||||
type Output = f32;
|
||||
fn magnitude(&self) -> Self::Output {
|
||||
self.abs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N:ComplexField, R:Dim, C:Dim, S:Storage<N,R,C>> Magnitude for Matrix<N,R,C,S> {
|
||||
type Output = N::RealField;
|
||||
fn magnitude(&self) -> Self::Output {
|
||||
self.norm()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Signum ===
|
||||
@ -275,6 +297,7 @@ impl Normalize for f32 {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === Square Root ===
|
||||
// ===================
|
||||
@ -394,61 +417,194 @@ impl Acos for f32 {
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Point ===
|
||||
// =============
|
||||
|
||||
/// A coordinate in space.
|
||||
#[derive(Clone,Copy,Debug,Neg,Sub,Add,Div,AddAssign,From,Shrinkwrap)]
|
||||
#[shrinkwrap(mutable)]
|
||||
pub struct Point3 {
|
||||
/// Underlying representation
|
||||
pub matrix : Vector3<f32>
|
||||
macro_rules! define_vector {
|
||||
($name:ident {$($field:ident),*}) => {
|
||||
/// A coordinate in space.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
#[repr(C)]
|
||||
pub struct $name<T=f32> {
|
||||
$(
|
||||
/// Vector component.
|
||||
pub $field : T
|
||||
),*
|
||||
}
|
||||
|
||||
/// Smart constructor.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn $name<T>($($field:T),*) -> $name<T> {
|
||||
$name {$($field),*}
|
||||
}
|
||||
|
||||
impl<T> $name<T> {
|
||||
/// Constructor.
|
||||
pub fn new($($field:T),*) -> Self {
|
||||
Self {$($field),*}
|
||||
}
|
||||
|
||||
/// Converts the struct to slice.
|
||||
///
|
||||
/// # Safety
|
||||
/// The code is safe as the struct is implemented as `repr(C)`.
|
||||
#[allow(unsafe_code)]
|
||||
#[allow(trivial_casts)]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
// Safe, because $name is defined as `#[repr(C)]`.
|
||||
let ptr = self as *const $name<T> as *const T;
|
||||
unsafe {
|
||||
std::slice::from_raw_parts(ptr, std::mem::size_of::<T>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Magnitude for $name {
|
||||
type Output = f32;
|
||||
fn magnitude(&self) -> Self::Output {
|
||||
$(let $field = self.$field * self.$field;)*
|
||||
let sum = 0.0 $(+$field)*;
|
||||
sum.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl Normalize for $name {
|
||||
fn normalize(&self) -> Self {
|
||||
let magnitude = self.magnitude();
|
||||
$(let $field = self.$field / magnitude;)*
|
||||
Self {$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<f32> for $name {
|
||||
fn add_assign(&mut self, rhs:f32) {
|
||||
$(self.$field += rhs;)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,S> AddAssign<$name<S>> for $name<T>
|
||||
where T:AddAssign<S> {
|
||||
fn add_assign(&mut self, rhs:$name<S>) {
|
||||
$(self.$field += rhs.$field;)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,S> Add<$name<S>> for $name<T>
|
||||
where T:Add<S> {
|
||||
type Output = $name<<T as Add<S>>::Output>;
|
||||
fn add(self,rhs:$name<S>) -> Self::Output {
|
||||
$(let $field = self.$field.add(rhs.$field);)*
|
||||
$name {$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,S> Sub<$name<S>> for $name<T>
|
||||
where T:Sub<S> {
|
||||
type Output = $name<<T as Sub<S>>::Output>;
|
||||
fn sub(self,rhs:$name<S>) -> Self::Output {
|
||||
$(let $field = self.$field.sub(rhs.$field);)*
|
||||
$name {$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Neg for $name<T>
|
||||
where T:Neg {
|
||||
type Output = $name<<T as Neg>::Output>;
|
||||
fn neg(self) -> Self::Output {
|
||||
$(let $field = self.$field.neg();)*
|
||||
$name {$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for $name {
|
||||
type Output = $name;
|
||||
fn mul(self, rhs:f32) -> Self::Output {
|
||||
$(let $field = self.$field.mul(rhs);)*
|
||||
Self {$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for $name {
|
||||
type Output = $name;
|
||||
fn div(self, rhs:f32) -> Self::Output {
|
||||
$(let $field = self.$field.div(rhs);)*
|
||||
Self {$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<$name> for f32 {
|
||||
type Output = $name;
|
||||
fn mul(self, rhs:$name) -> Self::Output {
|
||||
$(let $field = self.mul(rhs.$field);)*
|
||||
$name {$($field),*}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Point3 {
|
||||
/// Constructor.
|
||||
pub fn new(x:f32, y:f32, z:f32) -> Self {
|
||||
let matrix = Vector3::new(x,y,z);
|
||||
Self {matrix}
|
||||
define_vector! {V2 {x,y}}
|
||||
define_vector! {V3 {x,y,z}}
|
||||
define_vector! {V4 {x,y,z,w}}
|
||||
|
||||
impl<T:Scalar> From<Vector2<T>> for V2<T> {
|
||||
fn from(t:Vector2<T>) -> Self {
|
||||
V2(t.x,t.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Point3 {
|
||||
fn default() -> Self {
|
||||
let matrix = nalgebra::zero();
|
||||
Self {matrix}
|
||||
impl<T:Scalar> Into<Vector2<T>> for V2<T> {
|
||||
fn into(self) -> Vector2<T> {
|
||||
Vector2::new(self.x,self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Magnitude for Point3 {
|
||||
fn magnitude(&self) -> f32 {
|
||||
self.matrix.magnitude()
|
||||
impl<T:Scalar> Into<Vector2<T>> for &V2<T> {
|
||||
fn into(self) -> Vector2<T> {
|
||||
Vector2::new(self.x,self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Normalize for Point3 {
|
||||
fn normalize(&self) -> Self {
|
||||
Self {matrix:self.matrix.normalize()}
|
||||
|
||||
|
||||
impl<T:Scalar> From<Vector3<T>> for V3<T> {
|
||||
fn from(t:Vector3<T>) -> Self {
|
||||
V3(t.x,t.y,t.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Point3 {
|
||||
type Output = Point3;
|
||||
fn mul(self, rhs:f32) -> Self::Output {
|
||||
let matrix = self.matrix * rhs;
|
||||
Self {matrix}
|
||||
impl<T:Scalar> Into<Vector3<T>> for V3<T> {
|
||||
fn into(self) -> Vector3<T> {
|
||||
Vector3::new(self.x,self.y,self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vector3<f32>> for Point3 {
|
||||
fn into(self) -> Vector3<f32> {
|
||||
self.matrix
|
||||
impl<T:Scalar> Into<Vector3<T>> for &V3<T> {
|
||||
fn into(self) -> Vector3<T> {
|
||||
Vector3::new(self.x,self.y,self.z)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl<T:Scalar> From<Vector4<T>> for V4<T> {
|
||||
fn from(t:Vector4<T>) -> Self {
|
||||
V4(t.x,t.y,t.z,t.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Scalar> Into<Vector4<T>> for V4<T> {
|
||||
fn into(self) -> Vector4<T> {
|
||||
Vector4::new(self.x,self.y,self.z,self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Scalar> Into<Vector4<T>> for &V4<T> {
|
||||
fn into(self) -> Vector4<T> {
|
||||
Vector4::new(self.x,self.y,self.z,self.w)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================
|
||||
// === Algebraic Structures ===
|
||||
// ============================
|
||||
|
@ -183,23 +183,34 @@ pub type Distance<Type=Anything,Repr=f32> = Unit<quantity::Distance,Type,Repr>;
|
||||
pub struct Pixels;
|
||||
|
||||
/// Provides a `px` method to every unit that can be converted to a pixel distance.
|
||||
#[allow(missing_docs)]
|
||||
pub trait PixelDistance {
|
||||
type Output;
|
||||
/// Distance in pixels.
|
||||
fn px(&self) -> Distance<Pixels>;
|
||||
fn px(&self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl PixelDistance for f32 {
|
||||
fn px(&self) -> Distance<Pixels> {
|
||||
type Output = Distance<Pixels>;
|
||||
fn px(&self) -> Self::Output {
|
||||
Distance::new(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelDistance for i32 {
|
||||
fn px(&self) -> Distance<Pixels> {
|
||||
type Output = Distance<Pixels>;
|
||||
fn px(&self) -> Self::Output {
|
||||
Distance::new(*self as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelDistance for V2<f32> {
|
||||
type Output = V2<Distance<Pixels>>;
|
||||
fn px(&self) -> Self::Output {
|
||||
V2(Distance::new(self.x),Distance::new(self.y))
|
||||
}
|
||||
}
|
||||
|
||||
impls! { From + &From <Distance<Pixels>> for Glsl { |t| { t.value.into() } }}
|
||||
|
||||
impls! { From<PhantomData<Distance<Pixels>>> for glsl::PrimType {
|
||||
|
@ -180,6 +180,110 @@ impl Storable for f32 {
|
||||
fn slice_to_items_mut (buffer: &mut [Self]) -> &mut [Self::Cell] { buffer }
|
||||
}
|
||||
|
||||
impl<T:Storable<Cell=T>> Storable for V2<T>
|
||||
where Self:GpuDefault + GpuKnownSize + PhantomInto<glsl::PrimType>, T:ItemBounds {
|
||||
type Cell = T;
|
||||
type Rows = U2;
|
||||
type Cols = U1;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() / Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_from_items_mut(buffer: &mut [Self::Cell]) -> &mut [Self] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() / Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_to_items(buffer: &[Self]) -> &[Self::Cell] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() * Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_to_items_mut(buffer: &mut [Self]) -> &mut [Self::Cell] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() * Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Storable<Cell=T>> Storable for V3<T>
|
||||
where Self:GpuDefault + GpuKnownSize + PhantomInto<glsl::PrimType>, T:ItemBounds {
|
||||
type Cell = T;
|
||||
type Rows = U2;
|
||||
type Cols = U1;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() / Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_from_items_mut(buffer: &mut [Self::Cell]) -> &mut [Self] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() / Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_to_items(buffer: &[Self]) -> &[Self::Cell] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() * Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_to_items_mut(buffer: &mut [Self]) -> &mut [Self::Cell] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() * Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Storable<Cell=T>> Storable for V4<T>
|
||||
where Self:GpuDefault + GpuKnownSize + PhantomInto<glsl::PrimType>, T:ItemBounds {
|
||||
type Cell = T;
|
||||
type Rows = U2;
|
||||
type Cols = U1;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_from_items(buffer: &[Self::Cell]) -> &[Self] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() / Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_from_items_mut(buffer: &mut [Self::Cell]) -> &mut [Self] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() / Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_to_items(buffer: &[Self]) -> &[Self::Cell] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() * Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts(buffer.as_ptr().cast(), len) }
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn slice_to_items_mut(buffer: &mut [Self]) -> &mut [Self::Cell] {
|
||||
// This code casts slice to vector. It is safe because vector was defined as `#[repr(C)]`.
|
||||
let len = buffer.len() * Self::item_count();
|
||||
unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr().cast(), len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Storable<Cell=T>,R,C> Storable for MatrixMN<T,R,C>
|
||||
where T:ItemBounds, Self:MatrixCtx<T,R,C>,
|
||||
@ -292,6 +396,36 @@ impl JsBufferView for [u8] {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
impl<T> JsBufferView for [V2<T>]
|
||||
where T : Storable<Cell=T> + ItemBounds,
|
||||
V2<T> : Storable,
|
||||
[Cell<V2<T>>] : JsBufferView {
|
||||
unsafe fn js_buffer_view(&self) -> js_sys::Object {
|
||||
<V2<T> as Storable>::slice_to_items(self).js_buffer_view()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
impl<T> JsBufferView for [V3<T>]
|
||||
where T : Storable<Cell=T> + ItemBounds,
|
||||
V3<T> : Storable,
|
||||
[Cell<V3<T>>] : JsBufferView {
|
||||
unsafe fn js_buffer_view(&self) -> js_sys::Object {
|
||||
<V3<T> as Storable>::slice_to_items(self).js_buffer_view()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
impl<T> JsBufferView for [V4<T>]
|
||||
where T : Storable<Cell=T> + ItemBounds,
|
||||
V4<T> : Storable,
|
||||
[Cell<V4<T>>] : JsBufferView {
|
||||
unsafe fn js_buffer_view(&self) -> js_sys::Object {
|
||||
<V4<T> as Storable>::slice_to_items(self).js_buffer_view()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
impl<T: Storable<Cell=T>,R,C> JsBufferView for [MatrixMN<T,R,C>]
|
||||
where Self : MatrixCtx<T,R,C>,
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Defines abstraction for data types that have a default value when used as GPU values.
|
||||
|
||||
use nalgebra::*;
|
||||
use crate::math::types::*;
|
||||
|
||||
|
||||
|
||||
@ -39,6 +40,10 @@ define_gpu_defaults! {
|
||||
f32 = 0.0,
|
||||
bool = false,
|
||||
|
||||
V2<f32> = V2::new(0.0,0.0),
|
||||
V3<f32> = V3::new(0.0,0.0,0.0),
|
||||
V4<f32> = V4::new(0.0,0.0,0.0,0.0),
|
||||
|
||||
Vector2<f32> = Vector2::new(0.0,0.0),
|
||||
Vector3<f32> = Vector3::new(0.0,0.0,0.0),
|
||||
Vector4<f32> = Vector4::new(0.0,0.0,0.0,1.0),
|
||||
|
@ -68,6 +68,7 @@ macro_rules! with_all_prim_types {
|
||||
[Matrix2x3 f32] [Matrix2x4 f32]
|
||||
[Matrix3x2 f32] [Matrix3x4 f32]
|
||||
[Matrix4x2 f32] [Matrix4x3 f32]
|
||||
[V2 f32] [V3 f32] [V4 f32]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! This module implements type-level utils for checking the size of values for a given type.
|
||||
|
||||
use nalgebra::*;
|
||||
use crate::math::types::*;
|
||||
use crate::system::gpu::data::buffer::item::MatrixCtx;
|
||||
|
||||
|
||||
@ -37,6 +38,21 @@ impl GpuKnownSize for i32 { type GpuByteSize = U4; }
|
||||
impl GpuKnownSize for u32 { type GpuByteSize = U4; }
|
||||
impl GpuKnownSize for f32 { type GpuByteSize = U4; }
|
||||
|
||||
impl<T> GpuKnownSize for V2<T>
|
||||
where T:GpuKnownSize, U2:DimMul<GpuByteSize<T>>, Mul<U2,GpuByteSize<T>>:DimName {
|
||||
type GpuByteSize = Mul<U2,GpuByteSize<T>>;
|
||||
}
|
||||
|
||||
impl<T> GpuKnownSize for V3<T>
|
||||
where T:GpuKnownSize, U3:DimMul<GpuByteSize<T>>, Mul<U3,GpuByteSize<T>>:DimName {
|
||||
type GpuByteSize = Mul<U3,GpuByteSize<T>>;
|
||||
}
|
||||
|
||||
impl<T> GpuKnownSize for V4<T>
|
||||
where T:GpuKnownSize, U4:DimMul<GpuByteSize<T>>, Mul<U4,GpuByteSize<T>>:DimName {
|
||||
type GpuByteSize = Mul<U4,GpuByteSize<T>>;
|
||||
}
|
||||
|
||||
type Mul<A,B> = <A as DimMul<B>>::Output;
|
||||
impl<T:GpuKnownSize,R:DimName,C:DimName> GpuKnownSize for MatrixMN<T,R,C>
|
||||
where Self:MatrixCtx<T,R,C>,
|
||||
|
@ -6,6 +6,7 @@ use web_sys::WebGlUniformLocation;
|
||||
|
||||
use crate::system::gpu::Context;
|
||||
use crate::system::gpu::data::prim::*;
|
||||
use crate::math::types::*;
|
||||
|
||||
|
||||
|
||||
@ -43,6 +44,9 @@ impl UniformUpload for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Vector ===
|
||||
|
||||
impl UniformUpload for Vector2<f32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform2fv_with_f32_array(Some(location),self.data.as_slice());
|
||||
@ -118,6 +122,87 @@ impl UniformUpload for Vector4<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === V ===
|
||||
|
||||
impl UniformUpload for V2<f32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform2fv_with_f32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V3<f32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform3fv_with_f32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V4<f32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform4fv_with_f32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V2<i32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform2iv_with_i32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V3<i32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform3iv_with_i32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V4<i32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform4iv_with_i32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V2<u32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform2uiv_with_u32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V3<u32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform3uiv_with_u32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V4<u32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform4uiv_with_u32_array(Some(location),self.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V2<bool> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
let v:Vec<i32> = self.as_slice().iter().cloned().map(|t| if t {1} else {0}).collect();
|
||||
context.uniform2iv_with_i32_array(Some(location),&v);
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V3<bool> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
let v:Vec<i32> = self.as_slice().iter().cloned().map(|t| if t {1} else {0}).collect();
|
||||
context.uniform3iv_with_i32_array(Some(location),&v);
|
||||
}
|
||||
}
|
||||
|
||||
impl UniformUpload for V4<bool> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
let v:Vec<i32> = self.as_slice().iter().cloned().map(|t| if t {1} else {0}).collect();
|
||||
context.uniform4iv_with_i32_array(Some(location),&v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Matrix ===
|
||||
|
||||
impl UniformUpload for Matrix2<f32> {
|
||||
fn upload_uniform(&self, context:&Context, location:&WebGlUniformLocation) {
|
||||
context.uniform_matrix2fv_with_f32_array(Some(location),false,self.data.as_slice());
|
||||
|
@ -105,6 +105,51 @@ where [ T1:Into<Glsl>, T2:Into<Glsl>, T3:Into<Glsl>, T4:Into<Glsl> ] { |t| {
|
||||
}}}
|
||||
|
||||
|
||||
// === From Vectors to Glsl ===
|
||||
|
||||
impls! {[T:Into<Glsl>] From <V2<T>> for Glsl { |t| {
|
||||
let x = t.x.into();
|
||||
let y = t.y.into();
|
||||
iformat!("vec2({x},{y})").into()
|
||||
}}}
|
||||
|
||||
impls! {[T:Into<Glsl>] From <V3<T>> for Glsl { |t| {
|
||||
let x = t.x.into();
|
||||
let y = t.y.into();
|
||||
let z = t.z.into();
|
||||
iformat!("vec2({x},{y},{z})").into()
|
||||
}}}
|
||||
|
||||
impls! {[T:Into<Glsl>] From <V4<T>> for Glsl { |t| {
|
||||
let x = t.x.into();
|
||||
let y = t.y.into();
|
||||
let z = t.z.into();
|
||||
let w = t.w.into();
|
||||
iformat!("vec2({x},{y},{z},{w})").into()
|
||||
}}}
|
||||
|
||||
impls! {[T:RefInto<Glsl>] From <&V2<T>> for Glsl { |t| {
|
||||
let x = t.x.glsl();
|
||||
let y = t.y.glsl();
|
||||
iformat!("vec2({x},{y})").into()
|
||||
}}}
|
||||
|
||||
impls! {[T:RefInto<Glsl>] From <&V3<T>> for Glsl { |t| {
|
||||
let x = t.x.glsl();
|
||||
let y = t.y.glsl();
|
||||
let z = t.z.glsl();
|
||||
iformat!("vec2({x},{y},{z})").into()
|
||||
}}}
|
||||
|
||||
impls! {[T:RefInto<Glsl>] From <&V4<T>> for Glsl { |t| {
|
||||
let x = t.x.glsl();
|
||||
let y = t.y.glsl();
|
||||
let z = t.z.glsl();
|
||||
let w = t.w.glsl();
|
||||
iformat!("vec2({x},{y},{z},{w})").into()
|
||||
}}}
|
||||
|
||||
|
||||
// === From Prim Types to Glsl ===
|
||||
|
||||
impls! { From + &From <bool> for Glsl { |t| t.to_string().into() } }
|
||||
@ -857,6 +902,10 @@ define_glsl_prim_type_conversions! {
|
||||
u32 => UInt,
|
||||
f32 => Float,
|
||||
|
||||
V2<f32> => Vec2,
|
||||
V3<f32> => Vec3,
|
||||
V4<f32> => Vec4,
|
||||
|
||||
Vector2<f32> => Vec2,
|
||||
Vector3<f32> => Vec3,
|
||||
Vector4<f32> => Vec4,
|
||||
|
@ -74,13 +74,13 @@ impl<Parameter:frp::Data> FencedAction<Parameter> {
|
||||
/// Wrap the `action` in `FencedAction`.
|
||||
fn fence(network:&frp::Network, action:impl Fn(&Parameter) + 'static) -> Self {
|
||||
frp::extend! { network
|
||||
def trigger = source::<Parameter>();
|
||||
def triggered = trigger.constant(());
|
||||
def switch = gather();
|
||||
switch.attach(&triggered);
|
||||
def performed = trigger.map(move |param| action(param));
|
||||
switch.attach(&performed);
|
||||
def is_running = switch.toggle();
|
||||
trigger <- source::<Parameter>();
|
||||
triggered <- trigger.constant(());
|
||||
switch <- any(...);
|
||||
switch <+ triggered;
|
||||
performed <- trigger.map(move |param| action(param));
|
||||
switch <+ performed;
|
||||
is_running <- switch.toggle();
|
||||
}
|
||||
Self {trigger,is_running}
|
||||
}
|
||||
@ -158,7 +158,7 @@ impl GraphEditorIntegratedWithController {
|
||||
|
||||
// Changes in Graph Editor
|
||||
let is_handling_notification = handle_notification.is_running;
|
||||
def is_hold = is_handling_notification.zip_with(&invalidate.is_running, |l,r| *l || *r);
|
||||
def is_hold = is_handling_notification.all_with(&invalidate.is_running, |l,r| *l || *r);
|
||||
def _action = editor_outs.node_removed .map2(&is_hold,node_removed);
|
||||
def _action = editor_outs.connection_added .map2(&is_hold,connection_created);
|
||||
def _action = editor_outs.connection_removed.map2(&is_hold,connection_removed);
|
||||
|
@ -246,13 +246,23 @@ where K:Eq+Hash {
|
||||
|
||||
// === Impls ===
|
||||
|
||||
impl<K,V,S> Semigroup for HashMapTree<K,V,S>
|
||||
impl<K,V,S> PartialSemigroup<HashMapTree<K,V,S>> for HashMapTree<K,V,S>
|
||||
where K : Eq + Hash + Clone,
|
||||
V : Semigroup,
|
||||
S : BuildHasher + Clone {
|
||||
fn concat_mut(&mut self, other:Self) {
|
||||
self.value.concat_mut(&other.value);
|
||||
PartialSemigroup::concat_mut(&mut self.branches, other.branches);
|
||||
}
|
||||
}
|
||||
|
||||
impl<K,V,S> PartialSemigroup<&HashMapTree<K,V,S>> for HashMapTree<K,V,S>
|
||||
where K : Eq + Hash + Clone,
|
||||
V : Semigroup,
|
||||
S : BuildHasher + Clone {
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
self.value.concat_mut(&other.value);
|
||||
self.branches.concat_mut(&other.branches);
|
||||
PartialSemigroup::concat_mut(&mut self.branches, &other.branches);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ use ensogl::prelude::*;
|
||||
use ensogl::display::navigation::navigator::Navigator;
|
||||
use ensogl::system::web;
|
||||
use ensogl::application::Application;
|
||||
use graph_editor::{GraphEditor, EdgeTarget};
|
||||
use graph_editor::GraphEditor;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use ensogl::display::object::ObjectOps;
|
||||
use ensogl_core_msdf_sys::run_once_initialized;
|
||||
@ -33,13 +33,13 @@ pub fn run_example_shapes() {
|
||||
}
|
||||
|
||||
|
||||
fn fence<T,Out>(network:&frp::Network, trigger:T) -> (frp::Stream,frp::Stream<bool>)
|
||||
fn _fence<T,Out>(network:&frp::Network, trigger:T) -> (frp::Stream,frp::Stream<bool>)
|
||||
where T:frp::HasOutput<Output=Out>, T:Into<frp::Stream<Out>>, Out:frp::Data {
|
||||
let trigger = trigger.into();
|
||||
frp::extend! { network
|
||||
def trigger_ = trigger.constant(());
|
||||
def runner = source::<()>();
|
||||
def switch = gather();
|
||||
def switch = any_mut();
|
||||
switch.attach(&trigger_);
|
||||
def triggered = trigger.map(f_!(runner.emit(())));
|
||||
switch.attach(&triggered);
|
||||
@ -49,16 +49,6 @@ where T:frp::HasOutput<Output=Out>, T:Into<frp::Stream<Out>>, Out:frp::Data {
|
||||
(runner,condition)
|
||||
}
|
||||
|
||||
//fn fenced_gate<F,T,X>(network:&frp::Network, target:&frp::Stream<X>, f:F) -> frp::Stream<X>
|
||||
//where F:'static+Fn()->T, X:frp::Data {
|
||||
// let target = target.clone_ref();
|
||||
// frp::extend! { network
|
||||
// let (trigger,runner,condition) = fence(&network);
|
||||
// def _eval = runner.map(move |_| {f();});
|
||||
// def out = target.gate(&condition);
|
||||
// }
|
||||
// out
|
||||
//}
|
||||
|
||||
fn init(app:&Application) {
|
||||
|
||||
@ -67,7 +57,6 @@ fn init(app:&Application) {
|
||||
dark.insert("graph_editor.node.background.color", color::Lcha::new(0.2,0.013,0.18,1.0));
|
||||
dark.insert("graph_editor.node.selection.color", color::Lcha::new(0.72,0.5,0.22,1.0));
|
||||
dark.insert("graph_editor.node.selection.size", 7.0);
|
||||
// dark.insert("graph_editor.node.selection.color", color::Lcha::new(0.7,0.59,0.18,1.0));
|
||||
dark.insert("animation.duration", 0.5);
|
||||
dark.insert("graph.node.shadow.color", 5.0);
|
||||
dark.insert("graph.node.shadow.size", 5.0);
|
||||
@ -78,39 +67,7 @@ fn init(app:&Application) {
|
||||
|
||||
let _bg = app.display.scene().style_sheet.var("application.background.color");
|
||||
|
||||
// println!("{:?}",bg.value());
|
||||
// println!("{:?}",app.display.scene().style_sheet.debug_sheet_nodes_count());
|
||||
|
||||
// let t1 : color::Hsla = color::Hsla::new(0.0,0.0,0.03,1.0);
|
||||
// let t2 : color::Lcha = t1.into();
|
||||
// let t4 : color::Rgba = color::Rgba::from(t2);
|
||||
// println!("{:?}", t2);
|
||||
// println!("{:?}", color::Rgba::from(t1));
|
||||
// println!("{:?}", t4);
|
||||
// println!("{:?}", color::Hsla::from(color::LinearRgba::new(0.2,0.3,0.4,1.0)));
|
||||
//
|
||||
// let x = color::Hsla::from(color::Rgba::new(0.031,0.031,0.031,1.0));
|
||||
// let y = color::Rgba::from(x);
|
||||
// println!("{:?}", y);
|
||||
// let xyz = color::Xyz::from(color::Rgb::new(0.2,0.4,0.6));
|
||||
// let lab = color::Lab::from(color::Rgb::new(0.2,0.4,0.6));
|
||||
// let lch = color::Lch::from(color::Rgb::new(0.2,0.4,0.6));
|
||||
// let lch = color::Lch::from(color::Rgb::new(1.0,0.0,0.0));
|
||||
// println!("{:?}", xyz);
|
||||
// println!("{:?}", lab);
|
||||
// println!("{:?}", lch);
|
||||
// println!("-----------");
|
||||
// println!("{:?}", color::Rgb::from(xyz));
|
||||
// println!("{:?}", color::Rgb::from(lab));
|
||||
// println!("{:?}", color::Rgb::from(lch));
|
||||
// println!("{:?}", color::Lab::from(color::Xyz::new(0.1,0.2,0.3)));
|
||||
|
||||
// println!("{:?}", palette::Xyz::from(palette::Srgb::new(0.2,0.4,0.6)));
|
||||
// println!("{:?}", palette::Lab::from(palette::LinSrgb::new(0.2,0.4,0.6)));
|
||||
// println!("{:?}", palette::Lab::from(palette::Xyz::new(0.1,0.2,0.3)));
|
||||
|
||||
|
||||
// color::test();
|
||||
|
||||
let world = &app.display;
|
||||
let scene = world.scene();
|
||||
@ -124,27 +81,27 @@ fn init(app:&Application) {
|
||||
|
||||
let node1_id = graph_editor.add_node();
|
||||
let node2_id = graph_editor.add_node();
|
||||
|
||||
//
|
||||
graph_editor.frp.set_node_position.emit((node1_id,Position::new(100.0 , 250.0)));
|
||||
graph_editor.frp.set_node_position.emit((node2_id,Position::new(200.0 , 50.0)));
|
||||
|
||||
//
|
||||
graph_editor.frp.set_node_expression.emit((node1_id,expression_mock()));
|
||||
graph_editor.frp.set_node_expression.emit((node2_id,expression_mock2()));
|
||||
|
||||
frp::new_network! { network
|
||||
def trigger = source::<()>();
|
||||
let (runner,condition) = fence(&network,&trigger);
|
||||
def _eval = runner.map(f_!( {
|
||||
graph_editor.frp.connect_nodes.emit((EdgeTarget::new(node1_id,default()),EdgeTarget::new(node2_id,vec![1,0,2])));
|
||||
}));
|
||||
def _debug = graph_editor.frp.outputs.edge_added.map2(&condition, |id,cond| {
|
||||
let owner = if *cond { "GUI" } else { "ME" };
|
||||
println!("Edge {:?} added by {}!",id,owner)
|
||||
});
|
||||
// frp::new_network! { network
|
||||
// def trigger = source::<()>();
|
||||
// let (runner,condition) = fence(&network,&trigger);
|
||||
// def _eval = runner.map(f_!( {
|
||||
// graph_editor.frp.connect_nodes.emit((EdgeTarget::new(node1_id,default()),EdgeTarget::new(node2_id,vec![1,0,2])));
|
||||
// }));
|
||||
// def _debug = graph_editor.frp.outputs.edge_added.map2(&condition, |id,cond| {
|
||||
// let owner = if *cond { "GUI" } else { "ME" };
|
||||
// println!("Edge {:?} added by {}!",id,owner)
|
||||
// });
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
trigger.emit(());
|
||||
// trigger.emit(());
|
||||
|
||||
|
||||
let mut was_rendered = false;
|
||||
@ -152,7 +109,7 @@ fn init(app:&Application) {
|
||||
world.on_frame(move |_| {
|
||||
let _keep_alive = &navigator;
|
||||
let _keep_alive = &graph_editor;
|
||||
let _keep_alive = &network;
|
||||
// let _keep_alive = &network;
|
||||
|
||||
// Temporary code removing the web-loader instance.
|
||||
// To be changed in the future.
|
||||
@ -181,8 +138,6 @@ use graph_editor::component::node::port::Expression;
|
||||
|
||||
|
||||
pub fn expression_mock() -> Expression {
|
||||
let pattern_crumb = vec![Seq{right:false },Or,Or,Build];
|
||||
let _val = ast::crumbs::SegmentMatchCrumb::Body{val:pattern_crumb};
|
||||
let code = "open \"data.csv\"".into();
|
||||
let output_span_tree = default();
|
||||
let input_span_tree = span_tree::builder::TreeBuilder::new(15)
|
||||
|
@ -153,9 +153,9 @@ impl Default for Keyboard {
|
||||
change_set <- on_pressed . map(KeyMaskChange::on_pressed);
|
||||
change_unset <- on_released . map(KeyMaskChange::on_released);
|
||||
change_clear <- on_defocus . map(|_| KeyMaskChange::on_defocus());
|
||||
change_set_unset <- [change_set, change_unset];
|
||||
change <- [change_set_unset, change_clear];
|
||||
prev_key_mask <- gather::<KeyMask>();
|
||||
change_set_unset <- any (change_set,change_unset);
|
||||
change <- any (change_set_unset,change_clear);
|
||||
prev_key_mask <- any_mut::<KeyMask>();
|
||||
key_mask <- change.map2(&prev_key_mask,KeyMaskChange::updated_mask);
|
||||
prev_key_mask <+ key_mask;
|
||||
previous_key_mask <- key_mask.previous();
|
||||
|
@ -71,18 +71,18 @@ pub struct Mouse {
|
||||
impl Default for Mouse {
|
||||
fn default() -> Self {
|
||||
frp::new_network! { mouse
|
||||
def release = source_();
|
||||
def press = source_();
|
||||
def wheel = source_();
|
||||
def leave = source_();
|
||||
def position = source();
|
||||
def down_const = press.constant(true);
|
||||
def up_const = release.constant(false);
|
||||
def down = down_const.merge(&up_const);
|
||||
def up = down.map(|t| !t);
|
||||
def prev_position = position.previous();
|
||||
def translation = position.map2(&prev_position,|t,s| t - s);
|
||||
def distance = translation.map(|t:&Position| t.length());
|
||||
release <- source_();
|
||||
press <- source_();
|
||||
wheel <- source_();
|
||||
leave <- source_();
|
||||
position <- source();
|
||||
down_const <- press.constant(true);
|
||||
up_const <- release.constant(false);
|
||||
down <- any (down_const,up_const);
|
||||
up <- down.map(|t| !t);
|
||||
prev_position <- position.previous();
|
||||
translation <- position.map2(&prev_position,|t,s| t - s);
|
||||
distance <- translation.map(|t:&Position| t.length());
|
||||
};
|
||||
let network = mouse;
|
||||
Self {network,release,press,leave,wheel,down,up,position,prev_position,translation,distance}
|
||||
|
@ -71,21 +71,21 @@
|
||||
///
|
||||
/// - Stream merge.
|
||||
/// ```compile_fail
|
||||
/// all_nodes <- [selected_nodes,non_selected_nodes];
|
||||
/// all_nodes <- any (selected_nodes,non_selected_nodes);
|
||||
/// ```
|
||||
/// Desugars to:
|
||||
/// ```compile_fail
|
||||
/// def all_nodes = merge2(&selected_nodes,non_selected_nodes);
|
||||
/// def all_nodes = any2 (&selected_nodes,&non_selected_nodes);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// - Stream merge dropping input values.
|
||||
/// ```compile_fail
|
||||
/// all_nodes <-_ [selected_nodes,non_selected_nodes];
|
||||
/// all_nodes <- any_ (selected_nodes,non_selected_nodes);
|
||||
/// ```
|
||||
/// Desugars to:
|
||||
/// ```compile_fail
|
||||
/// def all_nodes = merge2_(&selected_nodes,non_selected_nodes);
|
||||
/// def all_nodes = any2_ (&selected_nodes,&non_selected_nodes);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
@ -218,15 +218,37 @@ macro_rules! extend_line2 {
|
||||
([$($lines:tt)*] $net:ident def $name:ident $(:$ty:ty)? = $tgt1:ident . $tgt2:ident . $tgt3:ident . $tgt4:ident . $base:ident$(::<$param:ty>)?($($arg:tt)*) $($ts:tt)*) => { $crate::extend_line2! { [$($lines)* let $name $(:$ty)? = $net.$base$(::<$param>)?(concat!(stringify!($net),".",stringify!($name)),&$tgt1.$tgt2.$tgt3.$tgt4,$($arg)*) ;] $net def $name = $name $($ts)* } };
|
||||
([$($lines:tt)*] $net:ident def $name:ident $(:$ty:ty)? = $tgt1:ident . $tgt2:ident . $tgt3:ident . $tgt4:ident . $tgt5:ident . $base:ident$(::<$param:ty>)?($($arg:tt)*) $($ts:tt)*) => { $crate::extend_line2! { [$($lines)* let $name $(:$ty)? = $net.$base$(::<$param>)?(concat!(stringify!($net),".",stringify!($name)),&$tgt1.$tgt2.$tgt3.$tgt4.$tgt5,$($arg)*) ;] $net def $name = $name $($ts)* } };
|
||||
|
||||
([] $net:ident $name:ident <- [ $($arg1:ident).+ ] ) => { let $name = $($arg1).+.clone_ref(); };
|
||||
([] $net:ident $name:ident <- [ $($arg1:ident).+ , $($arg2:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = merge2(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- [ $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = merge3(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- [ $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = merge4(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- any (...) $($ts:tt)* ) => {$crate::extend_line2! { [] $net $name <- any_mut() $($ts)* } };
|
||||
([] $net:ident $name:ident <- any ( $($arg1:ident).+ ) ) => { let $name = $($arg1).+.clone_ref(); };
|
||||
([] $net:ident $name:ident <- any ( $($arg1:ident).+ , $($arg2:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = any2(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- any ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = any3(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- any ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = any4(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
|
||||
([] $net:ident $name:ident <- any_ (...) $($ts:tt)* ) => {$crate::extend_line2! { [] $net $name <- any_mut_() $($ts)* } };
|
||||
([] $net:ident $name:ident <- any_ ( $($arg1:ident).+ ) ) => { let $name = $($arg1).+.constant(()); };
|
||||
([] $net:ident $name:ident <- any_ ( $($arg1:ident).+ , $($arg2:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = any2_(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- any_ ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = any3_(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- any_ ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = any4_(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
|
||||
|
||||
([] $net:ident $name:ident <- all (...) $($ts:tt)* ) => {$crate::extend_line2! { [] $net $name <- all_mut() $($ts)* } };
|
||||
([] $net:ident $name:ident <- all ( $($arg1:ident).+ ) ) => { let $name = $($arg1).+.clone_ref(); };
|
||||
([] $net:ident $name:ident <- all ( $($arg1:ident).+ , $($arg2:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all2(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- all ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all3(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- all ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all4(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
|
||||
([] $net:ident $name:ident <- all [...] $($ts:tt)* ) => {$crate::extend_line2! { [] $net $name <- all_mut() $($ts)* } };
|
||||
([] $net:ident $name:ident <- all [ $($arg1:ident).+ ] ) => { let $name = $($arg1).+.clone_ref(); };
|
||||
([] $net:ident $name:ident <- all [ $($arg1:ident).+ , $($arg2:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all_vec2(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- all [ $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all_vec3(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- all [ $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all_vec4(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
|
||||
([] $net:ident $name:ident <- all_ (...) $($ts:tt)* ) => {$crate::extend_line2! { [] $net $name <- all_mut_() $($ts)* } };
|
||||
([] $net:ident $name:ident <- all_ ( $($arg1:ident).+ ) ) => { let $name = $($arg1).+.constant(()); };
|
||||
([] $net:ident $name:ident <- all_ ( $($arg1:ident).+ , $($arg2:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all2_(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- all_ ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all3_(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <- all_ ( $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ) $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = all4_(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
|
||||
([] $net:ident $name:ident <-_ [ $($arg1:ident).+ ] ) => { let $name = $($arg1).+.constant(()); };
|
||||
([] $net:ident $name:ident <-_ [ $($arg1:ident).+ , $($arg2:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = merge2_(&$($arg1).+,&$($arg2).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <-_ [ $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = merge3_(&$($arg1).+,&$($arg2).+,&$($arg3).+) $($ts)* } };
|
||||
([] $net:ident $name:ident <-_ [ $($arg1:ident).+ , $($arg2:ident).+ , $($arg3:ident).+ , $($arg4:ident).+ ] $($ts:tt)* ) => {$crate::extend_line2! { [] $net def $name = merge4_(&$($arg1).+,&$($arg2).+,&$($arg3).+,&$($arg4).+) $($ts)* } };
|
||||
|
||||
([] $net:ident $name:ident <= $($toks:tt)*) => {$crate::extend_line2! { [] $net def $name = $($toks)* . iter()} };
|
||||
([] $net:ident $name:ident <- $($toks:tt)*) => {$crate::extend_line2! { [] $net def $name = $($toks)* } };
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,11 @@
|
||||
//! Root module for graph component definitions.
|
||||
|
||||
pub mod connection;
|
||||
pub mod connection2;
|
||||
pub mod edge;
|
||||
pub mod edge2;
|
||||
pub mod cursor;
|
||||
pub mod node;
|
||||
pub mod visualization;
|
||||
|
||||
pub use cursor::Cursor;
|
||||
pub use edge::Edge;
|
||||
pub use node::Node;
|
||||
|
@ -1,220 +0,0 @@
|
||||
//! Definition of the Connection component.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
// WARNING! UNDER HEAVY DEVELOPMENT. EXPECT DRASTIC CHANGES.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp;
|
||||
use enso_frp as frp;
|
||||
use ensogl::data::color;
|
||||
use ensogl::display::Attribute;
|
||||
use ensogl::display::Buffer;
|
||||
use ensogl::display::Sprite;
|
||||
use ensogl::display::scene::Scene;
|
||||
use ensogl::display::shape::*;
|
||||
use ensogl::display::traits::*;
|
||||
use ensogl::display;
|
||||
use ensogl::gui::component;
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === Connection ===
|
||||
// ==================
|
||||
|
||||
/// Canvas node shape definition.
|
||||
pub mod shape {
|
||||
use super::*;
|
||||
|
||||
ensogl::define_shape_system! {
|
||||
(radius:f32, start_angle:f32, angle:f32) {
|
||||
let radius = 1.px() * radius;
|
||||
let width = LINE_WIDTH.px();
|
||||
let width2 = width / 2.0;
|
||||
let ring = Circle(&radius + &width2) - Circle(radius-width2);
|
||||
let right : Var<f32> = (std::f32::consts::PI/2.0).into();
|
||||
let rot = right - &angle/2.0;
|
||||
let mask = Plane().cut_angle_fast(angle).rotate(rot);
|
||||
let shape = ring * mask;
|
||||
let shape = shape.fill(color::Rgba::from(color::Lcha::new(0.6,0.5,0.76,1.0)));
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Canvas node shape definition.
|
||||
pub mod line {
|
||||
use super::*;
|
||||
|
||||
ensogl::define_shape_system! {
|
||||
() {
|
||||
let width = LINE_WIDTH.px();
|
||||
let height : Var<Distance<Pixels>> = "input_size.y".into();
|
||||
let shape = Rect((width,height));
|
||||
let shape = shape.fill(color::Rgba::from(color::Lcha::new(0.6,0.5,0.76,1.0)));
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Canvas node shape definition.
|
||||
pub mod helper {
|
||||
use super::*;
|
||||
|
||||
ensogl::define_shape_system! {
|
||||
() {
|
||||
let shape = Circle(2.px());
|
||||
let shape = shape.fill(color::Rgba::new(1.0,0.0,0.0,1.0));
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const LINE_WIDTH : f32 = 4.0;
|
||||
const PADDING : f32 = 5.0;
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === Connection ===
|
||||
// ==================
|
||||
|
||||
/// Connection definition.
|
||||
#[derive(AsRef,Clone,CloneRef,Debug,Deref)]
|
||||
pub struct Connection {
|
||||
data : Rc<ConnectionData>,
|
||||
}
|
||||
|
||||
impl AsRef<Connection> for Connection {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
pub struct InputEvents {
|
||||
pub network : frp::Network,
|
||||
pub source_width : frp::Source<f32>,
|
||||
pub target_position : frp::Source<frp::Position>,
|
||||
}
|
||||
|
||||
impl InputEvents {
|
||||
pub fn new() -> Self {
|
||||
frp::new_network! { network
|
||||
def source_width = source();
|
||||
def target_position = source();
|
||||
}
|
||||
Self {network,source_width,target_position}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InputEvents {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Internal data of `Connection`
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ConnectionData {
|
||||
pub object : display::object::Instance,
|
||||
pub logger : Logger,
|
||||
pub events : InputEvents,
|
||||
pub corner : component::ShapeView<shape::Shape>,
|
||||
pub side_line : component::ShapeView<line::Shape>,
|
||||
pub main_line : component::ShapeView<line::Shape>,
|
||||
pub source_width : Rc<Cell<f32>>,
|
||||
pub target_position : Rc<Cell<frp::Position>>,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
/// Constructor.
|
||||
pub fn new(scene:&Scene) -> Self {
|
||||
let logger = Logger::new("edge");
|
||||
let object = display::object::Instance::new(&logger);
|
||||
|
||||
let corner = component::ShapeView::<shape::Shape>::new(&logger.sub("corner"),scene);
|
||||
let side_line = component::ShapeView::<line::Shape>::new(&logger.sub("side_line"),scene);
|
||||
let main_line = component::ShapeView::<line::Shape>::new(&logger.sub("main_line"),scene);
|
||||
object.add_child(&corner);
|
||||
object.add_child(&side_line);
|
||||
object.add_child(&main_line);
|
||||
side_line.mod_rotation(|r| r.z = std::f32::consts::PI/2.0);
|
||||
|
||||
let input = InputEvents::new();
|
||||
let network = &input.network;
|
||||
|
||||
let source_width : Rc<Cell<f32>> = default();
|
||||
let target_position = Rc::new(Cell::new(frp::Position::default()));
|
||||
source_width.set(100.0);
|
||||
|
||||
frp::extend! { network
|
||||
eval input.target_position ((t) target_position.set(*t));
|
||||
eval input.source_width ((t) source_width.set(*t));
|
||||
on_change <-_ [input.source_width,input.target_position];
|
||||
eval_ on_change ([source_width,target_position,object,side_line,main_line,corner] {
|
||||
let target = target_position.get();
|
||||
let target = Vector2::new(target.x - object.position().x, target.y - object.position().y);
|
||||
let radius = 14.0;
|
||||
let width = source_width.get() / 2.0;
|
||||
|
||||
let side_circle_x = width - radius;
|
||||
let side = target.x.signum();
|
||||
let target = Vector2::new(target.x.abs(),target.y);
|
||||
|
||||
let corner_grow = ((target.x - width) * 0.6).max(0.0);
|
||||
let corner_radius = 20.0 + corner_grow;
|
||||
let corner_radius = corner_radius.min(target.y.abs());
|
||||
let corner_x = target.x - corner_radius;
|
||||
|
||||
|
||||
let x = (corner_x - side_circle_x).clamp(-corner_radius,radius);
|
||||
let y = (radius*radius + corner_radius*corner_radius - x*x).sqrt();
|
||||
|
||||
|
||||
let angle1 = f32::atan2(y,x);
|
||||
let angle2 = f32::atan2(radius,corner_radius);
|
||||
let corner_angle = std::f32::consts::PI - angle1 - angle2;
|
||||
let angle_overlap = if corner_x > width { 0.0 } else { 0.1 };
|
||||
|
||||
corner.shape.angle.set((corner_angle + angle_overlap) * side);
|
||||
|
||||
|
||||
let corner_y = - y;
|
||||
let corner_side = (corner_radius + PADDING) * 2.0;
|
||||
corner.shape.sprite.size().set(Vector2::new(corner_side,corner_side));
|
||||
corner.shape.radius.set(corner_radius);
|
||||
corner.mod_position(|t| t.x = corner_x * side);
|
||||
corner.mod_position(|t| t.y = corner_y);
|
||||
|
||||
let line_overlap = 2.0;
|
||||
side_line.shape.sprite.size().set(Vector2::new(10.0,corner_x - width + line_overlap));
|
||||
side_line.mod_position(|p| p.x = side*(width + corner_x)/2.0);
|
||||
|
||||
main_line.shape.sprite.size().set(Vector2::new(10.0,corner_y - target.y + line_overlap));
|
||||
main_line.mod_position(|p| {
|
||||
p.x = side * target.x;
|
||||
p.y = (target.y + corner_y) / 2.0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let events = input;
|
||||
let data = Rc::new(ConnectionData {object,logger,events,corner,side_line,main_line
|
||||
,source_width,target_position});
|
||||
Self {data}
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for Connection {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.object
|
||||
}
|
||||
}
|
@ -16,32 +16,101 @@ use ensogl::display::{Sprite, Attribute};
|
||||
use ensogl::display;
|
||||
use ensogl::gui::component::animation;
|
||||
use ensogl::gui::component::animation2;
|
||||
use ensogl::gui::component::animator;
|
||||
use ensogl::gui::component;
|
||||
use ensogl::system::web;
|
||||
|
||||
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum Mode {
|
||||
Normal,
|
||||
Cursor,
|
||||
Highlight {
|
||||
host : display::object::Instance,
|
||||
position : Vector2<f32>,
|
||||
size : Vector2<f32>,
|
||||
}
|
||||
pub struct StyleParam<T> {
|
||||
pub value : T,
|
||||
pub animate : bool,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn highlight<H>(host:H, position:Vector2<f32>, size:Vector2<f32>) -> Self
|
||||
where H:display::Object {
|
||||
let host = host.display_object().clone_ref();
|
||||
Self::Highlight {host,position,size}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mode {
|
||||
impl<T:Default> Default for StyleParam<T> {
|
||||
fn default() -> Self {
|
||||
Self::Normal
|
||||
let value = default();
|
||||
let animate = true;
|
||||
Self {value,animate}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StyleParam<T> {
|
||||
pub fn new(value:T) -> Self {
|
||||
let animate = true;
|
||||
Self {value,animate}
|
||||
}
|
||||
|
||||
pub fn new_no_animation(value:T) -> Self {
|
||||
let animate = false;
|
||||
Self {value,animate}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug,Clone,Default)]
|
||||
pub struct Style {
|
||||
host : Option<display::object::Instance>,
|
||||
size : Option<StyleParam<Vector2<f32>>>,
|
||||
offset : Option<StyleParam<Vector2<f32>>>,
|
||||
color : Option<StyleParam<color::Lcha>>,
|
||||
radius : Option<f32>,
|
||||
press : Option<f32>,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn highlight<H>
|
||||
(host:H, size:Vector2<f32>, color:Option<color::Lcha>) -> Self
|
||||
where H:display::Object {
|
||||
let host = Some(host.display_object().clone_ref());
|
||||
let size = Some(StyleParam::new(size));
|
||||
let color = color.map(StyleParam::new);
|
||||
Self {host,size,color,..default()}
|
||||
}
|
||||
|
||||
pub fn color(color:color::Lcha) -> Self {
|
||||
let color = Some(StyleParam::new(color));
|
||||
Self {color,..default()}
|
||||
}
|
||||
|
||||
pub fn color_no_animation(color:color::Lcha) -> Self {
|
||||
let color = Some(StyleParam::new_no_animation(color));
|
||||
Self {color,..default()}
|
||||
}
|
||||
|
||||
pub fn selection(size:Vector2<f32>) -> Self {
|
||||
let offset = Some(StyleParam::new_no_animation(-size / 2.0));
|
||||
let size = Some(StyleParam::new_no_animation(size.abs() + Vector2::new(16.0,16.0)));
|
||||
Self {size,offset,..default()}
|
||||
}
|
||||
|
||||
pub fn pressed() -> Self {
|
||||
let press = Some(1.0);
|
||||
Self {press,..default()}
|
||||
}
|
||||
|
||||
pub fn press(mut self) -> Self {
|
||||
self.press = Some(1.0);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialSemigroup<&Style> for Style {
|
||||
#[allow(clippy::clone_on_copy)]
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
if self.host . is_none() { self.host = other.host . clone() }
|
||||
if self.size . is_none() { self.size = other.size . clone() }
|
||||
if self.offset . is_none() { self.offset = other.offset . clone() }
|
||||
if self.color . is_none() { self.color = other.color . clone() }
|
||||
if self.radius . is_none() { self.radius = other.radius . clone() }
|
||||
if self.press . is_none() { self.press = other.press . clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialSemigroup<Style> for Style {
|
||||
fn concat_mut(&mut self, other:Self) {
|
||||
self.concat_mut(&other)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,57 +125,52 @@ pub mod shape {
|
||||
use super::*;
|
||||
|
||||
ensogl::define_shape_system! {
|
||||
(position:Vector2<f32>, width:f32, height:f32, selection_size:Vector2<f32>, press:f32, radius:f32) {
|
||||
( position : Vector2<f32>
|
||||
, dim : V2
|
||||
, offset : V2
|
||||
, selection_size : Vector2<f32>
|
||||
, press : f32
|
||||
, radius : f32
|
||||
, color : Vector4<f32>
|
||||
) {
|
||||
let press_diff = 2.px() * &press;
|
||||
let radius = 1.px() * radius - &press_diff;
|
||||
let selection_width = 1.px() * &selection_size.x() * &press;
|
||||
let selection_height = 1.px() * &selection_size.y() * &press;
|
||||
let width = (1.px() * &width - &press_diff * 2.0) + selection_width.abs();
|
||||
let height = (1.px() * &height - &press_diff * 2.0) + selection_height.abs();
|
||||
let offset : Var<V2<Distance<Pixels>>> = offset.px();
|
||||
let selection_width = 1.px() * &selection_size.x(); // * &press;
|
||||
let selection_height = 1.px() * &selection_size.y(); // * &press;
|
||||
let width = (1.px() * &dim.x() - &press_diff * 2.0) + selection_width.abs();
|
||||
let height = (1.px() * &dim.y() - &press_diff * 2.0) + selection_height.abs();
|
||||
let cursor = Rect((width,height))
|
||||
.corners_radius(radius)
|
||||
.translate((-&selection_width/2.0, -&selection_height/2.0))
|
||||
.translate(offset)
|
||||
.translate(("input_position.x","input_position.y"))
|
||||
.fill(color::Rgba::new(1.0,1.0,1.0,0.2));
|
||||
.fill("srgba(input_color)");
|
||||
cursor.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///// Shape view for Cursor.
|
||||
//#[derive(Clone,CloneRef,Debug)]
|
||||
//#[allow(missing_docs)]
|
||||
//pub struct CursorView {}
|
||||
//
|
||||
//impl component::ShapeViewDefinition for CursorView {
|
||||
// type Shape = shape::Shape;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// ===================
|
||||
// === InputEvents ===
|
||||
// ==============
|
||||
// ===================
|
||||
|
||||
/// Cursor events.
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct InputEvents {
|
||||
pub network : frp::Network,
|
||||
pub set_mode : frp::Source<Mode>,
|
||||
pub press : frp::Source,
|
||||
pub release : frp::Source,
|
||||
pub network : frp::Network,
|
||||
pub set_style : frp::Source<Style>,
|
||||
}
|
||||
|
||||
impl Default for InputEvents {
|
||||
fn default() -> Self {
|
||||
frp::new_network! { cursor_events
|
||||
def set_mode = source();
|
||||
def press = source();
|
||||
def release = source();
|
||||
def set_style = source();
|
||||
}
|
||||
let network = cursor_events;
|
||||
Self {network,set_mode,press,release}
|
||||
Self {network,set_style}
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,12 +242,6 @@ impl Cursor {
|
||||
|
||||
scene.views.main.remove(&shape_system.shape_system.symbol);
|
||||
scene.views.cursor.add(&shape_system.shape_system.symbol);
|
||||
// let scene_view = scene.views.new();
|
||||
// scene.views.main.remove(&shape_system.shape_system.symbol);
|
||||
// scene_view.add(&shape_system.shape_system.symbol);
|
||||
|
||||
|
||||
|
||||
|
||||
let network = &input.network;
|
||||
|
||||
@ -197,95 +255,129 @@ impl Cursor {
|
||||
view_data.radius.set(value)
|
||||
});
|
||||
|
||||
let view_data = view.shape.clone_ref();
|
||||
let width = animation(network,move |value| {
|
||||
view_data.width.set(value)
|
||||
});
|
||||
|
||||
let view_data = view.shape.clone_ref();
|
||||
let height = animation(network,move |value| {
|
||||
view_data.height.set(value)
|
||||
});
|
||||
|
||||
let (size,current_size) = animator::<V2>(network);
|
||||
let (offset,current_offset) = animator::<V2>(network);
|
||||
|
||||
let (anim_use_fixed_pos_setter,anim_use_fixed_pos) = animation2(network);
|
||||
let (anim_pos_x_setter,anim_pos_x) = animation2(network);
|
||||
let (anim_pos_y_setter,anim_pos_y) = animation2(network);
|
||||
|
||||
let (anim_color_lab_l_setter,anim_color_lab_l) = animation2(network);
|
||||
let (anim_color_lab_a_setter,anim_color_lab_a) = animation2(network);
|
||||
let (anim_color_lab_b_setter,anim_color_lab_b) = animation2(network);
|
||||
let (anim_color_alpha_setter,anim_color_alpha) = animation2(network);
|
||||
|
||||
|
||||
anim_color_lab_l_setter.set_target_value(1.0);
|
||||
anim_color_alpha_setter.set_target_value(0.2);
|
||||
|
||||
radius.set_target_value(8.0);
|
||||
size.set_target_value(V2::new(16.0,16.0));
|
||||
|
||||
let mouse = &scene.mouse.frp;
|
||||
|
||||
|
||||
|
||||
|
||||
frp::extend! { network
|
||||
|
||||
def anim_position = anim_pos_x.zip_with(&anim_pos_y,|x,y| frp::Position::new(*x,*y));
|
||||
eval current_size ((v) view.shape.dim.set(*v));
|
||||
eval current_offset ((v) view.shape.offset.set(*v));
|
||||
|
||||
def _t_press = input.press.map(enclose!((press) move |_| {
|
||||
press.set_target_position(1.0);
|
||||
}));
|
||||
def anim_position = anim_pos_x.all_with(&anim_pos_y,|x,y| frp::Position::new(*x,*y));
|
||||
|
||||
def _t_release = input.release.map(enclose!((press) move |_| {
|
||||
press.set_target_position(0.0);
|
||||
}));
|
||||
anim_color <- all_with4(&anim_color_lab_l,&anim_color_lab_a,&anim_color_lab_b,&anim_color_alpha,
|
||||
|l,a,b,alpha| color::Rgba::from(color::Laba::new(*l,*a,*b,*alpha))
|
||||
);
|
||||
|
||||
def fixed_position = input.set_mode.map(enclose!((anim_pos_x_setter,anim_pos_y_setter) move |m| {
|
||||
match m {
|
||||
Mode::Highlight {host,..} => {
|
||||
let p = host.global_position();
|
||||
anim_pos_x_setter.set_target_position(p.x);
|
||||
anim_pos_y_setter.set_target_position(p.y);
|
||||
anim_use_fixed_pos_setter.set_target_position(1.0);
|
||||
Some(p)
|
||||
}
|
||||
_ => {
|
||||
anim_use_fixed_pos_setter.set_target_position(0.0);
|
||||
None
|
||||
def _ev = input.set_style.map(enclose!((size,anim_pos_x_setter,anim_pos_y_setter) move |style| {
|
||||
match &style.press {
|
||||
None => press.set_target_value(0.0),
|
||||
Some(t) => press.set_target_value(*t),
|
||||
}
|
||||
|
||||
match &style.host {
|
||||
None => anim_use_fixed_pos_setter.set_target_value(0.0),
|
||||
Some(host) => {
|
||||
let position = host.global_position();
|
||||
anim_pos_x_setter.set_target_value(position.x);
|
||||
anim_pos_y_setter.set_target_value(position.y);
|
||||
anim_use_fixed_pos_setter.set_target_value(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
match &style.color {
|
||||
None => {
|
||||
anim_color_lab_l_setter.set_target_value(1.0);
|
||||
anim_color_lab_a_setter.set_target_value(0.0);
|
||||
anim_color_lab_b_setter.set_target_value(0.0);
|
||||
anim_color_alpha_setter.set_target_value(0.2);
|
||||
}
|
||||
Some(new_color) => {
|
||||
let lab = color::Laba::from(new_color.value);
|
||||
anim_color_lab_l_setter.set_target_value(lab.lightness);
|
||||
anim_color_lab_a_setter.set_target_value(lab.a);
|
||||
anim_color_lab_b_setter.set_target_value(lab.b);
|
||||
anim_color_alpha_setter.set_target_value(lab.alpha);
|
||||
if !new_color.animate {
|
||||
anim_color_lab_l_setter.skip();
|
||||
anim_color_lab_a_setter.skip();
|
||||
anim_color_lab_b_setter.skip();
|
||||
anim_color_alpha_setter.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match &style.size {
|
||||
None => {
|
||||
size.set_target_value(V2::new(16.0,16.0));
|
||||
}
|
||||
Some(new_size) => {
|
||||
size.set_target_value(V2::new(new_size.value.x,new_size.value.y));
|
||||
if !new_size.animate { size.skip() }
|
||||
}
|
||||
}
|
||||
|
||||
match &style.offset {
|
||||
None => {
|
||||
offset.set_target_value(V2::new(0.0,0.0));
|
||||
}
|
||||
Some(new_offset) => {
|
||||
offset.set_target_value(V2::new(new_offset.value.x,new_offset.value.y));
|
||||
if !new_offset.animate { offset.skip() }
|
||||
}
|
||||
}
|
||||
|
||||
match &style.radius {
|
||||
None => radius.set_target_value(8.0),
|
||||
Some(r) => radius.set_target_value(*r),
|
||||
}
|
||||
}));
|
||||
|
||||
def fixed_position = input.set_style.map(|style| style.host.as_ref().map(|t| t.global_position()));
|
||||
|
||||
def uses_mouse_position = fixed_position.map(|p| p.is_none());
|
||||
def mouse_position = mouse.position.gate(&uses_mouse_position);
|
||||
|
||||
def position = mouse.position.zip_with3(&anim_position,&anim_use_fixed_pos, |p,ap,au| {
|
||||
def position = mouse.position.all_with3(&anim_position,&anim_use_fixed_pos, |p,ap,au| {
|
||||
let x = ap.x * au + p.x * (1.0 - au);
|
||||
let y = ap.y * au + p.y * (1.0 - au);
|
||||
frp::Position::new(x,y)
|
||||
});
|
||||
|
||||
def _position = position.map(f!((p) {
|
||||
view.shape.position.set(Vector2::new(p.x,p.y));
|
||||
}));
|
||||
eval anim_color ((t) view.shape.color.set(Vector4::new(t.red,t.green,t.blue,t.alpha)));
|
||||
eval position ((p) view.shape.position.set(Vector2::new(p.x,p.y)));
|
||||
|
||||
def _position = mouse_position.map(f!([anim_pos_x_setter,anim_pos_y_setter](p) {
|
||||
anim_pos_x_setter.set_target_position(p.x);
|
||||
anim_pos_y_setter.set_target_position(p.y);
|
||||
}));
|
||||
|
||||
def _t_mode = input.set_mode.map(enclose!((radius,width,height) move |m| {
|
||||
match m {
|
||||
Mode::Normal => {
|
||||
radius.set_target_position(8.0);
|
||||
width.set_target_position(16.0);
|
||||
height.set_target_position(16.0);
|
||||
}
|
||||
Mode::Highlight {size,..} => {
|
||||
radius.set_target_position(4.0);
|
||||
width.set_target_position(size.x);
|
||||
height.set_target_position(size.y);
|
||||
}
|
||||
_ => panic!()
|
||||
};
|
||||
anim_pos_x_setter.set_target_value(p.x);
|
||||
anim_pos_y_setter.set_target_value(p.y);
|
||||
}));
|
||||
}
|
||||
|
||||
radius.set_target_position(8.0);
|
||||
width.set_target_position(16.0);
|
||||
height.set_target_position(16.0);
|
||||
|
||||
input.set_mode.emit(Mode::Normal);
|
||||
|
||||
|
||||
|
||||
input.set_style.emit(Style::default());
|
||||
|
||||
|
||||
let frp = Events {input,position};
|
||||
@ -293,17 +385,6 @@ impl Cursor {
|
||||
let data = Rc::new(data);
|
||||
|
||||
Cursor {data}
|
||||
|
||||
}
|
||||
|
||||
// /// Position setter.
|
||||
// pub fn set_position(&self, pos:Vector2<f32>) {
|
||||
// self.view.shape.position.set(pos);
|
||||
// }
|
||||
|
||||
/// Selection size setter.
|
||||
pub fn set_selection_size(&self, pos:Vector2<f32>) {
|
||||
self.view.shape.selection_size.set(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
883
gui/src/rust/lib/graph-editor/src/component/edge.rs
Normal file
883
gui/src/rust/lib/graph-editor/src/component/edge.rs
Normal file
@ -0,0 +1,883 @@
|
||||
//! Definition of the Edge component.
|
||||
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp;
|
||||
use enso_frp as frp;
|
||||
use ensogl::data::color;
|
||||
use ensogl::display::Attribute;
|
||||
use ensogl::display::Buffer;
|
||||
use ensogl::display::Sprite;
|
||||
use ensogl::display::scene::Scene;
|
||||
use ensogl::display::shape::*;
|
||||
use ensogl::display::traits::*;
|
||||
use ensogl::display;
|
||||
use ensogl::gui::component;
|
||||
|
||||
use super::node;
|
||||
|
||||
|
||||
|
||||
fn min(a:f32,b:f32) -> f32 {
|
||||
f32::min(a,b)
|
||||
}
|
||||
|
||||
|
||||
fn max(a:f32,b:f32) -> f32 {
|
||||
f32::max(a,b)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
const LINE_SHAPE_WIDTH : f32 = LINE_WIDTH + 2.0 * PADDING;
|
||||
const LINE_SIDE_OVERLAP : f32 = 1.0;
|
||||
const LINE_SIDES_OVERLAP : f32 = 2.0 * LINE_SIDE_OVERLAP;
|
||||
const LINE_WIDTH : f32 = 4.0;
|
||||
const MOUSE_OFFSET : f32 = 2.0;
|
||||
const NODE_HALF_HEIGHT : f32 = NODE_HEIGHT / 2.0;
|
||||
const NODE_HEIGHT : f32 = node::NODE_HEIGHT;
|
||||
const NODE_PADDING : f32 = node::SHADOW_SIZE;
|
||||
const PADDING : f32 = 4.0;
|
||||
const RIGHT_ANGLE : f32 = std::f32::consts::PI / 2.0;
|
||||
|
||||
const INFINITE : f32 = 99999.0;
|
||||
|
||||
|
||||
|
||||
// =========================
|
||||
// === Shape Definitions ===
|
||||
// =========================
|
||||
|
||||
macro_rules! define_corner_start {($($color:tt)*) => {
|
||||
/// Shape definition.
|
||||
pub mod corner {
|
||||
use super::*;
|
||||
ensogl::define_shape_system! {
|
||||
(radius:f32, angle:f32, start_angle:f32, pos:Vector2<f32>, dim:Vector2<f32>) {
|
||||
let radius = 1.px() * radius;
|
||||
let width = LINE_WIDTH.px();
|
||||
let width2 = width / 2.0;
|
||||
let ring = Circle(&radius + &width2) - Circle(radius-width2);
|
||||
let right : Var<f32> = (RIGHT_ANGLE).into();
|
||||
let rot = right - &angle/2.0 + start_angle;
|
||||
let mask = Plane().cut_angle_fast(angle).rotate(rot);
|
||||
let shape = ring * mask;
|
||||
|
||||
let shadow_size = 10.px();
|
||||
let n_radius = &shadow_size + 1.px() * dim.y();
|
||||
let n_shape = Rect((&shadow_size*2.0 + 2.px() * dim.x(),&n_radius*2.0)).corners_radius(n_radius);
|
||||
let n_shape = n_shape.fill(color::Rgba::new(1.0,0.0,0.0,1.0));
|
||||
let tx = - 1.px() * pos.x();
|
||||
let ty = - 1.px() * pos.y();
|
||||
let n_shape = n_shape.translate((tx,ty));
|
||||
|
||||
let shape = shape - n_shape;
|
||||
let shape = shape.fill(color::Rgba::from($($color)*));
|
||||
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
macro_rules! define_corner_end {($($color:tt)*) => {
|
||||
/// Shape definition.
|
||||
pub mod corner {
|
||||
use super::*;
|
||||
ensogl::define_shape_system! {
|
||||
(radius:f32, angle:f32, start_angle:f32, pos:Vector2<f32>, dim:Vector2<f32>) {
|
||||
let radius = 1.px() * radius;
|
||||
let width = LINE_WIDTH.px();
|
||||
let width2 = width / 2.0;
|
||||
let ring = Circle(&radius + &width2) - Circle(radius-width2);
|
||||
let right : Var<f32> = (RIGHT_ANGLE).into();
|
||||
let rot = right - &angle/2.0 + start_angle;
|
||||
let mask = Plane().cut_angle_fast(angle).rotate(rot);
|
||||
let shape = ring * mask;
|
||||
|
||||
let shadow_size = 10.px() + 1.px();
|
||||
let n_radius = &shadow_size + 1.px() * dim.y();
|
||||
let n_shape = Rect((&shadow_size*2.0 + 2.px() * dim.x(),&n_radius*2.0)).corners_radius(n_radius);
|
||||
let n_shape = n_shape.fill(color::Rgba::new(1.0,0.0,0.0,1.0));
|
||||
let tx = - 1.px() * pos.x();
|
||||
let ty = - 1.px() * pos.y();
|
||||
let n_shape = n_shape.translate((tx,ty));
|
||||
|
||||
let shape = shape * n_shape;
|
||||
let shape = shape.fill(color::Rgba::from($($color)*));
|
||||
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
macro_rules! define_line {($($color:tt)*) => {
|
||||
/// Shape definition.
|
||||
pub mod line {
|
||||
use super::*;
|
||||
ensogl::define_shape_system! {
|
||||
() {
|
||||
let width = LINE_WIDTH.px();
|
||||
let height : Var<Distance<Pixels>> = "input_size.y".into();
|
||||
let shape = Rect((width,height));
|
||||
let shape = shape.fill(color::Rgba::from($($color)*));
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
macro_rules! define_arrow {($($color:tt)*) => {
|
||||
/// Shape definition.
|
||||
pub mod arrow {
|
||||
use super::*;
|
||||
ensogl::define_shape_system! {
|
||||
() {
|
||||
let width : Var<Distance<Pixels>> = "input_size.x".into();
|
||||
let height : Var<Distance<Pixels>> = "input_size.y".into();
|
||||
let width = width - (2.0 * PADDING).px();
|
||||
let height = height - (2.0 * PADDING).px();
|
||||
let triangle = Triangle(width,height);
|
||||
let offset = (LINE_WIDTH/2.0).px();
|
||||
let triangle_l = triangle.translate_x(-&offset);
|
||||
let triangle_r = triangle.translate_x(&offset);
|
||||
let shape = triangle_l + triangle_r;
|
||||
let shape = shape.fill(color::Rgba::from($($color)*));
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
|
||||
// ========================
|
||||
// === Shape Operations ===
|
||||
// ========================
|
||||
|
||||
trait LayoutLine {
|
||||
fn layout_v(&self,start:Vector2<f32>,len:f32);
|
||||
fn layout_h(&self,start:Vector2<f32>,len:f32);
|
||||
fn layout_v_no_overlap(&self,start:Vector2<f32>,len:f32);
|
||||
fn layout_h_no_overlap(&self,start:Vector2<f32>,len:f32);
|
||||
}
|
||||
|
||||
impl LayoutLine for component::ShapeView<front::line::Shape> {
|
||||
fn layout_v(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x, start.y + len/2.0);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs()+LINE_SIDES_OVERLAP);
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
fn layout_h(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x + len/2.0, start.y);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs()+LINE_SIDES_OVERLAP);
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
fn layout_v_no_overlap(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x, start.y + len/2.0);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs());
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
fn layout_h_no_overlap(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x + len/2.0, start.y);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs());
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutLine for component::ShapeView<back::line::Shape> {
|
||||
fn layout_v(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x, start.y + len/2.0);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs()+LINE_SIDES_OVERLAP);
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
fn layout_h(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x + len/2.0, start.y);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs()+LINE_SIDES_OVERLAP);
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
fn layout_v_no_overlap(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x, start.y + len/2.0);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs());
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
fn layout_h_no_overlap(&self, start:Vector2<f32>, len:f32) {
|
||||
let pos = Vector2::new(start.x + len/2.0, start.y);
|
||||
let size = Vector2::new(LINE_SHAPE_WIDTH, len.abs());
|
||||
self.shape.sprite.size().set(size);
|
||||
self.set_position_xy(pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===========================
|
||||
// === Front / Back Shapes ===
|
||||
// ===========================
|
||||
|
||||
/// Shape definitions which will be rendered in the front layer (on top of nodes).
|
||||
pub mod front {
|
||||
use super::*;
|
||||
define_corner_start!(color::Lcha::new(0.6,0.5,0.76,1.0));
|
||||
define_line!(color::Lcha::new(0.6,0.5,0.76,1.0));
|
||||
define_arrow!(color::Lcha::new(0.6,0.5,0.76,1.0));
|
||||
}
|
||||
|
||||
/// Shape definitions which will be rendered in the bottom layer (below nodes).
|
||||
pub mod back {
|
||||
use super::*;
|
||||
define_corner_end!(color::Lcha::new(0.6,0.5,0.76,1.0));
|
||||
define_line!(color::Lcha::new(0.6,0.5,0.76,1.0));
|
||||
define_arrow!(color::Lcha::new(0.6,0.5,0.76,1.0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===========================
|
||||
// === Front / Back Layers ===
|
||||
// ===========================
|
||||
|
||||
macro_rules! define_components {
|
||||
($name:ident {
|
||||
$($field:ident : $field_type:ty),* $(,)?
|
||||
}) => {
|
||||
#[derive(Debug,Clone,CloneRef)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct $name {
|
||||
pub logger : Logger,
|
||||
pub display_object : display::object::Instance,
|
||||
$(pub $field : component::ShapeView<$field_type>),*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// Constructor.
|
||||
pub fn new(logger:Logger, scene:&Scene) -> Self {
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
$(let $field = component::ShapeView::new(&logger.sub(stringify!($field)),scene);)*
|
||||
$(display_object.add_child(&$field);)*
|
||||
Self {logger,display_object,$($field),*}
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for $name {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_components!{
|
||||
Front {
|
||||
corner : front::corner::Shape,
|
||||
corner2 : front::corner::Shape,
|
||||
corner3 : front::corner::Shape,
|
||||
side_line : front::line::Shape,
|
||||
side_line2 : front::line::Shape,
|
||||
main_line : front::line::Shape,
|
||||
port_line : front::line::Shape,
|
||||
arrow : front::arrow::Shape,
|
||||
}
|
||||
}
|
||||
|
||||
define_components!{
|
||||
Back {
|
||||
corner : back::corner::Shape,
|
||||
corner2 : back::corner::Shape,
|
||||
corner3 : back::corner::Shape,
|
||||
side_line : back::line::Shape,
|
||||
side_line2 : back::line::Shape,
|
||||
main_line : back::line::Shape,
|
||||
arrow : back::arrow::Shape,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement proper sorting and remove.
|
||||
/// Hack function used to register the elements for the sorting purposes. To be removed.
|
||||
pub fn sort_hack_1(scene:&Scene) {
|
||||
let logger = Logger::new("hack_sort");
|
||||
component::ShapeView::<back::corner::Shape>::new(&logger,scene);
|
||||
component::ShapeView::<back::line::Shape>::new(&logger,scene);
|
||||
component::ShapeView::<back::arrow::Shape>::new(&logger,scene);
|
||||
}
|
||||
|
||||
// TODO: Implement proper sorting and remove.
|
||||
/// Hack function used to register the elements for the sorting purposes. To be removed.
|
||||
pub fn sort_hack_2(scene:&Scene) {
|
||||
let logger = Logger::new("hack_sort");
|
||||
component::ShapeView::<front::corner::Shape>::new(&logger,scene);
|
||||
component::ShapeView::<front::line::Shape>::new(&logger,scene);
|
||||
component::ShapeView::<front::arrow::Shape>::new(&logger,scene);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===========
|
||||
// === FRP ===
|
||||
// ===========
|
||||
|
||||
/// FRP endpoints of the edge.
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Frp {
|
||||
pub source_width : frp::Source<f32>,
|
||||
pub target_position : frp::Source<frp::Position>,
|
||||
pub target_attached : frp::Source<bool>,
|
||||
}
|
||||
|
||||
impl Frp {
|
||||
/// Constructor.
|
||||
pub fn new(network:&frp::Network) -> Self {
|
||||
frp::extend! { network
|
||||
def source_width = source();
|
||||
def target_position = source();
|
||||
def target_attached = source();
|
||||
}
|
||||
Self {source_width,target_position,target_attached}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === Math Utils ===
|
||||
// ==================
|
||||
|
||||
/// For the given radius of the first circle (`r1`), radius of the second circle (`r2`), and the
|
||||
/// x-axis position of the second circle (`x`), computes the y-axis position of the second circle in
|
||||
/// such a way, that the borders of the circle cross at the right angle. It also computes the angle
|
||||
/// of the intersection. Please note, that the center of the first circle is in the origin.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// r1
|
||||
/// ◄───► (1) x^2 + y^2 = r1^2 + r2^2
|
||||
/// _____ (1) => y = sqrt((r1^2 + r2^2)/x^2)
|
||||
/// .' `.
|
||||
/// / _.-"""B-._ ▲
|
||||
/// | .'0┼ | `. │ angle1 = A-XY-0
|
||||
/// \/ │ / \ │ r2 angle2 = 0-XY-B
|
||||
/// |`._ │__.' | │ alpha = B-XY-X_AXIS
|
||||
/// | A└───┼─ | ▼
|
||||
/// | (x,y) | tg(angle1) = y / x
|
||||
/// \ / tg(angle2) = r1 / r2
|
||||
/// `._ _.' alpha = PI - angle1 - angle2
|
||||
/// `-....-'
|
||||
///```
|
||||
fn circle_intersection(x:f32, r1:f32, r2:f32) -> (f32,f32) {
|
||||
let x_norm = x.clamp(-r2,r1);
|
||||
let y = (r1*r1 + r2*r2 - x_norm*x_norm).sqrt();
|
||||
let angle1 = f32::atan2(y,x_norm);
|
||||
let angle2 = f32::atan2(r1,r2);
|
||||
let angle = std::f32::consts::PI - angle1 - angle2;
|
||||
(y,angle)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Edge ===
|
||||
// ============
|
||||
|
||||
/// Edge definition.
|
||||
#[derive(AsRef,Clone,CloneRef,Debug,Deref)]
|
||||
pub struct Edge {
|
||||
#[deref]
|
||||
model : Rc<EdgeModel>,
|
||||
network : frp::Network,
|
||||
}
|
||||
|
||||
impl AsRef<Edge> for Edge {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
impl display::Object for EdgeModelData {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
||||
impl Edge {
|
||||
/// Constructor.
|
||||
pub fn new(scene:&Scene) -> Self {
|
||||
let network = frp::Network::new();
|
||||
let data = Rc::new(EdgeModelData::new(scene,&network));
|
||||
let model = Rc::new(EdgeModel {data});
|
||||
Self {model,network} . init()
|
||||
}
|
||||
|
||||
fn init(self) -> Self {
|
||||
let network = &self.network;
|
||||
let input = &self.frp;
|
||||
let target_position = &self.target_position;
|
||||
let target_attached = &self.target_attached;
|
||||
let source_width = &self.source_width;
|
||||
let model = &self.model;
|
||||
frp::extend! { network
|
||||
eval input.target_position ((t) target_position.set(*t));
|
||||
eval input.target_attached ((t) target_attached.set(*t));
|
||||
eval input.source_width ((t) source_width.set(*t));
|
||||
on_change <- any_ (input.source_width, input.target_position, input.target_attached);
|
||||
eval_ on_change (model.redraw());
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for Edge {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === EdgeModel ===
|
||||
// =================
|
||||
|
||||
/// Edge definition.
|
||||
#[derive(AsRef,Clone,CloneRef,Debug,Deref)]
|
||||
pub struct EdgeModel {
|
||||
data : Rc<EdgeModelData>,
|
||||
}
|
||||
|
||||
/// Internal data of `Edge`
|
||||
#[derive(Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct EdgeModelData {
|
||||
pub display_object : display::object::Instance,
|
||||
pub logger : Logger,
|
||||
pub frp : Frp,
|
||||
pub front : Front,
|
||||
pub back : Back,
|
||||
pub source_width : Rc<Cell<f32>>,
|
||||
pub target_position : Rc<Cell<frp::Position>>,
|
||||
pub target_attached : Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl EdgeModelData {
|
||||
/// Constructor.
|
||||
pub fn new(scene:&Scene, network:&frp::Network) -> Self {
|
||||
let logger = Logger::new("edge");
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
let front = Front::new(logger.sub("front"),scene);
|
||||
let back = Back::new(logger.sub("back"),scene);
|
||||
|
||||
display_object.add_child(&front);
|
||||
display_object.add_child(&back);
|
||||
|
||||
front . side_line . mod_rotation(|r| r.z = RIGHT_ANGLE);
|
||||
back . side_line . mod_rotation(|r| r.z = RIGHT_ANGLE);
|
||||
front . side_line2 . mod_rotation(|r| r.z = RIGHT_ANGLE);
|
||||
back . side_line2 . mod_rotation(|r| r.z = RIGHT_ANGLE);
|
||||
|
||||
let frp = Frp::new(&network);
|
||||
let source_width = Rc::new(Cell::new(100.0));
|
||||
let target_position = Rc::new(Cell::new(frp::Position::default()));
|
||||
let target_attached = Rc::new(Cell::new(false));
|
||||
|
||||
Self {display_object,logger,frp,front,back,source_width,target_position,target_attached}
|
||||
}
|
||||
|
||||
/// Redraws the connection.
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn redraw(&self) {
|
||||
|
||||
// === Variables ===
|
||||
|
||||
let fg = &self.front;
|
||||
let bg = &self.back;
|
||||
let target_attached = self.target_attached.get();
|
||||
let node_half_width = self.source_width.get() / 2.0;
|
||||
let node_circle = Vector2::new(node_half_width-NODE_HALF_HEIGHT,0.0);
|
||||
let node_radius = NODE_HALF_HEIGHT;
|
||||
|
||||
|
||||
// === Target ===
|
||||
//
|
||||
// Target is the end position of the connection in local node space (the origin is placed in
|
||||
// the center of the node). We handle lines drawing in special way when target is below the
|
||||
// node (for example, not to draw the port line above source node).
|
||||
//
|
||||
// ╭──────────────╮
|
||||
// │ ┼ (0,0) │
|
||||
// ╰──────────────╯────╮
|
||||
// │
|
||||
// ▢ target
|
||||
|
||||
let world_space_target = self.target_position.get();
|
||||
let target_x = world_space_target.x - self.position().x;
|
||||
let target_y = world_space_target.y - self.position().y;
|
||||
let side = target_x.signum();
|
||||
let target_x = target_x.abs();
|
||||
let target = Vector2::new(target_x,target_y);
|
||||
let target_is_below_node_x = target.x < node_half_width;
|
||||
let target_is_below_node_y = target.y < (-NODE_HALF_HEIGHT);
|
||||
let target_is_below_node = target_is_below_node_x && target_is_below_node_y;
|
||||
let port_line_len_max = NODE_HALF_HEIGHT + NODE_PADDING;
|
||||
let side_right_angle = side * RIGHT_ANGLE;
|
||||
|
||||
|
||||
// === Upward Discovery ===
|
||||
//
|
||||
// Discovers when the connection should go upwards. The `upward_corner_radius` defines the
|
||||
// preferred radius for every corner in this scenario.
|
||||
//
|
||||
// ╭─────╮ ╭─╮
|
||||
// ╰─────╯────╯ │
|
||||
// ▢
|
||||
|
||||
let upward_corner_radius = 20.0;
|
||||
let min_len_for_non_curved_line = upward_corner_radius + port_line_len_max;
|
||||
let upward_distance = target.y + min_len_for_non_curved_line;
|
||||
|
||||
|
||||
// === Flat side ===
|
||||
//
|
||||
// Maximum side distance before connection is curved up.
|
||||
//
|
||||
// ╭─────╮◄──► ╭─────╮◄──►╭─╮
|
||||
// ╰─────╯───╮ ╰─────╯────╯ │
|
||||
// ▢ ▢
|
||||
|
||||
let flat_side_size = 40.0;
|
||||
let is_flat_side = target.x < node_half_width + flat_side_size;
|
||||
let downward_flat = if target_is_below_node_x {target_is_below_node_y} else {target.y<0.0};
|
||||
let downward_far = -target.y > min_len_for_non_curved_line || target_is_below_node;
|
||||
let is_down = if is_flat_side {downward_flat} else {downward_far};
|
||||
|
||||
|
||||
// === Port Line Length ===
|
||||
//
|
||||
// ╭──╮
|
||||
// ╰──╯───╮
|
||||
// ╵
|
||||
// ╭──┼──╮ ▲ Port line covers the area above target node and the area of target node
|
||||
// │ ▢ │ ▼ shadow. It can be shorter if the target position is below the node or the
|
||||
// ╰─────╯ connection is being dragged, in order not to overlap with the source node.
|
||||
|
||||
let port_line_start = Vector2::new(side * target.x, target.y + MOUSE_OFFSET);
|
||||
let space_attached = -port_line_start.y - NODE_HALF_HEIGHT - LINE_SIDE_OVERLAP;
|
||||
let space = space_attached - NODE_PADDING;
|
||||
let len_below_free = max(0.0,min(port_line_len_max,space));
|
||||
let len_below_attached = max(0.0,min(port_line_len_max,space_attached));
|
||||
let len_below = if target_attached {len_below_attached} else {len_below_free};
|
||||
let far_side_len = if target_is_below_node {len_below} else {port_line_len_max};
|
||||
let flat_side_len = min(far_side_len,-port_line_start.y);
|
||||
let mut port_line_len = if is_flat_side && is_down {flat_side_len} else {far_side_len};
|
||||
let port_line_end = Vector2::new(target.x,port_line_start.y + port_line_len);
|
||||
|
||||
|
||||
// === Corner1 ===
|
||||
//
|
||||
// The first corner on the line. It is always placed at the right angle to the tangent of
|
||||
// the node border. In case the edge is in the drag mode, the curve is divided into two
|
||||
// parts. The first part is placed under the source node shadow, while the second part is
|
||||
// placed on the top layer.
|
||||
//
|
||||
// ╭─────╮ ╭─────╮ 2╭──╮3
|
||||
// ╰─────╯──╮1 ╰─────╯──╯1 ▢
|
||||
// ▢
|
||||
|
||||
let mut corner1_target = port_line_end;
|
||||
if !is_down {
|
||||
corner1_target.x = if is_flat_side {
|
||||
let radius_grow = max(0.0,target.x - node_half_width + upward_corner_radius);
|
||||
node_half_width + upward_corner_radius + radius_grow
|
||||
} else {
|
||||
let radius1 = node_half_width + (target.x - node_half_width)/2.0;
|
||||
let radius2 = node_half_width + 2.0*upward_corner_radius;
|
||||
min(radius1,radius2)
|
||||
};
|
||||
corner1_target.y = min(upward_corner_radius,upward_distance/2.0);
|
||||
}
|
||||
|
||||
let corner1_grow = ((corner1_target.x - node_half_width) * 0.6).max(0.0);
|
||||
let corner1_radius = 20.0 + corner1_grow;
|
||||
let corner1_radius = corner1_radius.min(corner1_target.y.abs());
|
||||
let corner1_x = corner1_target.x - corner1_radius;
|
||||
|
||||
let corner1_x_loc = corner1_x - node_circle.x;
|
||||
let (y,angle) = circle_intersection(corner1_x_loc,node_radius,corner1_radius);
|
||||
let corner1_y = if is_down {-y} else {y};
|
||||
let corner1 = Vector2::new(corner1_x*side, corner1_y);
|
||||
let angle_overlap = if corner1_x > node_half_width { 0.0 } else { 0.1 };
|
||||
let corner1_side = (corner1_radius + PADDING) * 2.0;
|
||||
let corner1_size = Vector2::new(corner1_side,corner1_side);
|
||||
let corner1_start_angle = if is_down {0.0} else {side_right_angle};
|
||||
let corner1_angle = (angle + angle_overlap) * side;
|
||||
let corner1_angle = if is_down {corner1_angle} else {side_right_angle};
|
||||
|
||||
bg.corner.shape.sprite.size().set(corner1_size);
|
||||
bg.corner.shape.start_angle.set(corner1_start_angle);
|
||||
bg.corner.shape.angle.set(corner1_angle);
|
||||
bg.corner.shape.radius.set(corner1_radius);
|
||||
bg.corner.shape.pos.set(corner1);
|
||||
bg.corner.set_position_xy(corner1);
|
||||
if !target_attached {
|
||||
bg.corner.shape.dim.set(Vector2::new(node_half_width,NODE_HALF_HEIGHT));
|
||||
fg.corner.shape.sprite.size().set(corner1_size);
|
||||
fg.corner.shape.start_angle.set(corner1_start_angle);
|
||||
fg.corner.shape.angle.set(corner1_angle);
|
||||
fg.corner.shape.radius.set(corner1_radius);
|
||||
fg.corner.shape.pos.set(corner1);
|
||||
fg.corner.shape.dim.set(Vector2::new(node_half_width,NODE_HALF_HEIGHT));
|
||||
fg.corner.set_position_xy(corner1);
|
||||
} else {
|
||||
fg.corner.shape.sprite.size().set(zero());
|
||||
bg.corner.shape.dim.set(Vector2::new(INFINITE,INFINITE));
|
||||
}
|
||||
|
||||
|
||||
// === Side Line ===
|
||||
//
|
||||
// Side line is the first horizontal line. In case the edge is in drag mode, the line is
|
||||
// divided into two segments. The first is placed below the shadow of the source node, while
|
||||
// the second is placed on the top layer. The side line placement is the same in case of
|
||||
// upwards connections - it is then placed between node and corenr 1.
|
||||
//
|
||||
// ╭─────╮ ╭─────╮ 2╭──╮3
|
||||
// ╰─────╯╴──╮ ╰─────╯╴─╯1 ▢
|
||||
// ▢
|
||||
|
||||
let side_line_shift = LINE_SIDES_OVERLAP;
|
||||
let side_line_len = max(0.0,corner1_x - node_half_width + side_line_shift);
|
||||
let bg_line_x = node_half_width - side_line_shift;
|
||||
let bg_line_start = Vector2::new(side*bg_line_x,0.0);
|
||||
if target_attached {
|
||||
let bg_line_len = side*side_line_len;
|
||||
fg.side_line.shape.sprite.size().set(zero());
|
||||
bg.side_line.layout_h(bg_line_start,bg_line_len);
|
||||
} else {
|
||||
let bg_max_len = NODE_PADDING + side_line_shift;
|
||||
let bg_line_len = min(side_line_len,bg_max_len);
|
||||
let bg_end_x = bg_line_x + bg_line_len;
|
||||
let fg_line_start = Vector2::new(side*(bg_end_x+LINE_SIDE_OVERLAP),0.0);
|
||||
let fg_line_len = side*(side_line_len - bg_line_len);
|
||||
let bg_line_len_overlap = side * min(side_line_len,bg_max_len+LINE_SIDES_OVERLAP);
|
||||
bg.side_line.layout_h(bg_line_start,bg_line_len_overlap);
|
||||
fg.side_line.layout_h_no_overlap(fg_line_start,fg_line_len);
|
||||
}
|
||||
|
||||
|
||||
// === Main Line (downwards) ===
|
||||
//
|
||||
// Main line is the long vertical line. In case it is placed below the node and the edge is
|
||||
// in drag mode, it is divided into two segments. The upper segment is drawn behind node
|
||||
// shadow, while the second is drawn on the top layer. In case of edge in drag mode drawn
|
||||
// next to node, only the top layer segment is used.
|
||||
//
|
||||
// Please note that only applies to edges going down. Refer to docs of main line of edges
|
||||
// going up to learn more.
|
||||
//
|
||||
// Double edge: Single edge:
|
||||
// ╭─────╮ ╭─────╮
|
||||
// ╰──┬──╯ ╰─────╯────╮
|
||||
// ╷ │
|
||||
// ▢ ▢
|
||||
|
||||
if is_down {
|
||||
let main_line_end_y = corner1.y;
|
||||
let main_line_len = main_line_end_y - port_line_start.y;
|
||||
if !target_attached && target_is_below_node {
|
||||
let back_line_start_y = max(-NODE_HALF_HEIGHT - NODE_PADDING, port_line_start.y);
|
||||
let back_line_start = Vector2::new(port_line_start.x, back_line_start_y);
|
||||
let back_line_len = main_line_end_y - back_line_start_y;
|
||||
let front_line_len = main_line_len - back_line_len;
|
||||
bg.main_line.layout_v(back_line_start, back_line_len);
|
||||
fg.main_line.layout_v(port_line_start, front_line_len);
|
||||
} else if target_attached {
|
||||
let main_line_start_y = port_line_start.y + port_line_len;
|
||||
let main_line_start = Vector2::new(port_line_start.x, main_line_start_y);
|
||||
fg.main_line.shape.sprite.size().set(zero());
|
||||
bg.main_line.layout_v(main_line_start, main_line_len - port_line_len);
|
||||
} else {
|
||||
bg.main_line.shape.sprite.size().set(zero());
|
||||
fg.main_line.layout_v(port_line_start, main_line_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !is_down {
|
||||
|
||||
// === Corner2 & Corner3 Radius ===
|
||||
//
|
||||
// ╭─────╮ 2╭──╮3
|
||||
// ╰─────╯──╯1 ▢
|
||||
|
||||
let corner2_radius = corner1_radius;
|
||||
let corner3_radius = upward_corner_radius;
|
||||
|
||||
let corner2_x = corner1_target.x + corner1_radius;
|
||||
let corner3_x = port_line_end.x - corner3_radius;
|
||||
let corner2_bbox_x = corner2_x - corner2_radius;
|
||||
let corner3_bbox_x = corner3_x + corner3_radius;
|
||||
|
||||
let corner_2_3_dist = corner3_bbox_x - corner2_bbox_x;
|
||||
let corner_2_3_side = corner_2_3_dist.signum();
|
||||
let corner_2_3_dist = corner_2_3_dist.abs();
|
||||
let corner_2_3_width = corner2_radius + corner3_radius;
|
||||
let corner_2_3_do_scale = corner_2_3_dist < corner_2_3_width;
|
||||
let corner_2_3_scale = corner_2_3_dist / corner_2_3_width;
|
||||
let corner_2_3_scale = if corner_2_3_do_scale {corner_2_3_scale} else {1.0};
|
||||
|
||||
let side_combined = side * corner_2_3_side;
|
||||
let corner2_radius = corner2_radius * corner_2_3_scale;
|
||||
let corner3_radius = corner3_radius * corner_2_3_scale;
|
||||
let is_right_side = (side_combined - 1.0) < std::f32::EPSILON;
|
||||
|
||||
|
||||
// === Corner2 & Corner3 Placement ===
|
||||
//
|
||||
// ╭─────╮ 2╭──╮3
|
||||
// ╰─────╯──╯1 ▢
|
||||
|
||||
let corner3_side = (corner3_radius + PADDING) * 2.0;
|
||||
let corner3_size = Vector2::new(corner3_side,corner3_side);
|
||||
let corner3_x = port_line_end.x - corner_2_3_side * corner3_radius;
|
||||
let corner3_y = port_line_end.y;
|
||||
let corner2_y = corner3_y + corner3_radius - corner2_radius;
|
||||
let corner2_y = max(corner2_y, corner1.y);
|
||||
let corner3_y = max(corner3_y,corner2_y - corner3_radius + corner2_radius);
|
||||
let corner3 = Vector2::new(corner3_x*side,corner3_y);
|
||||
let corner3_angle = if is_right_side {0.0} else {-RIGHT_ANGLE};
|
||||
|
||||
if target_attached {
|
||||
fg.corner3.shape.sprite.size().set(zero());
|
||||
bg.corner3.shape.sprite.size().set(corner3_size);
|
||||
bg.corner3.shape.start_angle.set(corner3_angle);
|
||||
bg.corner3.shape.angle.set(RIGHT_ANGLE);
|
||||
bg.corner3.shape.radius.set(corner3_radius);
|
||||
bg.corner3.shape.pos.set(corner3);
|
||||
bg.corner3.shape.dim.set(Vector2::new(INFINITE,INFINITE));
|
||||
bg.corner3.set_position_xy(corner3);
|
||||
} else {
|
||||
bg.corner3.shape.sprite.size().set(zero());
|
||||
fg.corner3.shape.sprite.size().set(corner3_size);
|
||||
fg.corner3.shape.start_angle.set(corner3_angle);
|
||||
fg.corner3.shape.angle.set(RIGHT_ANGLE);
|
||||
fg.corner3.shape.radius.set(corner3_radius);
|
||||
fg.corner3.shape.pos.set(corner3);
|
||||
fg.corner3.shape.dim.set(zero());
|
||||
fg.corner3.set_position_xy(corner3);
|
||||
}
|
||||
|
||||
let corner2_x = corner1_target.x + corner_2_3_side * corner2_radius;
|
||||
let corner2 = Vector2::new(corner2_x*side,corner2_y);
|
||||
let corner2_angle = if is_right_side {-RIGHT_ANGLE} else {0.0};
|
||||
|
||||
if target_attached {
|
||||
fg.corner2.shape.sprite.size().set(zero());
|
||||
bg.corner2.shape.sprite.size().set(corner1_size);
|
||||
bg.corner2.shape.start_angle.set(corner2_angle);
|
||||
bg.corner2.shape.angle.set(RIGHT_ANGLE);
|
||||
bg.corner2.shape.radius.set(corner2_radius);
|
||||
bg.corner2.shape.pos.set(corner2);
|
||||
bg.corner2.shape.dim.set(Vector2::new(INFINITE,INFINITE));
|
||||
bg.corner2.set_position_xy(corner2);
|
||||
} else {
|
||||
bg.corner2.shape.sprite.size().set(zero());
|
||||
fg.corner2.shape.sprite.size().set(corner1_size);
|
||||
fg.corner2.shape.start_angle.set(corner2_angle);
|
||||
fg.corner2.shape.angle.set(RIGHT_ANGLE);
|
||||
fg.corner2.shape.radius.set(corner2_radius);
|
||||
fg.corner2.shape.pos.set(corner2);
|
||||
fg.corner2.shape.dim.set(zero());
|
||||
fg.corner2.set_position_xy(corner2);
|
||||
}
|
||||
|
||||
|
||||
// === Main Line (upwards) ===
|
||||
//
|
||||
// Main line is the first vertical line of the edge placed between the corner 1 and the
|
||||
// corner 2. In case the line is long enough, it has an arrow pointing up to show its
|
||||
// direction.
|
||||
//
|
||||
// ╭─────╮ 2╭──╮3
|
||||
// ╰─────╯──╯1 ▢
|
||||
|
||||
let main_line_len = corner2_y - corner1.y;
|
||||
let main_line_start = Vector2::new(side*corner1_target.x,corner1.y);
|
||||
|
||||
if target_attached {
|
||||
fg.main_line.shape.sprite.size().set(zero());
|
||||
bg.main_line.layout_v(main_line_start, main_line_len);
|
||||
} else {
|
||||
bg.main_line.shape.sprite.size().set(zero());
|
||||
fg.main_line.layout_v(main_line_start, main_line_len);
|
||||
}
|
||||
|
||||
if main_line_len > 2.0 {
|
||||
let arrow_y = (corner1.y - corner1_radius + corner2_y + corner2_radius)/2.0;
|
||||
let arrow_pos = Vector2::new(main_line_start.x, arrow_y);
|
||||
let arrow_size = Vector2::new(20.0,20.0);
|
||||
if target_attached {
|
||||
fg.arrow.shape.sprite.size().set(zero());
|
||||
bg.arrow.shape.sprite.size().set(arrow_size);
|
||||
bg.arrow.set_position_xy(arrow_pos);
|
||||
} else {
|
||||
bg.arrow.shape.sprite.size().set(zero());
|
||||
fg.arrow.shape.sprite.size().set(arrow_size);
|
||||
fg.arrow.set_position_xy(arrow_pos);
|
||||
}
|
||||
} else {
|
||||
bg.arrow.shape.sprite.size().set(zero());
|
||||
fg.arrow.shape.sprite.size().set(zero());
|
||||
}
|
||||
|
||||
|
||||
// === Side Line 2 ===
|
||||
//
|
||||
// Side line 2 is the horizontal line connecting corner 2 and corner 3.
|
||||
//
|
||||
// ╭─────╮ 2╭──╮3
|
||||
// ╰─────╯──╯1 ▢
|
||||
|
||||
let side_line2_len = side*(corner3_x - corner2_x);
|
||||
let side_line2_start = Vector2::new(side*corner2_x,corner2_y + corner2_radius);
|
||||
if target_attached {
|
||||
fg.side_line2.shape.sprite.size().set(zero());
|
||||
bg.side_line2.layout_h(side_line2_start,side_line2_len);
|
||||
} else {
|
||||
bg.side_line2.shape.sprite.size().set(zero());
|
||||
fg.side_line2.layout_h(side_line2_start,side_line2_len);
|
||||
}
|
||||
|
||||
port_line_len = corner3_y - port_line_start.y;
|
||||
} else {
|
||||
fg.arrow.shape.sprite.size().set(zero());
|
||||
bg.arrow.shape.sprite.size().set(zero());
|
||||
fg.corner3.shape.sprite.size().set(zero());
|
||||
bg.corner3.shape.sprite.size().set(zero());
|
||||
fg.corner2.shape.sprite.size().set(zero());
|
||||
bg.corner2.shape.sprite.size().set(zero());
|
||||
fg.side_line2.shape.sprite.size().set(zero());
|
||||
bg.side_line2.shape.sprite.size().set(zero());
|
||||
}
|
||||
|
||||
|
||||
// === Port Line ===
|
||||
|
||||
fg.port_line.layout_v(port_line_start, port_line_len);
|
||||
}
|
||||
}
|
@ -23,76 +23,11 @@ use ensogl::gui::component::animation;
|
||||
use ensogl::gui::component::animation2;
|
||||
use ensogl::gui::component;
|
||||
|
||||
use super::connection::Connection;
|
||||
use super::edge;
|
||||
use crate::component::visualization;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Icons ===
|
||||
// =============
|
||||
|
||||
/// Icons definitions.
|
||||
pub mod icons {
|
||||
use super::*;
|
||||
|
||||
/// History icon.
|
||||
pub fn history() -> AnyShape {
|
||||
let radius_diff = 0.5.px();
|
||||
let corners_radius = 2.0.px();
|
||||
let width_diff = &corners_radius * 3.0;
|
||||
let offset = 2.px();
|
||||
let width = 32.px();
|
||||
let height = 16.px();
|
||||
let persp_diff1 = 6.px();
|
||||
|
||||
let width2 = &width - &width_diff;
|
||||
let width3 = &width2 - &width_diff;
|
||||
let corners_radius2 = &corners_radius - &radius_diff;
|
||||
let corners_radius3 = &corners_radius2 - &radius_diff;
|
||||
let persp_diff2 = &persp_diff1 * 2.0;
|
||||
|
||||
let rect1 = Rect((&width ,&height)).corners_radius(&corners_radius);
|
||||
let rect2 = Rect((&width2,&height)).corners_radius(&corners_radius2).translate_y(&persp_diff1);
|
||||
let rect3 = Rect((&width3,&height)).corners_radius(&corners_radius3).translate_y(&persp_diff2);
|
||||
|
||||
let rect3 = rect3 - rect2.translate_y(&offset);
|
||||
let rect2 = rect2 - rect1.translate_y(&offset);
|
||||
|
||||
let rect1 = rect1.fill(color::Rgba::new(0.26, 0.69, 0.99, 1.00));
|
||||
let rect2 = rect2.fill(color::Rgba::new(0.26, 0.69, 0.99, 0.6));
|
||||
let rect3 = rect3.fill(color::Rgba::new(0.26, 0.69, 0.99, 0.4));
|
||||
|
||||
let icon = (rect3 + rect2 + rect1).translate_y(-persp_diff2/2.0);
|
||||
icon.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Ring angle shape definition.
|
||||
pub fn ring_angle<R,W,A>(inner_radius:R, width:W, angle:A) -> AnyShape
|
||||
where R : Into<Var<Distance<Pixels>>>,
|
||||
W : Into<Var<Distance<Pixels>>>,
|
||||
A : Into<Var<Angle<Radians>>> {
|
||||
let inner_radius = inner_radius.into();
|
||||
let width = width.into();
|
||||
let angle = angle.into();
|
||||
|
||||
let angle2 = &angle / 2.0;
|
||||
let radius = &width / 2.0;
|
||||
let inner = Circle(&inner_radius);
|
||||
let outer = Circle(&inner_radius + &width);
|
||||
let section = Plane().cut_angle(&angle);
|
||||
let corner1 = Circle(&radius).translate_y(inner_radius + radius);
|
||||
let corner2 = corner1.rotate(&angle2);
|
||||
let corner1 = corner1.rotate(-&angle2);
|
||||
let ring = &outer - &inner;
|
||||
let pie = &ring * §ion;
|
||||
let out = &pie + &corner1 + &corner2;
|
||||
let out = out.fill(color::Rgba::new(0.9,0.9,0.9,1.0));
|
||||
out.into()
|
||||
}
|
||||
|
||||
|
||||
const NODE_SHAPE_PADDING : f32 = 40.0;
|
||||
|
||||
|
||||
@ -108,7 +43,7 @@ pub mod shape {
|
||||
(style:Style, selection:f32) {
|
||||
let bg_color = style.get("graph_editor.node.background.color").color().unwrap_or_else(|| color::Rgba::new(1.0,0.0,0.0,1.0).into());
|
||||
let selection_color = style.get("graph_editor.node.selection.color").color().unwrap_or_else(|| color::Rgba::new(1.0,0.0,0.0,1.0).into());
|
||||
let selection_size = style.get("graph_editor.node.selection.size").number().unwrap_or(8.0);
|
||||
let _selection_size = style.get("graph_editor.node.selection.size").number().unwrap_or(8.0);
|
||||
|
||||
let border_size_f = 16.0;
|
||||
|
||||
@ -123,7 +58,7 @@ pub mod shape {
|
||||
|
||||
// === Shadow ===
|
||||
|
||||
let shadow_size = 14.px();
|
||||
let shadow_size = SHADOW_SIZE.px();
|
||||
let shadow_width = &width + &shadow_size * 2.0;
|
||||
let shadow_height = &height + &shadow_size * 2.0;
|
||||
let shadow_radius = &shadow_height / 2.0;
|
||||
@ -131,17 +66,25 @@ pub mod shape {
|
||||
let shadow_color = color::LinearGradient::new()
|
||||
.add(0.0,color::Rgba::new(0.0,0.0,0.0,0.0).into_linear())
|
||||
.add(1.0,color::Rgba::new(0.0,0.0,0.0,0.20).into_linear());
|
||||
let shadow_color = color::SdfSampler::new(shadow_color).max_distance(border_size_f).slope(color::Slope::Exponent(4.0));
|
||||
let shadow_color = color::SdfSampler::new(shadow_color).max_distance(border_size_f).slope(color::Slope::Exponent(2.0));
|
||||
let shadow = shadow.fill(shadow_color);
|
||||
|
||||
|
||||
// === Selection ===
|
||||
|
||||
let selection_size = selection_size.px();
|
||||
let select_width = &width - 2.px() + &selection_size * 2.0 * &selection;
|
||||
let select_height = &height - 2.px() + &selection_size * 2.0 * &selection;
|
||||
let selection_offset = 4.px();
|
||||
let selection_size = 7.px();
|
||||
let select_width = &width - 2.px() + &selection_offset * 2.0 * &selection;
|
||||
let select_height = &height - 2.px() + &selection_offset * 2.0 * &selection;
|
||||
let select_radius = &select_height / 2.0;
|
||||
let select = Rect((select_width,select_height)).corners_radius(select_radius);
|
||||
let select = Rect((&select_width,&select_height)).corners_radius(&select_radius);
|
||||
|
||||
let select2_width = &width - 2.px() + &selection_size * 2.0 * &selection;
|
||||
let select2_height = &height - 2.px() + &selection_size * 2.0 * &selection;
|
||||
let select2_radius = &select2_height / 2.0;
|
||||
let select2 = Rect((&select2_width,&select2_height)).corners_radius(&select2_radius);
|
||||
|
||||
let select = select2 - select;
|
||||
let select = select.fill(color::Rgba::from(selection_color));
|
||||
|
||||
let out = select + shadow + shape;
|
||||
@ -271,12 +214,13 @@ impl Deref for Frp {
|
||||
// === Node ===
|
||||
// ============
|
||||
|
||||
// FIXME: Remove all Weak nodes - no needed anymore
|
||||
|
||||
/// Node definition.
|
||||
#[derive(AsRef,Clone,CloneRef,Debug,Deref)]
|
||||
/// Internal data of `Node`
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Node {
|
||||
data : Rc<NodeModelWithNetwork>,
|
||||
pub model : Rc<NodeModel>,
|
||||
pub frp_network : frp::Network,
|
||||
}
|
||||
|
||||
impl AsRef<Node> for Node {
|
||||
@ -285,42 +229,8 @@ impl AsRef<Node> for Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Weak version of `Node`.
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
pub struct WeakNode {
|
||||
data : Weak<NodeModelWithNetwork>
|
||||
}
|
||||
|
||||
impl WeakElement for WeakNode {
|
||||
type Strong = Node;
|
||||
|
||||
fn new(view: &Self::Strong) -> Self {
|
||||
view.downgrade()
|
||||
}
|
||||
|
||||
fn view(&self) -> Option<Self::Strong> {
|
||||
self.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakKey for WeakNode {
|
||||
type Key = display::object::Id;
|
||||
|
||||
fn with_key<F, R>(view: &Self::Strong, f: F) -> R where F: FnOnce(&Self::Key) -> R {
|
||||
f(&view.id())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Internal data of `Node`
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct NodeModelWithNetwork {
|
||||
pub model : Rc<NodeModel>,
|
||||
pub frp_network : frp::Network,
|
||||
}
|
||||
|
||||
impl Deref for NodeModelWithNetwork {
|
||||
impl Deref for Node {
|
||||
type Target = NodeModel;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.model
|
||||
@ -342,16 +252,9 @@ pub struct NodeModel {
|
||||
pub visualization_container : visualization::Container,
|
||||
}
|
||||
|
||||
//pub const NODE_WIDTH : f32 = 284.0;
|
||||
pub const NODE_HEIGHT : f32 = 28.0;
|
||||
pub const TEXT_OFF : f32 = 12.0;
|
||||
|
||||
impl Node {
|
||||
pub fn new(scene:&Scene) -> Self {
|
||||
let data = Rc::new(NodeModelWithNetwork::new(scene));
|
||||
Self {data}
|
||||
}
|
||||
}
|
||||
pub const TEXT_OFF : f32 = 10.0;
|
||||
pub const SHADOW_SIZE : f32 = 10.0;
|
||||
|
||||
|
||||
impl NodeModel {
|
||||
@ -359,11 +262,13 @@ impl NodeModel {
|
||||
pub fn new(scene:&Scene, network:&frp::Network) -> Self {
|
||||
|
||||
let logger = Logger::new("node");
|
||||
let _connection = Connection::new(scene); // FIXME hack for sorting
|
||||
edge::sort_hack_1(scene);
|
||||
|
||||
let output_area = component::ShapeView::<output_area::Shape>::new(&logger.sub("output_area"),scene);
|
||||
let main_area = component::ShapeView::<shape::Shape>::new(&logger.sub("main_area"),scene);
|
||||
let drag_area = component::ShapeView::<drag_area::Shape>::new(&logger.sub("drag_area"),scene);
|
||||
edge::sort_hack_2(scene);
|
||||
|
||||
port::sort_hack(scene); // FIXME hack for sorting
|
||||
|
||||
let display_object = display::object::Instance::new(&logger);
|
||||
@ -435,7 +340,7 @@ impl NodeModel {
|
||||
|
||||
|
||||
|
||||
impl NodeModelWithNetwork {
|
||||
impl Node {
|
||||
pub fn new(scene:&Scene) -> Self {
|
||||
let frp_network = frp::Network::new();
|
||||
let model = Rc::new(NodeModel::new(scene,&frp_network));
|
||||
@ -448,15 +353,15 @@ impl NodeModelWithNetwork {
|
||||
let (output_area_size_setter, output_area_size) = animation2(&frp_network);
|
||||
|
||||
frp::extend! { frp_network
|
||||
eval_ inputs.select (selection.set_target_position(1.0));
|
||||
eval_ inputs.deselect (selection.set_target_position(0.0));
|
||||
eval_ inputs.select (selection.set_target_value(1.0));
|
||||
eval_ inputs.deselect (selection.set_target_value(0.0));
|
||||
|
||||
eval inputs.set_expression ((expr) model.set_expression(expr));
|
||||
|
||||
eval output_area_size ((size) model.output_area.shape.grow.set(*size));
|
||||
|
||||
eval_ model.output_area.events.mouse_over (output_area_size_setter.set_target_position(1.0));
|
||||
eval_ model.output_area.events.mouse_out (output_area_size_setter.set_target_position(0.0));
|
||||
eval_ model.output_area.events.mouse_over (output_area_size_setter.set_target_value(1.0));
|
||||
eval_ model.output_area.events.mouse_out (output_area_size_setter.set_target_value(0.0));
|
||||
|
||||
eval inputs.set_visualization ((content)
|
||||
model.visualization_container.frp.set_visualization.emit(content)
|
||||
@ -468,29 +373,75 @@ impl NodeModelWithNetwork {
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl StrongRef for Node {
|
||||
type WeakRef = WeakNode;
|
||||
fn downgrade(&self) -> WeakNode {
|
||||
WeakNode {data:Rc::downgrade(&self.data)}
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakRef for WeakNode {
|
||||
type StrongRef = Node;
|
||||
fn upgrade(&self) -> Option<Node> {
|
||||
self.data.upgrade().map(|data| Node{data})
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for Node {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
||||
impl display::WeakObject for WeakNode {
|
||||
fn try_display_object(&self) -> Option<display::object::Instance> {
|
||||
self.upgrade().map(|ref t| t.display_object().clone_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// =============
|
||||
//// === Icons ===
|
||||
//// =============
|
||||
//
|
||||
///// Icons definitions.
|
||||
//pub mod icons {
|
||||
// use super::*;
|
||||
//
|
||||
// /// History icon.
|
||||
// pub fn history() -> AnyShape {
|
||||
// let radius_diff = 0.5.px();
|
||||
// let corners_radius = 2.0.px();
|
||||
// let width_diff = &corners_radius * 3.0;
|
||||
// let offset = 2.px();
|
||||
// let width = 32.px();
|
||||
// let height = 16.px();
|
||||
// let persp_diff1 = 6.px();
|
||||
//
|
||||
// let width2 = &width - &width_diff;
|
||||
// let width3 = &width2 - &width_diff;
|
||||
// let corners_radius2 = &corners_radius - &radius_diff;
|
||||
// let corners_radius3 = &corners_radius2 - &radius_diff;
|
||||
// let persp_diff2 = &persp_diff1 * 2.0;
|
||||
//
|
||||
// let rect1 = Rect((&width ,&height)).corners_radius(&corners_radius);
|
||||
// let rect2 = Rect((&width2,&height)).corners_radius(&corners_radius2).translate_y(&persp_diff1);
|
||||
// let rect3 = Rect((&width3,&height)).corners_radius(&corners_radius3).translate_y(&persp_diff2);
|
||||
//
|
||||
// let rect3 = rect3 - rect2.translate_y(&offset);
|
||||
// let rect2 = rect2 - rect1.translate_y(&offset);
|
||||
//
|
||||
// let rect1 = rect1.fill(color::Rgba::new(0.26, 0.69, 0.99, 1.00));
|
||||
// let rect2 = rect2.fill(color::Rgba::new(0.26, 0.69, 0.99, 0.6));
|
||||
// let rect3 = rect3.fill(color::Rgba::new(0.26, 0.69, 0.99, 0.4));
|
||||
//
|
||||
// let icon = (rect3 + rect2 + rect1).translate_y(-persp_diff2/2.0);
|
||||
// icon.into()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///// Ring angle shape definition.
|
||||
//pub fn ring_angle<R,W,A>(inner_radius:R, width:W, angle:A) -> AnyShape
|
||||
// where R : Into<Var<Distance<Pixels>>>,
|
||||
// W : Into<Var<Distance<Pixels>>>,
|
||||
// A : Into<Var<Angle<Radians>>> {
|
||||
// let inner_radius = inner_radius.into();
|
||||
// let width = width.into();
|
||||
// let angle = angle.into();
|
||||
//
|
||||
// let angle2 = &angle / 2.0;
|
||||
// let radius = &width / 2.0;
|
||||
// let inner = Circle(&inner_radius);
|
||||
// let outer = Circle(&inner_radius + &width);
|
||||
// let section = Plane().cut_angle(&angle);
|
||||
// let corner1 = Circle(&radius).translate_y(inner_radius + radius);
|
||||
// let corner2 = corner1.rotate(&angle2);
|
||||
// let corner1 = corner1.rotate(-&angle2);
|
||||
// let ring = &outer - &inner;
|
||||
// let pie = &ring * §ion;
|
||||
// let out = &pie + &corner1 + &corner2;
|
||||
// let out = out.fill(color::Rgba::new(0.9,0.9,0.9,1.0));
|
||||
// out.into()
|
||||
//}
|
||||
//
|
||||
|
@ -109,11 +109,13 @@ pub fn sort_hack(scene:&Scene) {
|
||||
|
||||
#[derive(Debug,Clone,CloneRef)]
|
||||
pub struct Events {
|
||||
pub network : frp::Network,
|
||||
pub cursor_mode : frp::Stream<cursor::Mode>,
|
||||
pub press : frp::Stream<span_tree::Crumbs>,
|
||||
press_source : frp::Source<span_tree::Crumbs>,
|
||||
cursor_mode_source : frp::Merge<cursor::Mode>,
|
||||
pub network : frp::Network,
|
||||
pub cursor_style : frp::Stream<cursor::Style>,
|
||||
pub press : frp::Stream<span_tree::Crumbs>,
|
||||
pub hover : frp::Stream<Option<span_tree::Crumbs>>,
|
||||
press_source : frp::Source<span_tree::Crumbs>,
|
||||
hover_source : frp::Source<Option<span_tree::Crumbs>>,
|
||||
cursor_style_source : frp::Any<cursor::Style>,
|
||||
}
|
||||
|
||||
|
||||
@ -172,8 +174,9 @@ pub struct Manager {
|
||||
impl Manager {
|
||||
pub fn new(logger:&Logger, scene:&Scene) -> Self {
|
||||
frp::new_network! { network
|
||||
def cursor_mode_source = gather::<cursor::Mode>();
|
||||
def press_source = source::<span_tree::Crumbs>();
|
||||
cursor_style_source <- any_mut::<cursor::Style>();
|
||||
press_source <- source::<span_tree::Crumbs>();
|
||||
hover_source <- source::<Option<span_tree::Crumbs>>();
|
||||
}
|
||||
|
||||
let logger = logger.sub("port_manager");
|
||||
@ -184,9 +187,11 @@ impl Manager {
|
||||
let label = component::ShapeView::<label::Shape>::new(&logger,&scene);
|
||||
let ports = default();
|
||||
let width = default();
|
||||
let cursor_mode = (&cursor_mode_source).into();
|
||||
let cursor_style = (&cursor_style_source).into();
|
||||
let press = (&press_source).into();
|
||||
let frp = Events {network,cursor_mode,press,cursor_mode_source,press_source};
|
||||
let hover = (&hover_source).into();
|
||||
let frp = Events
|
||||
{network,cursor_style,press,hover,cursor_style_source,press_source,hover_source};
|
||||
|
||||
label.mod_position(|t| t.y -= 4.0);
|
||||
|
||||
@ -222,7 +227,7 @@ impl Manager {
|
||||
let port = component::ShapeView::<shape::Shape>::new(&logger,&self.scene);
|
||||
let unit = 7.224_609_4;
|
||||
let width = unit * span.size.value as f32;
|
||||
let width2 = width + 4.0;
|
||||
let width2 = width + 8.0;
|
||||
let node_height = 28.0;
|
||||
let height = 18.0;
|
||||
port.shape.sprite.size().set(Vector2::new(width2,node_height));
|
||||
@ -237,16 +242,21 @@ impl Manager {
|
||||
def _foo = port.events.mouse_over . map(f_!(hover.set(1.0);));
|
||||
def _foo = port.events.mouse_out . map(f_!(hover.set(0.0);));
|
||||
|
||||
def out = port.events.mouse_out.constant(cursor::Mode::Normal);
|
||||
def over = port.events.mouse_over.constant(cursor::Mode::highlight(&port,Vector2::new(x,0.0),Vector2::new(width2,height)));
|
||||
def out = port.events.mouse_out.constant(cursor::Style::default());
|
||||
def over = port.events.mouse_over.constant(cursor::Style::highlight(&port,Vector2::new(width2,height),Some(color::Lcha::new(0.6,0.5,0.76,1.0))));
|
||||
// FIXME: the following lines leak memory in the current FRP
|
||||
// implementation because self.frp does not belong to this network and
|
||||
// we are attaching node there. Nothing bad should happen though.
|
||||
self.frp.cursor_mode_source.attach(&over);
|
||||
self.frp.cursor_mode_source.attach(&out);
|
||||
self.frp.cursor_style_source.attach(&over);
|
||||
self.frp.cursor_style_source.attach(&out);
|
||||
|
||||
let crumbs_down = crumbs.clone();
|
||||
let crumbs_over = crumbs.clone();
|
||||
let press_source = &self.frp.press_source;
|
||||
def _press = port.events.mouse_down.map(f_!(press_source.emit(&crumbs)));
|
||||
let hover_source = &self.frp.hover_source;
|
||||
eval_ port.events.mouse_down (press_source.emit(&crumbs_down));
|
||||
eval_ port.events.mouse_over (hover_source.emit(&Some(crumbs_over.clone())));
|
||||
eval_ port.events.mouse_out (hover_source.emit(&None));
|
||||
}
|
||||
ports.push(port);
|
||||
port_networks.push(port_network);
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
#![recursion_limit="1024"]
|
||||
|
||||
|
||||
#[warn(missing_docs)]
|
||||
pub mod component;
|
||||
|
||||
@ -34,28 +33,28 @@ pub mod prelude {
|
||||
pub use ensogl::prelude::*;
|
||||
}
|
||||
|
||||
use ensogl::application;
|
||||
use ensogl::prelude::*;
|
||||
use ensogl::traits::*;
|
||||
use ensogl::application::shortcut;
|
||||
|
||||
use crate::component::cursor::Cursor;
|
||||
use crate::component::cursor;
|
||||
use crate::component::node;
|
||||
use crate::component::node::Node as NodeView;
|
||||
use crate::component::connection::Connection as EdgeView;
|
||||
use crate::component::visualization::MockDataGenerator3D;
|
||||
use crate::component::visualization::Visualization;
|
||||
use crate::component::visualization;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_frp::io::keyboard;
|
||||
use enso_frp::Position;
|
||||
use ensogl::application::shortcut;
|
||||
use ensogl::application;
|
||||
use ensogl::data::color;
|
||||
use ensogl::display::object::Id;
|
||||
use ensogl::display::Scene;
|
||||
use ensogl::display::world::*;
|
||||
use ensogl::display;
|
||||
use ensogl::prelude::*;
|
||||
use ensogl::system::web::StyleSetter;
|
||||
use ensogl::system::web;
|
||||
use ensogl::traits::*;
|
||||
use nalgebra::Vector2;
|
||||
use ensogl::display::Scene;
|
||||
use crate::component::visualization::Visualization;
|
||||
use crate::component::visualization;
|
||||
use crate::component::visualization::MockDataGenerator3D;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -111,6 +110,10 @@ where T:Eq+Hash, S:std::hash::BuildHasher {
|
||||
}
|
||||
|
||||
impl<T,S> SharedHashSet<T,S> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.raw.borrow().is_empty()
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.raw.borrow_mut().clear()
|
||||
}
|
||||
@ -337,59 +340,74 @@ impl Commands {
|
||||
#[derive(Debug,Clone,CloneRef,Shrinkwrap)]
|
||||
pub struct FrpInputs {
|
||||
#[shrinkwrap(main_field)]
|
||||
commands : Commands,
|
||||
pub connect_detached_edges_to_node : frp::Source<EdgeTarget>,
|
||||
pub connect_edge_source : frp::Source<(EdgeId,EdgeTarget)>,
|
||||
pub connect_edge_target : frp::Source<(EdgeId,EdgeTarget)>,
|
||||
pub connect_nodes : frp::Source<(EdgeTarget,EdgeTarget)>,
|
||||
pub deselect_all_nodes : frp::Source,
|
||||
pub press_node_input : frp::Source<EdgeTarget>,
|
||||
pub remove_all_node_edges : frp::Source<NodeId>,
|
||||
pub remove_all_node_input_edges : frp::Source<NodeId>,
|
||||
pub remove_all_node_output_edges : frp::Source<NodeId>,
|
||||
pub remove_edge : frp::Source<EdgeId>,
|
||||
pub select_node : frp::Source<NodeId>,
|
||||
pub remove_node : frp::Source<NodeId>,
|
||||
pub set_node_expression : frp::Source<(NodeId,node::Expression)>,
|
||||
pub set_node_position : frp::Source<(NodeId,Position)>,
|
||||
pub set_visualization_data : frp::Source<NodeId>,
|
||||
pub translate_selected_nodes : frp::Source<Position>,
|
||||
pub cycle_visualization : frp::Source<NodeId>,
|
||||
pub set_visualization : frp::Source<(NodeId,Option<Visualization>)>,
|
||||
pub register_visualization_class : frp::Source<Option<Rc<visualization::Handle>>>,
|
||||
commands : Commands,
|
||||
pub set_detached_edge_targets : frp::Source<EdgeTarget>,
|
||||
pub set_edge_source : frp::Source<(EdgeId,EdgeTarget)>,
|
||||
pub set_edge_target : frp::Source<(EdgeId,EdgeTarget)>,
|
||||
pub unset_edge_source : frp::Source<EdgeId>,
|
||||
pub unset_edge_target : frp::Source<EdgeId>,
|
||||
pub connect_nodes : frp::Source<(EdgeTarget,EdgeTarget)>,
|
||||
pub deselect_all_nodes : frp::Source,
|
||||
pub press_node_input : frp::Source<EdgeTarget>,
|
||||
pub remove_all_node_edges : frp::Source<NodeId>,
|
||||
pub remove_all_node_input_edges : frp::Source<NodeId>,
|
||||
pub remove_all_node_output_edges : frp::Source<NodeId>,
|
||||
pub remove_edge : frp::Source<EdgeId>,
|
||||
pub select_node : frp::Source<NodeId>,
|
||||
pub remove_node : frp::Source<NodeId>,
|
||||
pub set_node_expression : frp::Source<(NodeId,node::Expression)>,
|
||||
pub set_node_position : frp::Source<(NodeId,Position)>,
|
||||
pub set_visualization_data : frp::Source<NodeId>,
|
||||
pub translate_selected_nodes : frp::Source<Position>,
|
||||
pub cycle_visualization : frp::Source<NodeId>,
|
||||
pub set_visualization : frp::Source<(NodeId,Option<Visualization>)>,
|
||||
pub register_visualization_class : frp::Source<Option<Rc<visualization::Handle>>>,
|
||||
|
||||
|
||||
hover_node_input : frp::Source<Option<EdgeTarget>>,
|
||||
some_edge_targets_detached : frp::Source,
|
||||
all_edge_targets_attached : frp::Source,
|
||||
}
|
||||
|
||||
impl FrpInputs {
|
||||
pub fn new(network:&frp::Network) -> Self {
|
||||
frp::extend! { network
|
||||
def connect_detached_edges_to_node = source();
|
||||
def connect_edge_source = source();
|
||||
def connect_edge_target = source();
|
||||
def connect_nodes = source();
|
||||
def deselect_all_nodes = source();
|
||||
def press_node_input = source();
|
||||
def remove_all_node_edges = source();
|
||||
def remove_all_node_input_edges = source();
|
||||
def remove_all_node_output_edges = source();
|
||||
def remove_edge = source();
|
||||
def select_node = source();
|
||||
def remove_node = source();
|
||||
def set_node_expression = source();
|
||||
def set_node_position = source();
|
||||
def set_visualization_data = source();
|
||||
def translate_selected_nodes = source();
|
||||
def cycle_visualization = source();
|
||||
def set_visualization = source();
|
||||
def register_visualization_class = source();
|
||||
def set_detached_edge_targets = source();
|
||||
def set_edge_source = source();
|
||||
def set_edge_target = source();
|
||||
def unset_edge_source = source();
|
||||
def unset_edge_target = source();
|
||||
def connect_nodes = source();
|
||||
def deselect_all_nodes = source();
|
||||
def press_node_input = source();
|
||||
def remove_all_node_edges = source();
|
||||
def remove_all_node_input_edges = source();
|
||||
def remove_all_node_output_edges = source();
|
||||
def remove_edge = source();
|
||||
def select_node = source();
|
||||
def remove_node = source();
|
||||
def set_node_expression = source();
|
||||
def set_node_position = source();
|
||||
def set_visualization_data = source();
|
||||
def translate_selected_nodes = source();
|
||||
def cycle_visualization = source();
|
||||
def set_visualization = source();
|
||||
def register_visualization_class = source();
|
||||
|
||||
def hover_node_input = source();
|
||||
def some_edge_targets_detached = source();
|
||||
def all_edge_targets_attached = source();
|
||||
}
|
||||
let commands = Commands::new(&network);
|
||||
Self {commands,remove_edge,press_node_input,remove_all_node_edges
|
||||
,remove_all_node_input_edges,remove_all_node_output_edges,set_visualization_data
|
||||
,connect_detached_edges_to_node,connect_edge_source,connect_edge_target
|
||||
,set_detached_edge_targets,set_edge_source,set_edge_target
|
||||
,unset_edge_source,unset_edge_target
|
||||
,set_node_position,select_node,remove_node,translate_selected_nodes,set_node_expression
|
||||
,connect_nodes,deselect_all_nodes,cycle_visualization,set_visualization
|
||||
,register_visualization_class
|
||||
}
|
||||
,register_visualization_class,some_edge_targets_detached,all_edge_targets_attached
|
||||
,hover_node_input
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,7 +448,7 @@ macro_rules! generate_frp_outputs {
|
||||
#[derive(Debug,Clone,CloneRef)]
|
||||
pub struct UnsealedFrpOutputs {
|
||||
network : frp::Network,
|
||||
$($field : frp::Merge<$field_ty>),*
|
||||
$($field : frp::Any<$field_ty>),*
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,CloneRef)]
|
||||
@ -443,7 +461,7 @@ macro_rules! generate_frp_outputs {
|
||||
impl UnsealedFrpOutputs {
|
||||
pub fn new() -> Self {
|
||||
frp::new_network! { network
|
||||
$(def $field = gather();)*
|
||||
$(def $field = any_mut();)*
|
||||
}
|
||||
Self {network, $($field),*}
|
||||
}
|
||||
@ -472,6 +490,11 @@ generate_frp_outputs! {
|
||||
edge_removed : EdgeId,
|
||||
edge_source_set : (EdgeId,EdgeTarget),
|
||||
edge_target_set : (EdgeId,EdgeTarget),
|
||||
edge_source_unset : EdgeId,
|
||||
edge_target_unset : EdgeId,
|
||||
|
||||
some_edge_targets_detached : (),
|
||||
all_edge_targets_attached : (),
|
||||
|
||||
connection_added : EdgeId,
|
||||
connection_removed : EdgeId,
|
||||
@ -486,7 +509,7 @@ generate_frp_outputs! {
|
||||
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
pub struct Node {
|
||||
pub view : NodeView,
|
||||
pub view : component::Node,
|
||||
pub in_edges : SharedHashSet<EdgeId>,
|
||||
pub out_edges : SharedHashSet<EdgeId>,
|
||||
}
|
||||
@ -495,7 +518,7 @@ pub struct Node {
|
||||
pub struct NodeId(pub Id);
|
||||
|
||||
impl Node {
|
||||
pub fn new(view:NodeView) -> Self {
|
||||
pub fn new(view:component::Node) -> Self {
|
||||
let in_edges = default();
|
||||
let out_edges = default();
|
||||
Self {view,in_edges,out_edges}
|
||||
@ -520,7 +543,7 @@ impl display::Object for Node {
|
||||
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
pub struct Edge {
|
||||
pub view : EdgeView,
|
||||
pub view : component::Edge,
|
||||
source : Rc<RefCell<Option<EdgeTarget>>>,
|
||||
target : Rc<RefCell<Option<EdgeTarget>>>,
|
||||
}
|
||||
@ -529,7 +552,7 @@ pub struct Edge {
|
||||
pub struct EdgeId(pub Id);
|
||||
|
||||
impl Edge {
|
||||
pub fn new(view:EdgeView) -> Self {
|
||||
pub fn new(view:component::Edge) -> Self {
|
||||
let source = default();
|
||||
let target = default();
|
||||
Self {view,source,target}
|
||||
@ -579,6 +602,7 @@ impl display::Object for Edge {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === EdgeTarget ===
|
||||
// ==================
|
||||
@ -590,8 +614,9 @@ pub struct EdgeTarget {
|
||||
}
|
||||
|
||||
impl EdgeTarget {
|
||||
pub fn new(node_id:NodeId, port:span_tree::Crumbs) -> Self {
|
||||
let port = Rc::new(port);
|
||||
pub fn new(node_id:impl Into<NodeId>, port:span_tree::Crumbs) -> Self {
|
||||
let node_id = node_id.into();
|
||||
let port = Rc::new(port);
|
||||
Self {node_id,port}
|
||||
}
|
||||
}
|
||||
@ -675,17 +700,17 @@ pub struct TouchNetwork<T:frp::Data> {
|
||||
impl<T:frp::Data> TouchNetwork<T> {
|
||||
pub fn new(network:&frp::Network, mouse:&frp::io::Mouse) -> Self {
|
||||
frp::extend! { network
|
||||
def down = source::<T> ();
|
||||
def down_bool = down.map(|_| true);
|
||||
def up_bool = mouse.release.map(|_| false);
|
||||
def is_down = down_bool.merge(&up_bool);
|
||||
def was_down = is_down.previous();
|
||||
def mouse_up = mouse.release.gate(&was_down);
|
||||
def pos_on_down = mouse.position.sample(&down);
|
||||
def pos_on_up = mouse.position.sample(&mouse_up);
|
||||
def should_select = pos_on_up.map3(&pos_on_down,&mouse.distance,Self::check);
|
||||
def up = down.sample(&mouse_up);
|
||||
def selected = up.gate(&should_select);
|
||||
down <- source::<T> ();
|
||||
down_bool <- down.map(|_| true);
|
||||
up_bool <- mouse.release.map(|_| false);
|
||||
is_down <- any (down_bool,up_bool);
|
||||
was_down <- is_down.previous();
|
||||
mouse_up <- mouse.release.gate(&was_down);
|
||||
pos_on_down <- mouse.position.sample(&down);
|
||||
pos_on_up <- mouse.position.sample(&mouse_up);
|
||||
should_select <- pos_on_up.map3(&pos_on_down,&mouse.distance,Self::check);
|
||||
up <- down.sample(&mouse_up);
|
||||
selected <- up.gate(&should_select);
|
||||
}
|
||||
Self {down,up,is_down,selected}
|
||||
}
|
||||
@ -704,8 +729,8 @@ pub struct TouchState {
|
||||
|
||||
impl TouchState {
|
||||
pub fn new(network:&frp::Network, mouse:&frp::io::Mouse) -> Self {
|
||||
let nodes = TouchNetwork::<NodeId>::new(&network,mouse);
|
||||
let background = TouchNetwork::<()>::new(&network,mouse);
|
||||
let nodes = TouchNetwork::<NodeId>::new(&network,mouse);
|
||||
let background = TouchNetwork::<()>::new(&network,mouse);
|
||||
Self {nodes,background}
|
||||
}
|
||||
}
|
||||
@ -745,56 +770,37 @@ impl Deref for GraphEditorModelWithNetwork {
|
||||
}
|
||||
|
||||
impl GraphEditorModelWithNetwork {
|
||||
pub fn new<S:Into<Scene>>(scene:S, cursor:Cursor) -> Self {
|
||||
pub fn new<S:Into<Scene>>(scene:S, cursor:component::Cursor) -> Self {
|
||||
let network = frp::Network::new();
|
||||
let model = GraphEditorModel::new(scene,cursor,&network);
|
||||
Self {model,network}
|
||||
}
|
||||
|
||||
fn new_node(&self, outputs:&UnsealedFrpOutputs) -> NodeId {
|
||||
let view = NodeView::new(&self.scene);
|
||||
fn new_node
|
||||
( &self
|
||||
, cursor_style : &frp::Source<cursor::Style>
|
||||
, output_press : &frp::Source<NodeId>
|
||||
) -> NodeId {
|
||||
let view = component::Node::new(&self.scene);
|
||||
let node = Node::new(view);
|
||||
let node_id = node.id();
|
||||
self.add_child(&node);
|
||||
|
||||
|
||||
let cursor = &self.cursor;
|
||||
let touch = &self.touch_state;
|
||||
let model = &self.model;
|
||||
let touch = &self.touch_state;
|
||||
let model = &self.model;
|
||||
|
||||
frp::new_bridge_network! { [self.network, node.view.main_area.events.network]
|
||||
def _node_on_down_tagged = node.view.drag_area.events.mouse_down.map(f_!(
|
||||
touch.nodes.down.emit(node_id)
|
||||
));
|
||||
def _cursor_mode = node.view.ports.frp.cursor_mode.map(f!((mode)
|
||||
cursor.frp.set_mode.emit(mode)
|
||||
));
|
||||
def new_edge = node.view.frp.output_ports.mouse_down.map(f_!([model] {
|
||||
if let Some(node) = model.nodes.get_cloned_ref(&node_id) {
|
||||
let view = EdgeView::new(&model.scene);
|
||||
model.add_child(&view);
|
||||
let edge = Edge::new(view);
|
||||
let edge_id = edge.id();
|
||||
model.edges.insert(edge);
|
||||
model.edges.detached_target.insert(edge_id);
|
||||
node.out_edges.insert(edge_id);
|
||||
edge_id
|
||||
} else { default() }
|
||||
}));
|
||||
|
||||
outputs.edge_added <+ new_edge;
|
||||
def new_edge_source = new_edge.map(move |id| (*id,EdgeTarget::new(node_id,default())));
|
||||
outputs.edge_source_set <+ new_edge_source;
|
||||
|
||||
def _eval = new_edge.map2(&cursor.frp.position,f!([model](id,position){
|
||||
if let Some(edge) = model.edges.get_cloned_ref(id) {
|
||||
edge.view.events.target_position.emit(position)
|
||||
}
|
||||
}));
|
||||
|
||||
def _press_node_input = node.view.ports.frp.press.map(f!((crumbs)
|
||||
eval_ node.view.drag_area.events.mouse_down(touch.nodes.down.emit(node_id));
|
||||
eval node.view.ports.frp.cursor_style ((style) cursor_style.emit(style));
|
||||
eval_ node.view.frp.output_ports.mouse_down (output_press.emit(node_id));
|
||||
eval node.view.ports.frp.press ((crumbs)
|
||||
model.frp.press_node_input.emit(EdgeTarget::new(node_id,crumbs.clone()))
|
||||
));
|
||||
);
|
||||
|
||||
eval node.view.ports.frp.hover ([model](crumbs) {
|
||||
let target = crumbs.as_ref().map(|c| EdgeTarget::new(node_id,c.clone()));
|
||||
model.frp.hover_node_input.emit(target);
|
||||
});
|
||||
}
|
||||
|
||||
self.nodes.insert(node_id,node);
|
||||
@ -819,7 +825,7 @@ pub struct GraphEditorModel {
|
||||
pub logger : Logger,
|
||||
pub display_object : display::object::Instance,
|
||||
pub scene : Scene,
|
||||
pub cursor : Cursor,
|
||||
pub cursor : component::Cursor,
|
||||
pub nodes : Nodes,
|
||||
pub edges : Edges,
|
||||
touch_state : TouchState,
|
||||
@ -829,7 +835,7 @@ pub struct GraphEditorModel {
|
||||
// === Public ===
|
||||
|
||||
impl GraphEditorModel {
|
||||
pub fn new<S:Into<Scene>>(scene:S, cursor:Cursor, network:&frp::Network) -> Self {
|
||||
pub fn new<S:Into<Scene>>(scene:S, cursor:component::Cursor, network:&frp::Network) -> Self {
|
||||
let scene = scene.into();
|
||||
let logger = Logger::new("GraphEditor");
|
||||
let display_object = display::object::Instance::new(logger.clone());
|
||||
@ -841,10 +847,18 @@ impl GraphEditorModel {
|
||||
}
|
||||
|
||||
fn new_edge(&self) -> EdgeId {
|
||||
let edge = Edge::new(EdgeView::new(&self.scene));
|
||||
let edge = Edge::new(component::Edge::new(&self.scene));
|
||||
let edge_id = edge.id();
|
||||
self.add_child(&edge);
|
||||
self.edges.insert(edge.clone_ref());
|
||||
edge.id()
|
||||
|
||||
let first_detached = self.edges.detached_target.is_empty();
|
||||
self.edges.detached_target.insert(edge_id);
|
||||
if first_detached {
|
||||
self.frp.some_edge_targets_detached.emit(());
|
||||
}
|
||||
|
||||
edge_id
|
||||
}
|
||||
|
||||
pub fn all_nodes(&self) -> Vec<NodeId> {
|
||||
@ -951,11 +965,12 @@ impl GraphEditorModel {
|
||||
// === Connect ===
|
||||
|
||||
impl GraphEditorModel {
|
||||
fn connect_edge_source(&self, edge_id:EdgeId, target:&EdgeTarget) {
|
||||
fn set_edge_source(&self, edge_id:EdgeId, target:impl Into<EdgeTarget>) {
|
||||
let target = target.into();
|
||||
if let Some(edge) = self.edges.get_cloned_ref(&edge_id) {
|
||||
if let Some(node) = self.nodes.get_cloned_ref(&target.node_id) {
|
||||
node.out_edges.insert(edge_id);
|
||||
edge.set_source(target.clone());
|
||||
edge.set_source(target);
|
||||
// FIXME: both lines require edge to refresh. Let's make it more efficient.
|
||||
self.refresh_edge_position(edge_id);
|
||||
self.refresh_edge_source_width(edge_id);
|
||||
@ -963,16 +978,33 @@ impl GraphEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_edge_target(&self, edge_id:EdgeId, target:&EdgeTarget) {
|
||||
fn set_edge_target(&self, edge_id:EdgeId, target:impl Into<EdgeTarget>) {
|
||||
let target = target.into();
|
||||
if let Some(edge) = self.edges.get_cloned_ref(&edge_id) {
|
||||
if let Some(node) = self.nodes.get_cloned_ref(&target.node_id) {
|
||||
node.in_edges.insert(edge_id);
|
||||
edge.set_target(target.clone());
|
||||
edge.set_target(target);
|
||||
|
||||
self.edges.detached_target.remove(&edge_id);
|
||||
let all_attached = self.edges.detached_target.is_empty();
|
||||
if all_attached {
|
||||
self.frp.all_edge_targets_attached.emit(());
|
||||
}
|
||||
|
||||
edge.view.frp.target_attached.emit(true);
|
||||
self.refresh_edge_position(edge_id);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn take_edges_with_detached_targets(&self) -> HashSet<EdgeId> {
|
||||
let edges = self.edges.detached_target.mem_take();
|
||||
if !edges.is_empty() {
|
||||
self.frp.all_edge_targets_attached.emit(());
|
||||
}
|
||||
edges
|
||||
}
|
||||
|
||||
fn overlapping_edges(&self, target:&EdgeTarget) -> Vec<EdgeId> {
|
||||
let mut overlapping = vec![];
|
||||
if let Some(node) = self.nodes.get_cloned_ref(&target.node_id) {
|
||||
@ -1030,7 +1062,7 @@ impl GraphEditorModel {
|
||||
if let Some(edge) = self.edges.get_cloned_ref(&edge_id) {
|
||||
if let Some(edge_source) = edge.source() {
|
||||
if let Some(node) = self.nodes.get_cloned_ref(&edge_source.node_id) {
|
||||
edge.view.events.source_width.emit(node.view.width());
|
||||
edge.view.frp.source_width.emit(node.view.width());
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1056,7 +1088,7 @@ impl GraphEditorModel {
|
||||
let offset = node.view.ports.get_port_offset(&edge_target.port).unwrap_or_else(|| Vector2::new(0.0,0.0));
|
||||
let node_position = node.view.position();
|
||||
let pos = frp::Position::new(node_position.x + offset.x, node_position.y + offset.y);
|
||||
edge.view.events.target_position.emit(pos);
|
||||
edge.view.frp.target_position.emit(pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1140,7 +1172,7 @@ fn enable_disable_toggle
|
||||
let disable = disable.clone_ref();
|
||||
let toggle = toggle.clone_ref();
|
||||
frp::extend! { network
|
||||
out <- gather();
|
||||
out <- any(...);
|
||||
on_toggle <- toggle.map2(&out,|_,t| !t);
|
||||
on_enable <- enable.constant(true);
|
||||
on_disable <- disable.constant(false);
|
||||
@ -1186,7 +1218,7 @@ impl Default for SelectionMode {
|
||||
#[allow(unused_parens)]
|
||||
fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
let scene = world.scene();
|
||||
let cursor = Cursor::new(world.scene());
|
||||
let cursor = component::Cursor::new(world.scene());
|
||||
web::body().set_style_or_panic("cursor","none");
|
||||
world.add_child(&cursor);
|
||||
|
||||
@ -1207,7 +1239,10 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
|
||||
// === Selection Target Redirection ===
|
||||
frp::extend! { network
|
||||
def mouse_down_target = mouse.press.map(f_!(model.scene.mouse.target.get()));
|
||||
mouse_down_target <- mouse.press.map(f_!(model.scene.mouse.target.get()));
|
||||
mouse_up_target <- mouse.release.map(f_!(model.scene.mouse.target.get()));
|
||||
background_up <- mouse_up_target.map(|t| if t==&display::scene::Target::Background {Some(())} else {None}).unwrap();
|
||||
|
||||
eval mouse_down_target([touch,model](target) {
|
||||
match target {
|
||||
display::scene::Target::Background => touch.background.down.emit(()),
|
||||
@ -1224,24 +1259,45 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
// === Cursor Selection ===
|
||||
frp::extend! { network
|
||||
|
||||
def mouse_on_down_position = mouse.position.sample(&mouse.press);
|
||||
def selection_zero = source::<Position>();
|
||||
def selection_size_down = mouse.position.map2(&mouse_on_down_position,|m,n|{m-n});
|
||||
def selection_size_if_down = selection_size_down.gate(&touch.background.is_down);
|
||||
def selection_size_on_down = selection_zero.sample(&mouse.press);
|
||||
def selection_size = selection_size_if_down.merge(&selection_size_on_down);
|
||||
mouse_on_down_position <- mouse.position.sample(&mouse.press);
|
||||
selection_size_down <- mouse.position.map2(&mouse_on_down_position,|m,n|{m-n});
|
||||
selection_size <- selection_size_down.gate(&touch.background.is_down);
|
||||
|
||||
on_press_style <- mouse.press . constant(cursor::Style::pressed());
|
||||
on_release_style <- mouse.release . constant(cursor::Style::default());
|
||||
|
||||
|
||||
cursor_selection_start <- selection_size.map(|p| cursor::Style::selection(Vector2::new(p.x,p.y)));
|
||||
cursor_selection_end <- mouse.release . constant(cursor::Style::default());
|
||||
|
||||
cursor_selection <- any (cursor_selection_start, cursor_selection_end);
|
||||
|
||||
cursor_press <- any (on_press_style, on_release_style);
|
||||
|
||||
eval selection_size ((p) cursor.set_selection_size(Vector2::new(p.x,p.y)));
|
||||
|
||||
eval_ mouse.press (cursor.frp.press.emit(()));
|
||||
eval_ mouse.release (cursor.frp.release.emit(()));
|
||||
}
|
||||
|
||||
|
||||
// === Cursor Color ===
|
||||
frp::extend! { network
|
||||
|
||||
let style = cursor::Style::color_no_animation(color::Lcha::new(0.6,0.5,0.76,1.0)).press();
|
||||
cursor_style_on_edge_drag <- outputs.some_edge_targets_detached.constant(style);
|
||||
cursor_style_on_edge_drag_stop <- outputs.all_edge_targets_attached.constant(default());
|
||||
cursor_style_edge_drag <- any (cursor_style_on_edge_drag,cursor_style_on_edge_drag_stop);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === Node Select ===
|
||||
frp::extend! { network
|
||||
|
||||
def deselect_all_nodes = gather_();
|
||||
deselect_all_nodes <- any_(...);
|
||||
|
||||
let multi_select_flag = enable_disable_toggle
|
||||
( network
|
||||
@ -1271,7 +1327,8 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
, &inputs.toggle_node_inverse_select
|
||||
);
|
||||
|
||||
selection_mode <- zip_with4
|
||||
|
||||
selection_mode <- all_with4
|
||||
(&multi_select_flag,&merge_select_flag,&subtract_select_flag,&inverse_select_flag,
|
||||
|multi,merge,subtract,inverse| {
|
||||
if *multi { SelectionMode::Multi }
|
||||
@ -1284,6 +1341,7 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
|
||||
let node_pressed = touch.nodes.selected.clone_ref();
|
||||
|
||||
|
||||
node_was_selected <- node_pressed.map(f!((id) model.nodes.selected.contains(id)));
|
||||
|
||||
should_select <- node_pressed.map3(&selection_mode,&node_was_selected,
|
||||
@ -1311,11 +1369,52 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
}
|
||||
|
||||
|
||||
// === Node Connect ===
|
||||
// === Add Node ===
|
||||
frp::extend! { network
|
||||
|
||||
outputs.edge_source_set <+ inputs.connect_edge_source;
|
||||
outputs.edge_target_set <+ inputs.connect_edge_target;
|
||||
node_cursor_style <- source::<cursor::Style>();
|
||||
|
||||
|
||||
let node_output_touch = TouchNetwork::<NodeId>::new(&network,&mouse);
|
||||
trace node_output_touch.selected;
|
||||
|
||||
on_connect_drag_mode <- node_output_touch.down.constant(true);
|
||||
on_connect_follow_mode <- node_output_touch.selected.constant(false);
|
||||
connect_drag_mode <- any (on_connect_drag_mode,on_connect_follow_mode);
|
||||
|
||||
new_edge <- node_output_touch.down.map(f_!(model.new_edge()));
|
||||
|
||||
outputs.edge_added <+ new_edge;
|
||||
new_edge_source <- new_edge.map2(&node_output_touch.down, move |id,node_id| (*id,EdgeTarget::new(node_id,default())));
|
||||
outputs.edge_source_set <+ new_edge_source;
|
||||
|
||||
|
||||
let add_node_at_cursor = inputs.add_node_at_cursor.clone_ref();
|
||||
add_node <- any (inputs.add_node, add_node_at_cursor);
|
||||
new_node <- add_node.map(f_!([model,node_cursor_style] model.new_node(&node_cursor_style,&node_output_touch.down)));
|
||||
outputs.node_added <+ new_node;
|
||||
|
||||
node_with_position <- add_node_at_cursor.map3(&new_node,&mouse.position,|_,id,pos| (*id,*pos));
|
||||
outputs.node_position_set <+ node_with_position;
|
||||
|
||||
cursor_style <- all
|
||||
[ cursor_selection
|
||||
, cursor_press
|
||||
, cursor_style_edge_drag
|
||||
, node_cursor_style
|
||||
].fold();
|
||||
|
||||
eval cursor_style ((style) cursor.frp.set_style.emit(style));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// === Node Connect ===
|
||||
|
||||
frp::extend! { network
|
||||
|
||||
outputs.edge_source_set <+ inputs.set_edge_source;
|
||||
outputs.edge_target_set <+ inputs.set_edge_target;
|
||||
|
||||
let endpoints = inputs.connect_nodes.clone_ref();
|
||||
edge <- endpoints . map(f_!(model.new_edge()));
|
||||
@ -1325,35 +1424,32 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
outputs.edge_source_set <+ new_edge_source;
|
||||
outputs.edge_target_set <+ new_edge_target;
|
||||
|
||||
new_node_input <- [inputs.press_node_input, inputs.connect_detached_edges_to_node];
|
||||
detached_targets <= new_node_input.map(f_!(model.edges.detached_target.mem_take()));
|
||||
new_edge_target <- detached_targets.map2(&new_node_input, |id,t| (*id,t.clone()));
|
||||
|
||||
port_mouse_up <- inputs.hover_node_input.sample(&mouse.release).unwrap();
|
||||
|
||||
attach_all_edges <- any (port_mouse_up, inputs.press_node_input, inputs.set_detached_edge_targets);
|
||||
detached_edge <= attach_all_edges.map(f_!(model.take_edges_with_detached_targets()));
|
||||
new_edge_target <- detached_edge.map2(&attach_all_edges, |id,t| (*id,t.clone()));
|
||||
outputs.edge_target_set <+ new_edge_target;
|
||||
|
||||
overlapping_edges <= outputs.edge_target_set._1().map(f!((t) model.overlapping_edges(t)));
|
||||
outputs.edge_removed <+ overlapping_edges;
|
||||
|
||||
drop_on_bg_up <- background_up.gate(&connect_drag_mode);
|
||||
drop_edges <- any (drop_on_bg_up,touch.background.down);
|
||||
edge_to_drop <= drop_edges.map(f_!(model.take_edges_with_detached_targets()));
|
||||
eval edge_to_drop ((id) model.remove_edge(id));
|
||||
|
||||
}
|
||||
|
||||
|
||||
// === Add Node ===
|
||||
frp::extend! { network
|
||||
|
||||
let add_node_at_cursor = inputs.add_node_at_cursor.clone_ref();
|
||||
add_node <- [inputs.add_node, add_node_at_cursor];
|
||||
new_node <- add_node.map(f_!([model,outputs] model.new_node(&outputs)));
|
||||
outputs.node_added <+ new_node;
|
||||
|
||||
node_with_position <- add_node_at_cursor.map3(&new_node,&mouse.position,|_,id,pos| (*id,*pos));
|
||||
outputs.node_position_set <+ node_with_position;
|
||||
}
|
||||
|
||||
|
||||
// === Remove Node ===
|
||||
frp::extend! { network
|
||||
|
||||
all_nodes <= inputs.remove_all_nodes . map(f_!(model.all_nodes()));
|
||||
selected_nodes <= inputs.remove_selected_nodes . map(f_!(model.selected_nodes()));
|
||||
nodes_to_remove <- [all_nodes, selected_nodes];
|
||||
nodes_to_remove <- any (all_nodes, selected_nodes);
|
||||
eval nodes_to_remove ((node_id) inputs.remove_all_node_edges.emit(node_id));
|
||||
|
||||
outputs.node_removed <+ nodes_to_remove;
|
||||
@ -1363,16 +1459,16 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
// === Remove Edge ===
|
||||
frp::extend! { network
|
||||
|
||||
rm_input_edges <- [inputs.remove_all_node_edges, inputs.remove_all_node_input_edges];
|
||||
rm_output_edges <- [inputs.remove_all_node_edges, inputs.remove_all_node_output_edges];
|
||||
rm_input_edges <- any (inputs.remove_all_node_edges, inputs.remove_all_node_input_edges);
|
||||
rm_output_edges <- any (inputs.remove_all_node_edges, inputs.remove_all_node_output_edges);
|
||||
input_edges_to_rm <= rm_input_edges . map(f!((node_id) model.node_in_edges(node_id)));
|
||||
output_edges_to_rm <= rm_output_edges . map(f!((node_id) model.node_out_edges(node_id)));
|
||||
edges_to_rm <- [inputs.remove_edge, input_edges_to_rm, output_edges_to_rm];
|
||||
edges_to_rm <- any (inputs.remove_edge, input_edges_to_rm, output_edges_to_rm);
|
||||
outputs.edge_removed <+ edges_to_rm;
|
||||
}
|
||||
|
||||
|
||||
// === Set NodeView Expression ===
|
||||
// === Set Node Expression ===
|
||||
frp::extend! { network
|
||||
|
||||
outputs.node_expression_set <+ inputs.set_node_expression;
|
||||
@ -1382,10 +1478,10 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
|
||||
node_drag <- mouse.translation.gate(&touch.nodes.is_down);
|
||||
was_selected <- touch.nodes.down.map(f!((id) model.nodes.selected.contains(id)));
|
||||
tx_sel_nodes <- [node_drag, inputs.translate_selected_nodes];
|
||||
tx_sel_nodes <- any (node_drag, inputs.translate_selected_nodes);
|
||||
non_selected_drag <- tx_sel_nodes.map2(&touch.nodes.down,|_,id|*id).gate_not(&was_selected);
|
||||
selected_drag <= tx_sel_nodes.map(f_!(model.nodes.selected.keys())).gate(&was_selected);
|
||||
nodes_to_drag <- [non_selected_drag,selected_drag];
|
||||
nodes_to_drag <- any (non_selected_drag, selected_drag);
|
||||
nodes_new_pos <- nodes_to_drag.map2(&tx_sel_nodes,f!((id,tx) model.node_pos_mod(id,tx)));
|
||||
outputs.node_position_set <+ nodes_new_pos;
|
||||
|
||||
@ -1398,13 +1494,15 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
|
||||
// === Move Edges ===
|
||||
|
||||
def _move_connections = cursor.frp.position.map(f!([edges](position) {
|
||||
cursor_pos_on_detach <- cursor.frp.position.sample(&inputs.some_edge_targets_detached);
|
||||
edge_refresh_cursor_pos <- any (cursor_pos_on_detach,cursor.frp.position);
|
||||
eval edge_refresh_cursor_pos ([edges](position) {
|
||||
edges.detached_target.for_each(|id| {
|
||||
if let Some(edge) = edges.get_cloned_ref(id) {
|
||||
edge.view.events.target_position.emit(position)
|
||||
edge.view.frp.target_position.emit(position)
|
||||
}
|
||||
})
|
||||
}));
|
||||
});
|
||||
|
||||
// === Vis Cycling ===
|
||||
def _cycle_vis = inputs.debug_cycle_visualization_for_selected_node.map(f!([inputs,nodes](_) {
|
||||
@ -1472,8 +1570,11 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
|
||||
// === OUTPUTS REBIND ===
|
||||
|
||||
eval outputs.edge_source_set (((id,tgt)) model.connect_edge_source(*id,tgt));
|
||||
eval outputs.edge_target_set (((id,tgt)) model.connect_edge_target(*id,tgt));
|
||||
outputs.some_edge_targets_detached <+ inputs.some_edge_targets_detached;
|
||||
outputs.all_edge_targets_attached <+ inputs.all_edge_targets_attached;
|
||||
|
||||
eval outputs.edge_source_set (((id,tgt)) model.set_edge_source(*id,tgt));
|
||||
eval outputs.edge_target_set (((id,tgt)) model.set_edge_target(*id,tgt));
|
||||
eval outputs.node_selected ((id) model.select_node(id));
|
||||
eval outputs.node_deselected ((id) model.deselect_node(id));
|
||||
eval outputs.edge_removed ((id) model.remove_edge(id));
|
||||
@ -1482,9 +1583,9 @@ fn new_graph_editor(world:&World) -> GraphEditor {
|
||||
|
||||
|
||||
|
||||
// === Connection discovery ===
|
||||
// === Edge discovery ===
|
||||
|
||||
edge_endpoint_set <- [outputs.edge_source_set,outputs.edge_target_set]._0();
|
||||
edge_endpoint_set <- any (outputs.edge_source_set, outputs.edge_target_set)._0();
|
||||
both_endpoints_set <- edge_endpoint_set.map(f!((id) model.is_connection(id)));
|
||||
new_connection <- edge_endpoint_set.gate(&both_endpoints_set);
|
||||
outputs.connection_added <+ new_connection;
|
||||
|
@ -2,7 +2,6 @@
|
||||
//! various general-purpose instances.
|
||||
|
||||
use super::semigroup::Semigroup;
|
||||
use super::semigroup::SemigroupIm;
|
||||
|
||||
|
||||
|
||||
@ -24,23 +23,16 @@ pub trait Monoid : Default + Semigroup {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Immutable Monoid definition.
|
||||
pub trait MonoidIm : Default + SemigroupIm {
|
||||
/// Repeat a value n times. Given that this works on a Monoid it will not fail if you request 0
|
||||
/// or fewer repetitions.
|
||||
fn times(&self, n:usize) -> Self {
|
||||
std::iter::repeat(self).take(n).fold(Default::default(),|l,r| l.concat(r))
|
||||
std::iter::repeat(self).take(n).fold(Default::default(),|l,r| l.concat_ref(r))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Default Impls ===
|
||||
|
||||
impl<T> Monoid for T where T : Default + Semigroup {}
|
||||
impl<T> MonoidIm for T where T : Default + SemigroupIm {}
|
||||
impl<T> Monoid for T where T : Default + Semigroup {}
|
||||
|
||||
|
||||
|
||||
@ -62,3 +54,54 @@ mod tests {
|
||||
assert_eq!(vec_1_2.times(3) , vec_1_2_times_3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Think what to do with this. It would not be needed if tuples implement Iter. Alternatively
|
||||
// we could immplement own tuple type.
|
||||
|
||||
//trait Foldable {
|
||||
// type Item : Monoid;
|
||||
// fn fold(self) -> Self::Item;
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
//macro_rules! replace {
|
||||
// ($a:tt,$b:tt) => {$b};
|
||||
//}
|
||||
//
|
||||
//
|
||||
//macro_rules! define_foldable_for_tuple {
|
||||
// (0$(,$num:tt)*) => {
|
||||
// impl<T:Monoid> Foldable for (T,$(replace!{$num,T}),*) {
|
||||
// type Item = T;
|
||||
// fn fold(self) -> Self::Item {
|
||||
// self.0$(.concat(self.$num))*
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<T:Monoid> Foldable for &(T,$(replace!{$num,T}),*) {
|
||||
// type Item = T;
|
||||
// fn fold(self) -> Self::Item {
|
||||
// self.0.clone()$(.concat(&self.$num))*
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//}
|
||||
//
|
||||
//define_foldable_for_tuple![0];
|
||||
//define_foldable_for_tuple![0,1];
|
||||
//define_foldable_for_tuple![0,1,2];
|
||||
//define_foldable_for_tuple![0,1,2,3];
|
||||
//define_foldable_for_tuple![0,1,2,3,4];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9,10];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9,10,11];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9,10,11,12];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9,10,11,12,13];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9,10,11,12,13,14];
|
||||
//define_foldable_for_tuple![0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
|
||||
|
@ -17,58 +17,34 @@ use std::iter::Extend;
|
||||
/// Mutable Semigroup definition. Impls should satisfy the associativity law:
|
||||
/// `x.concat(y.concat(z)) = x.concat(y).concat(z)`, in symbolic form:
|
||||
/// `x <> (y <> z) = (x <> y) <> z`
|
||||
pub trait Semigroup : Clone {
|
||||
pub trait PartialSemigroup<T> : Clone {
|
||||
/// An associative operation.
|
||||
fn concat_mut(&mut self, other:&Self);
|
||||
fn concat_mut(&mut self, other:T);
|
||||
|
||||
/// An associative operation which consumes the argument.
|
||||
fn concat_mut_take(&mut self, other:Self) {
|
||||
self.concat_mut(&other)
|
||||
/// An associative operation.
|
||||
fn concat_ref(&self, other:T) -> Self where Self:Clone {
|
||||
self.clone().concat(other)
|
||||
}
|
||||
|
||||
/// Repeat a value n times. Given that this works on a Semigroup it is allowed to fail if you
|
||||
/// request 0 or fewer repetitions, and the default definition will do so.
|
||||
fn partial_times(&mut self, n:usize) {
|
||||
/// An associative operation.
|
||||
fn concat(mut self, other:T) -> Self {
|
||||
self.concat_mut(other);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Semigroup for T where T : PartialSemigroup<T> + for<'t> PartialSemigroup<&'t T> {}
|
||||
pub trait Semigroup : PartialSemigroup<Self> + for<'t> PartialSemigroup<&'t Self> {
|
||||
fn partial_times_mut(&mut self, n:usize) {
|
||||
let val = self.clone();
|
||||
for _ in 0..n-1 {
|
||||
self.concat_mut(&val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable Semigroup definition. Impls should satisfy the associativity law:
|
||||
/// `x.concat(y.concat(z)) = x.concat(y).concat(z)`, in symbolic form:
|
||||
/// `x <> (y <> z) = (x <> y) <> z`
|
||||
pub trait SemigroupIm : Clone {
|
||||
/// An associative operation.
|
||||
fn concat(&self, other:&Self) -> Self;
|
||||
|
||||
/// An associative operation which consumes the argument.
|
||||
fn concat_take(&self, other:Self) -> Self {
|
||||
self.concat(&other)
|
||||
}
|
||||
|
||||
/// Repeat a value n times. Given that this works on a Semigroup it is allowed to fail if you
|
||||
/// request 0 or fewer repetitions, and the default definition will do so.
|
||||
fn partial_times(&self, n:usize) -> Self where Self:Clone {
|
||||
std::iter::repeat(self).take(n-1).fold(self.clone(),|l,r| l.concat(r))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Default Impls ===
|
||||
|
||||
impl<T:Semigroup> SemigroupIm for T {
|
||||
default fn concat(&self, other:&Self) -> Self {
|
||||
let mut this = self.clone();
|
||||
this.concat_mut(other);
|
||||
this
|
||||
}
|
||||
|
||||
default fn concat_take(&self, other:Self) -> Self {
|
||||
let mut this = self.clone();
|
||||
this.concat_mut_take(other);
|
||||
this
|
||||
fn partial_times(mut self, n:usize) -> Self {
|
||||
self.partial_times_mut(n);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +56,7 @@ impl<T:Semigroup> SemigroupIm for T {
|
||||
|
||||
// === Option ===
|
||||
|
||||
impl<T:Semigroup> Semigroup for Option<T> {
|
||||
impl<T:Semigroup> PartialSemigroup<&Option<T>> for Option<T> {
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
if let Some(r) = other {
|
||||
match self {
|
||||
@ -89,12 +65,14 @@ impl<T:Semigroup> Semigroup for Option<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn concat_mut_take(&mut self, other:Self) {
|
||||
impl<T:Semigroup> PartialSemigroup<Option<T>> for Option<T> {
|
||||
fn concat_mut(&mut self, other:Self) {
|
||||
if let Some(r) = other {
|
||||
match self {
|
||||
None => *self = Some(r),
|
||||
Some(l) => l.concat_mut_take(r)
|
||||
Some(l) => l.concat_mut(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,7 +81,7 @@ impl<T:Semigroup> Semigroup for Option<T> {
|
||||
|
||||
// === HashMap ===
|
||||
|
||||
impl<K,V,S> Semigroup for HashMap<K,V,S>
|
||||
impl<K,V,S> PartialSemigroup<&HashMap<K,V,S>> for HashMap<K,V,S>
|
||||
where K : Eq + Hash + Clone,
|
||||
V : Semigroup,
|
||||
S : Clone + BuildHasher {
|
||||
@ -115,8 +93,13 @@ where K : Eq + Hash + Clone,
|
||||
.or_insert_with(|| new_val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn concat_mut_take(&mut self, other:Self) {
|
||||
impl<K,V,S> PartialSemigroup<HashMap<K,V,S>> for HashMap<K,V,S>
|
||||
where K : Eq + Hash + Clone,
|
||||
V : Semigroup,
|
||||
S : Clone + BuildHasher {
|
||||
fn concat_mut(&mut self, other:Self) {
|
||||
for (key,new_val) in other {
|
||||
self.entry(key)
|
||||
.and_modify(|val| val.concat_mut(&new_val))
|
||||
@ -128,12 +111,14 @@ where K : Eq + Hash + Clone,
|
||||
|
||||
// === Vec ===
|
||||
|
||||
impl<T:Clone> Semigroup for Vec<T> {
|
||||
impl<T:Clone> PartialSemigroup<&Vec<T>> for Vec<T> {
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
self.extend(other.iter().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
fn concat_mut_take(&mut self, other:Self) {
|
||||
impl<T:Clone> PartialSemigroup<Vec<T>> for Vec<T> {
|
||||
fn concat_mut(&mut self, other:Self) {
|
||||
self.extend(other.into_iter())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user