diff --git a/package-lock.json b/package-lock.json index 3f63bbb4..46ec9226 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1793,7 +1793,8 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true }, "assign-symbols": { "version": "1.0.0", @@ -2110,12 +2111,6 @@ "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", - "dev": true - }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -2350,36 +2345,6 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, - "cacache": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.1.tgz", - "integrity": "sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "figgy-pudding": "^3.1.0", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.3", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.0", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - }, - "dependencies": { - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - } - } - }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -2751,6 +2716,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -2758,7 +2724,8 @@ "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true }, "commondir": { "version": "1.0.1", @@ -3357,7 +3324,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true }, "depd": { "version": "1.1.2", @@ -3682,9 +3650,9 @@ } }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "globals": { @@ -3694,9 +3662,9 @@ "dev": true }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -4161,7 +4129,8 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "optional": true }, "fast-deep-equal": { "version": "1.1.0", @@ -4479,7 +4448,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -4500,12 +4470,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4520,17 +4492,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4647,7 +4622,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4659,6 +4635,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4673,6 +4650,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4680,12 +4658,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4704,6 +4684,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4791,7 +4772,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4803,6 +4785,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4888,7 +4871,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4924,6 +4908,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4943,6 +4928,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4986,12 +4972,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -5270,9 +5258,9 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", @@ -5723,6 +5711,12 @@ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6174,7 +6168,8 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true }, "jsesc": { "version": "2.5.2", @@ -6389,9 +6384,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.camelcase": { "version": "4.3.0", @@ -6760,9 +6755,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -8273,27 +8268,75 @@ } }, "react": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.4.1.tgz", - "integrity": "sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", + "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } + } + } } }, "react-dom": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.1.tgz", - "integrity": "sha512-1Gin+wghF/7gl4Cqcvr1DxFX2Osz7ugxSwl6gBqCMpdrxHjIFUS7GYxrFftZ9Ln44FHw0JxCFD9YtZsrbR5/4A==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", + "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2", + "scheduler": "^0.18.0" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } + } + } } }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -8731,6 +8774,15 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "scheduler": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", + "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -8818,12 +8870,6 @@ } } }, - "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", - "dev": true - }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -8884,9 +8930,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -9161,6 +9207,24 @@ "urix": "^0.1.0" } }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -9592,65 +9656,60 @@ "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==", "dev": true }, - "terser": { - "version": "3.10.12", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.10.12.tgz", - "integrity": "sha512-3ODPC1eVt25EVNb04s/PkHxOmzKBQUF6bwwuR6h2DbEF8/j265Y1UkwNtOk9am/pRxfJ5HPapOlUlO6c16mKQQ==", + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1", - "source-map-support": "~0.5.6" + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" }, "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", - "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" } - } - } - }, - "terser-webpack-plugin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz", - "integrity": "sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==", - "dev": true, - "requires": { - "cacache": "^11.0.2", - "find-cache-dir": "^2.0.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "terser": "^3.8.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { + }, "find-cache-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", - "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, "requires": { "commondir": "^1.0.1", - "make-dir": "^1.0.0", + "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" } }, @@ -9663,6 +9722,26 @@ "locate-path": "^3.0.0" } }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -9673,10 +9752,29 @@ "path-exists": "^3.0.0" } }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -9692,9 +9790,15 @@ } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, "pkg-dir": { @@ -9706,11 +9810,68 @@ "find-up": "^3.0.0" } }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "terser": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.1.tgz", + "integrity": "sha512-w0f2OWFD7ka3zwetgVAhNMeyzEbj39ht2Tb0qKflw9PmW9Qbo5tjTh01QJLkhO9t9RDDQYvk+WXqpECI2C6i2A==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } } }, @@ -9871,7 +10032,8 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true }, "type-check": { "version": "0.3.2", @@ -9921,15 +10083,21 @@ "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==" }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.4.tgz", + "integrity": "sha512-tinYWE8X1QfCHxS1lBS8yiDekyhSXOO6R66yNOCdUJeojxxw+PX2BHAz/BWyW7PQ7pkiWVxJfIEbiDxyLWvUGg==", "optional": true, "requires": { - "commander": "~2.20.0", + "commander": "~2.20.3", "source-map": "~0.6.1" }, "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "optional": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9977,38 +10145,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "uniq": { @@ -11411,15 +11556,6 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, - "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index be473f2e..28b2c65c 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "clipper-lib": "6.2.1", "diff-match-patch": "1.0.0", "earcut": "2.1.1", - "handlebars": "^4.1.2", + "handlebars": "^4.5.3", "handlebars-loader": "1.7.1", "jquery": "^3.4.1", "less": "^3.8.1", @@ -63,8 +63,8 @@ "mousetrap": "1.6.1", "numeric": "1.2.6", "prop-types": "15.6.0", - "react": "^16.4.0", - "react-dom": "^16.4.0", + "react": "^16.8.6", + "react-dom": "^16.8.6", "sprintf": "0.1.5", "three": "0.89.0" } diff --git a/web/app/sketcher/actions/constraintActions.js b/web/app/sketcher/actions/constraintActions.js new file mode 100644 index 00000000..9f1a22a6 --- /dev/null +++ b/web/app/sketcher/actions/constraintActions.js @@ -0,0 +1,89 @@ +import {AlgNumConstraint, ConstraintDefinitions} from "../constr/ANConstraints"; +import {EndPoint} from "../shapes/point"; +import {Circle} from "../shapes/circle"; +import {Segment} from "../shapes/segment"; +import {matchAll, matchTypes, sortSelectionByType} from "./matchUtils"; + +export default [ + + + { + shortName: 'Coincident', + description: 'Point Coincident', + selectionMatcher: (selection, sortedByType) => matchAll(selection, EndPoint, 2), + + invoke: viewer => { + const [first, ...others] = viewer.selected; + let pm = viewer.parametricManager; + for (let obj of others) { + pm.algnNumSystem.addConstraint( + new AlgNumConstraint(ConstraintDefinitions.PCoincident, [first, obj]) + ); + } + pm.refresh(); + } + }, + + { + shortName: 'Tangent', + description: 'Tangent Between Line And Circle', + selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, Circle, 1, Segment, 1), + + invoke: viewer => { + const [circle, line] = sortSelectionByType(viewer.selected); + let pm = viewer.parametricManager; + const ang = line.params.ang.get(); + const w = line.params.w.get(); + const inverted = Math.cos(ang) * circle.c.x + Math.sin(ang) * circle.c.y < w ; + + pm.algnNumSystem.addConstraint(new AlgNumConstraint(ConstraintDefinitions.TangentLC, [line, circle], { + inverted + })); + } + + }, + + { + shortName: 'Point On Line', + description: 'Point On Line', + selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, EndPoint, 1, Segment, 1), + + invoke: viewer => { + const [pt, line] = sortSelectionByType(viewer.selected); + let pm = viewer.parametricManager; + pm.algnNumSystem.addConstraint(new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [pt, line])); + } + }, + + { + shortName: 'Angle', + description: 'Angle', + selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 1), + + invoke: viewer => { + + const firstSegment = viewer.selected[0]; + + const firstConstr = new AlgNumConstraint(ConstraintDefinitions.Angle, [firstSegment]); + firstConstr.initConstants(); + + viewer.streams.constraintEditRequest.next({ + constraint: firstConstr, + onCancel: () => viewer.streams.constraintEditRequest.next(null), + onApply: () => { + viewer.streams.constraintEditRequest.next(null); + const pm = viewer.parametricManager; + pm.algnNumSystem.addConstraint(firstConstr); + for (let i = 1; i < viewer.selected.length; ++i) { + pm.algnNumSystem.addConstraint(new AlgNumConstraint(ConstraintDefinitions.Angle, viewer.selected[i], {...firstConstr.constants})); + } + pm.refresh(); + } + }); + + } + } + + + +]; \ No newline at end of file diff --git a/web/app/sketcher/actions/index.js b/web/app/sketcher/actions/index.js new file mode 100644 index 00000000..9634928b --- /dev/null +++ b/web/app/sketcher/actions/index.js @@ -0,0 +1,25 @@ +import constraintActions from "./constraintActions"; +import {sortSelectionByType} from "./matchUtils"; + +const ALL_CONTEXTUAL_ACTIONS = [ + ...constraintActions, + //keep going here +]; + +export function matchAvailableActions(selection) { + + let sortedByType = sortSelectionByType(selection); + let matched = []; + + + if (selection.length) { + for (let action of ALL_CONTEXTUAL_ACTIONS) { + if (action.selectionMatcher(selection, sortedByType)) { + matched.push(action); + } + } + } + + return matched; + +} \ No newline at end of file diff --git a/web/app/sketcher/actions/matchUtils.js b/web/app/sketcher/actions/matchUtils.js new file mode 100644 index 00000000..57fc2d3e --- /dev/null +++ b/web/app/sketcher/actions/matchUtils.js @@ -0,0 +1,38 @@ + + + +export function matchAll(selection, shapeConstructor, min) { + if (min !== undefined && selection.length < min) { + return false; + } + for (let obj of selection) { + if (obj._class !== shapeConstructor.prototype._class) { + return false; + } + } + return true; +} + + +export function matchTypes(selection) { + let si = 0; + let i = 1; + for (; i < arguments.length; i+=2) { + let shapeConstructor = arguments[i]; + let quantity = arguments[i+1]; + if (si === selection.length) { + return false; + } + for (let j = 0; j < quantity && si < selection.length; j++) { + let obj = selection[si++]; + if (obj._class !== shapeConstructor.prototype._class) { + return false; + } + } + } + return si === selection.length && i === arguments.length; +} + +export function sortSelectionByType(selection) { + return [...selection].sort((a, b) => a._class.localeCompare(b._class)) +} diff --git a/web/app/sketcher/components/ConstraintEditor.jsx b/web/app/sketcher/components/ConstraintEditor.jsx new file mode 100644 index 00000000..ecf81ce6 --- /dev/null +++ b/web/app/sketcher/components/ConstraintEditor.jsx @@ -0,0 +1,73 @@ +import React, {useState} from 'react'; +import Widget from "ui/components/Widget"; +import NumberControl from "ui/components/controls/NumberControl"; +import {DEG_RAD} from "../../math/math"; +import Stack from "ui/components/Stack"; +import ButtonGroup from "ui/components/controls/ButtonGroup"; +import Button from "ui/components/controls/Button"; +import connect from "../../../../modules/ui/connect"; + + +export const ConstraintEditor = connect(streams => streams.sketcherApp.constraintEditRequest)( + function ConstraintEditor({constraint, onCancel, onApply}) { + return + } +); + +export function ConstraintEditorImpl({constraint, onCancel, onApply}) { + + if (!constraint) { + return null; + } + + const [values, setValues] = useState({...constraint.constants}); + + const setValue = (name, value) => { + setValues({...value, [name]: value}); + }; + + const apply = () => { + Object.keys(constraint.schema.constants).map(name => { + const val = values[name]; + if (val !== undefined) { + constraint.constants[name] = val; + } + }); + onApply(); + }; + + return + + + + {Object.keys(constraint.schema.constants).sort().map(name =>
+ + { + (() => { + const def = constraint.schema.constants[name]; + const val = values[name]; + if (def.type === 'number') { + return setValue(name, value)} /> + } else { + return {val}; + } + + })() + + } + +
)} + + + + + + + +
+ +
; + +} \ No newline at end of file diff --git a/web/app/sketcher/components/ContextualControls.jsx b/web/app/sketcher/components/ContextualControls.jsx new file mode 100644 index 00000000..b4d42361 --- /dev/null +++ b/web/app/sketcher/components/ContextualControls.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import ls from './ContextualControls.less'; +import connect from "../../../../modules/ui/connect"; +import {matchAvailableActions} from "../actions"; +import mapContext from "../../../../modules/ui/mapContext"; + +export const ContextualControls = + mapContext(ctx => ({ + invokeAction: action => action.invoke(ctx.viewer) + }))( + connect(streams => streams.sketcherApp.selection.map(selection => ({selection})))( + + + function ContextualControls({selection, invokeAction}) { + + if (selection.length === 0) { + return null; + } + + const availableActions = matchAvailableActions(selection); + + return
+ + { + selection.map(s =>
{s.simpleClassName}: {s.id}
) + } + +
AVAILABLE ACTIONS:
+ + { + availableActions.map(a => ) + } + +
; + + } +)); \ No newline at end of file diff --git a/web/app/sketcher/components/ContextualControls.less b/web/app/sketcher/components/ContextualControls.less new file mode 100644 index 00000000..37ae7ee7 --- /dev/null +++ b/web/app/sketcher/components/ContextualControls.less @@ -0,0 +1,15 @@ + +.root { + margin: 5px; + padding: 3px 5px; + background-color: #000D; + color: white; +} + +.item { + padding: 3px 0; +} + +.hr { + .item; +} \ No newline at end of file diff --git a/web/app/sketcher/components/RightSideControls.jsx b/web/app/sketcher/components/RightSideControls.jsx new file mode 100644 index 00000000..329dff76 --- /dev/null +++ b/web/app/sketcher/components/RightSideControls.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import {ConstraintEditor} from "./ConstraintEditor"; +import {ContextualControls} from "./ContextualControls"; + +export function RightSideControls() { + + return + + + ; + +} + + diff --git a/web/app/sketcher/constr/ANConstraints.js b/web/app/sketcher/constr/ANConstraints.js new file mode 100644 index 00000000..032a2ad5 --- /dev/null +++ b/web/app/sketcher/constr/ANConstraints.js @@ -0,0 +1,238 @@ +import {R_DistancePP, R_Equal, R_PointOnLine} from "./residuals"; +import {indexById} from "../../../../modules/gems/iterables"; +import {DEG_RAD, distanceAB} from "../../math/math"; +import {COS_FN, Polynomial, POW_1_FN, POW_2_FN, SIN_FN} from "./polynomial"; +import {Types} from "../io"; + +export const ConstraintDefinitions = indexById([ + + { + id: 'PCoincident', + name: 'Two Points Coincidence', + + defineParamsScope: ([p1, p2], callback) => { + p1.visitParams(callback); + p2.visitParams(callback); + }, + + + collectPolynomials: (polynomials, [x1, y1, x2, y2]) => { + polynomials.push(new Polynomial(0) + .monomial(1) + .term(x1, POW_1_FN) + .monomial(-1) + .term(x2, POW_1_FN) + ); + polynomials.push(new Polynomial(0) + .monomial(1) + .term(y1, POW_1_FN) + .monomial(-1) + .term(y2, POW_1_FN) + ); + }, + }, + + + { + id: 'TangentLC', + name: 'Line & Circle Tangency', + constants: { + inverted: { + type: 'boolean', + description: 'whether the circle attached from the opposite side', + initialValue: () => false + } + }, + + defineParamsScope: ([segment, circle], callback) => { + callback(segment.params.ang); + callback(segment.params.w); + circle.c.visitParams(callback); + callback(circle.r); + }, + + + collectPolynomials: (polynomials, [ang, w, cx, cy, r], {inverted}) => { + polynomials.push(new Polynomial(0) + .monomial(1) + .term(cx, POW_1_FN) + .term(ang, COS_FN) + .monomial(1) + .term(cy, POW_1_FN) + .term(ang, SIN_FN) + .monomial(-1) + .term(w, POW_1_FN) + .monomial(- (inverted ? -1 : 1)) + .term(r, POW_1_FN) + ); + }, + }, + + { + id: 'PointOnLine', + name: 'Point On Line', + + defineParamsScope: ([pt, segment], callback) => { + pt.visitParams(callback); + callback(segment.params.ang); + callback(segment.params.w); + }, + + collectResiduals: (residuals, params) => { + residuals.push([R_PointOnLine, params, []]); + }, + + collectPolynomials: (polynomials, [x, y, ang, w]) => { + polynomials.push(new Polynomial(0) + .monomial(1) + .term(x, POW_1_FN) + .term(ang, COS_FN) + .monomial(1) + .term(y, POW_1_FN) + .term(ang, SIN_FN) + .monomial(-1) + .term(w, POW_1_FN) + ); + }, + + }, + + { + id: 'DistancePP', + name: 'Distance Between Two Point', + constants: { + distance: { + type: 'number', + description: 'the distance between two points', + initialValue: (constraint) => { + const [a, b] = constraint.object; + return distanceAB(a, b).toFixed(2) + ''; + } + } + }, + + defineParamsScope: ([pt, segment], callback) => { + pt.visitParams(callback); + callback(segment.params.ang); + callback(segment.params.w); + }, + + collectResiduals: (residuals, params, {distance}) => { + residuals.push([R_DistancePP, params, [distance]]); + }, + + collectPolynomials: (polynomials, [x1, y1, x2, y2], {distance}) => { + polynomials.push(new Polynomial( - distance * distance) + .monomial(1) + .term(x1, POW_2_FN) + .monomial(1) + .term(x2, POW_2_FN) + .monomial(-2) + .term(x1, POW_1_FN) + .term(x2, POW_1_FN) + + .monomial(1) + .term(y1, POW_2_FN) + .monomial(1) + .term(y2, POW_2_FN) + .monomial(-2) + .term(y1, POW_1_FN) + .term(y2, POW_1_FN) + + ); + }, + + }, + + { + id: 'Angle', + name: 'Absolute Line Angle', + constants: { + angle: { + type: 'number', + description: 'line angle', + initialValue: (constraint) => { + let degrees = constraint.objects[0].params.ang.get() / DEG_RAD; + degrees = (degrees + 360 - 90) % 360; + return degrees.toFixed(2) + ''; + }, + transform: degree => ( (degree + 90) % 360 ) * DEG_RAD + } + }, + + defineParamsScope: ([segment], callback) => { + callback(segment.params.ang); + }, + + collectPolynomials: (polynomials, [x], {angle}) => { + polynomials.push(new Polynomial( - angle).monomial(1).term(x, POW_1_FN)); + }, + }, + +]); + +export class AlgNumConstraint { + + static Counter = 0; + + constructor(schema, objects, constants) { + this.id = schema.id + ':' + (AlgNumConstraint.Counter ++); // only for debug purposes - not persisted + this.objects = objects; + this.constants = constants; + this.resolvedConstants = undefined; + this.internal = false; + this.schema = schema; + this.params = []; + this.schema.defineParamsScope(this.objects, p => this.params.push(p)); + // this.paramSet = new Set(this.params); + } + + collectPolynomials(polynomials) { + this.resolveConstants(); + this.schema.collectPolynomials(polynomials, this.params, this.resolvedConstants); + } + + resolveConstants() { + if (this.constants) { + if (!this.resolvedConstants) { + this.resolvedConstants = {}; + } + Object.keys(this.constants).map(name => { + let def = this.schema.constants[name]; + if (def.type === 'number') { + let val = parseFloat(this.constants[name]); + if (def.transform) { + val = def.transform(val); + } + this.resolvedConstants[name] = val; + } + }); + } + } + + write() { + return { + typeId: this.schema.id, + objects: this.objects.map(o => o.id), + constants: this.constants + } + } + + static read({typeId, objects, constants}, index) { + const schema = ConstraintDefinitions[typeId]; + if (!schema) { + throw "constraint schema ' + typeId + ' doesn't exist"; + } + return new AlgNumConstraint(schema, objects.map(oId => index[oId]), constants); + } + + initConstants() { + if (this.schema.constants) { + this.constants = {}; + Object.keys(this.schema.constants).map(name => { + this.constants[name] = this.schema.constants[name].initialValue(this); + }); + } + } +} + diff --git a/web/app/sketcher/constr/AlgNumSystem.js b/web/app/sketcher/constr/AlgNumSystem.js new file mode 100644 index 00000000..60e953dd --- /dev/null +++ b/web/app/sketcher/constr/AlgNumSystem.js @@ -0,0 +1,237 @@ +import {createByConstraintName} from "./solverConstraints"; +import {Param, prepare} from "./solver"; +import {findConstructionCluster} from "./constructions"; +import {GCCircle, GCPoint} from "./constractibles"; +import {eqEps, eqTol} from "../../brep/geom/tolerance"; +import {Polynomial} from "./polynomial"; +import {NOOP} from "../../../../modules/gems/func"; + +export class AlgNumSubSystem { + + + generator = NOOP;//new BoundaryObjectsGenerator(); + + constraints = []; + + ownParams = new Set(); + + readOnlyParams = new Set(); + + generatedParams = new Set(); + + beingSolvedParams = new Set(); + + + // generators = []; + subSystems = []; //subsystems are generated by generators only + + polynomials = []; + substitutedParams = []; + polyToConstr = new Map(); + conflicting = new Set(); + redundant = new Set(); + + SubSystem(generator) { + this.generator = generator; + } + + addConstraint(constraint, _ancestorParams) { + + if (this.canBeAdded(constraint.params)) { + // this.constraints.push(constraint); + // this.constraints + } + + this.constraints.push(constraint); + } + + reset() { + this.polynomials = []; + this.substitutedParams = []; + this.polyToConstr.clear(); + this.conflicting.clear(); + this.redundant.clear(); + this.beingSolvedParams.clear(); + } + + evaluatePolynomials() { + this.constraints.forEach(c => { + c.collectPolynomials(this.polynomials); + this.polynomials.forEach(p => this.polyToConstr.set(p, c)) + }); + + + + const toEliminate = Array.from(this.readOnlyParams); + + const toSubstitute = []; + const linearSub = []; + + let requirePass = true; + + while (requirePass) { + requirePass = false; + for (let i = 0; i < this.polynomials.length; ++i) { + const polynomial = this.polynomials[i]; + if (!polynomial) { + continue; + } + + if (polynomial.monomials.length === 0) { + if (!eqEps(polynomial.constant, 0)) { + this.conflicting.add(this.polyToConstr.get(polynomial)); + } + this.polynomials[i] = null; + } else if (polynomial.monomials.length === 1) { + const monomial = polynomial.monomials[0]; + const terms = monomial.terms; + if (terms.length === 1) { + const term = terms[0]; + if (term.fn.degree === 1) { + term.param.set(- polynomial.constant / monomial.constant); + toEliminate.push(term.param); + } + } + + this.polynomials[i] = null; + } else if (polynomial.monomials.length === 2 && polynomial.isLinear) { + const [m1, m2] = polynomial.monomials; + + const constant = - m2.constant / m1.constant; + if (!eqEps(polynomial.constant, 0)) { + toSubstitute.push([m1.param, m2.param, constant]); + this.substitutedParams.push([m1.param, new Polynomial().monomial(constant).addParam(m2.param)]); + } else { + // linearSub.push([m1.param, m2.param, constant, - polynomial.constant / m1.constant]); + } + + + this.polynomials[i] = null; + } + } + + while (toEliminate.length) { + requirePass = true; + const param = toEliminate.pop(); + for (let polynomial of this.polynomials) { + if (polynomial) { + polynomial.eliminate(param, param.get()); + } + } + } + while (toSubstitute.length) { + requirePass = true; + const [param, toParam, dotConstant] = toSubstitute.pop(); + for (let polynomial of this.polynomials) { + if (polynomial) { + polynomial.substitute(param, toParam, dotConstant); + } + } + } + + if (requirePass) { + this.polynomials.forEach(polynomial => polynomial && polynomial.compact()); + } + } + + + + + while (toEliminate.length || toSubstitute.length) { + + + // while (linearSub.length) { + // const [param, toParam, b, c] = linearSub.pop(); + // for (let polynomial of polynomials) { + // if (polynomial) { + // polynomial.linerSub(param, toParam, b, c); + // } + // } + // } + + } + + this.polynomials = this.polynomials.filter(p => p); + + } + + + prepare() { + + this.reset(); + this.evaluatePolynomials(); + + console.log('solving system:'); + this.polynomials.forEach(p => console.log(p.toString())); + + const residuals = []; + + this.polynomials.forEach(p => residuals.push(p.asResidual())); + + for (let residual of residuals) { + residual.params.forEach(solverParam => { + if (!this.beingSolvedParams.has(solverParam)) { + solverParam.reset(solverParam.objectParam.get()); + this.beingSolvedParams.add(solverParam); + } + }); + } + this.numericalSolver = prepare(residuals); + } + + + solve(rough) { + this.generator(); + + this.beingSolvedParams.forEach(solverParam => { + solverParam.set(solverParam.objectParam.get()); + }); + + this.numericalSolver.solveSystem(rough); + + this.beingSolvedParams.forEach(solverParam => { + solverParam.objectParam.set(solverParam.get()); + }); + for (let i = this.substitutedParams.length - 1; i >= 0; i--) { + let [param, expression] = this.substitutedParams[i]; + param.set(expression.value); + } + } + + + + canBeAdded(subjectParams, ancestorParams) { + + for (let p of subjectParams) { + if (!this.ownParams.has(p) && (!ancestorParams || !ancestorParams.has(p))) { + return false; + } + } + + return true; + } +} + + +export class AlgNumSystem { + constraints = []; + generators = []; + locked = new Set(); + constantParams = new Set(); + + constructor(visitAllObjects) { + this.visitAllObjects = visitAllObjects; + } + + addConstraint(constraint) { + this.constraints.push(constraint); + if (constraint.schema.generator) { + + } + } + + startTransaction(interactiveLock = []) { + this.systemTransaction.prepare(interactiveLock); + return this.systemTransaction; + } +} diff --git a/web/app/sketcher/constr/SEACConstraints.js b/web/app/sketcher/constr/SEACConstraints.js deleted file mode 100644 index aa33437a..00000000 --- a/web/app/sketcher/constr/SEACConstraints.js +++ /dev/null @@ -1,99 +0,0 @@ -import {R_Distance, R_DistancePP, R_PointOnLine, R_TangentLC, R_UnitVector} from "./residuals"; -import {indexById} from "../../../../modules/gems/iterables"; -import {distanceAB} from "../../math/math"; - -export const ConstraintDefinitions = indexById([ - - { - id: 'TangentLC', - name: 'Line & Circle Tangency', - constants: { - inverted: { - type: 'boolean', - description: 'whether the circle attached from the opposite side', - initialValue: () => false - } - }, - - constructibleObjects: ([line, circle]) => [line.gcLine, circle.gcCircle], - - collectResiduals: (residuals, constraint) => { - const [gcLine] = constraint.objects; - residuals.push([R_TangentLC, constraint.params, [constraint.constants.inverted]]); - } - - - }, - - { - id: 'PointOnLine', - name: 'Point On Line', - - constructibleObjects: ([point, line]) => [point.gcPoint, line.gcLine], - - collectResiduals: (residuals, constraint) => { - residuals.push([R_PointOnLine, constraint.params, []]); - } - - }, - - { - id: 'DistancePP', - name: 'Distance Between Two Point', - constants: { - distance: { - type: 'number', - description: 'the distance between two points', - initialValue: (constraint) => { - const [a, b] = constraint.object; - return distanceAB(a, b) - } - } - }, - - constructibleObjects: ([p1, p2]) => [p1.gcPoint, p2.gcPoint], - - collectResiduals: (residuals, constraint) => { - residuals.push([R_DistancePP, constraint.params, [constraint.constants.distance]]); - } - - }, - - -]); - -export class SEACConstraint { - - static Counter = 0; - - constructor(schema, attachedObjects, constants) { - this.id = schema.id + ':' + (SEACConstraint.Counter ++); // only for debug purposes - not persisted - this.attachedObjects = attachedObjects; - this.objects = schema.constructibleObjects(attachedObjects); - this.constants = constants; - this.objects.forEach(o => { - o.constraints.push(this); - }); - this.internal = false; - this.schema = schema; - } - - collectParams(params) { - this.objects.forEach(o => o.collectParams(params)); - } - - get params() { - const params = []; - this.collectParams(params); - return params; - } - - visitAdjacentConstraints(fn) { - this.objects.forEach(o => o.constraints.forEach(fn)); - } - - collectResiduals(out) { - return this.schema.collectResiduals(out, this); - } -} - diff --git a/web/app/sketcher/constr/SEACSystem.js b/web/app/sketcher/constr/SEACSystem.js deleted file mode 100644 index a5e56869..00000000 --- a/web/app/sketcher/constr/SEACSystem.js +++ /dev/null @@ -1,291 +0,0 @@ -import {createByConstraintName} from "./solverConstraints"; -import {Param, prepare} from "./solver"; -import {findConstructionCluster} from "./constructions"; -import {GCCircle, GCPoint} from "./constractibles"; - - -const ConstraintDegree = { - PARTIALLY_SOLVABLE: 1, - NOT_SOLVABLE: 2, - SOLVABLE: 3 -}; - - -export class SEACSystem { - constraints = []; - locked = new Set(); - systemTransaction = new SystemTransaction(this); - - addConstraint(constraint) { - this.constraints.push(constraint); - } - - startTransaction(interactiveLock = []) { - this.systemTransaction.prepare(interactiveLock); - return this.systemTransaction; - } -} - -export class SystemTransaction { - - locked = new Set(); - scheduled = new Map(); - internalObjects = new Set(); - decayedObjects = new Set(); - clusters = []; - - constructor(system) { - this.system = system; - } - - constraintDegree(constraint) { - - let hasNonFreeAgents = false; - let hasFreeAgents = false; - - for (let obj of constraint.objects) { - - if (this.isLocked(obj) || this.decayedObjects.has(obj)) { - hasNonFreeAgents = true; - } else { - hasFreeAgents = true; - } - } - - if (hasNonFreeAgents && hasFreeAgents) { - return ConstraintDegree.PARTIALLY_SOLVABLE; - } else if (hasNonFreeAgents) { - return ConstraintDegree.NOT_SOLVABLE; - } else { - return ConstraintDegree.SOLVABLE; - } - } - - findClusterForIsolated(constraint) { - for (let i = this.clusters.length - 1; i >= 0; i--) { - const cluster = this.clusters[i]; - if (constraint.objects.find(o => cluster.ownObjects.has(o))) { - return cluster; - } - } - console.log(`constraint ${constraint.id} can't be solved and will be skipped, most likely it's attached to read only geometry only`); - return null; - } - - createCluster(constraints = []) { - const cluster = new Cluster(this, constraints); - this.clusters.push(cluster); - return cluster; - } - - lock(object) { - this.locked.add(object); - } - - isLocked = object => { - return this.locked.has(object); - }; - - schedule(constr, cluster) { - this.scheduled.set(constr, cluster); - } - - isScheduled = constr => { - return this.scheduled.has(constr); - }; - - prepare(interactiveLock) { - const schedule = queue => { - - for (let constr of queue) { - - if (this.isScheduled(constr)) { - continue; // we're cool - solved as a part of construction cluster - } - - let clusterConstraints = findConstructionCluster(constr, this.isScheduled); - clusterConstraints.forEach((c, i) => { - if (this.constraintDegree(c) === ConstraintDegree.NOT_SOLVABLE) { - console.log('isolation detected for constraint ' + constr.id); - const neighbour = this.findClusterForIsolated(c); - if (!neighbour) { - console.warn('unable to resolve isolation for constraint ' + constr.id); - return; - } - neighbour.add(c); - clusterConstraints[i] = null; - } - }); - clusterConstraints = clusterConstraints.filter(c => c !== null); - if (clusterConstraints.length) { - this.createCluster(clusterConstraints); - } - } - - }; - - this.cleanup(); - - interactiveLock.forEach(l => this.locked.add(l)); - this.system.locked.forEach(l => this.locked.add(l)); - - this.system.constraints.forEach(c => c.objects.forEach(o => o.visitChildren( c => { - this.internalObjects.add(c); - if (this.isLocked(c)) { - this.decayedObjects.add(o); - } - } )) ); - - const queue = [...this.system.constraints]; - const constraintRank = constr => { - const pureSEAC = !constr.objects.find(o => o instanceof GCPoint); - const hasLocks = !!constr.objects.find(o => this.locked.has(o) || this.decayedObjects.has(o)); - const referToInternals = !!constr.objects.find(o => this.internalObjects.has(o)); - let rank = 0; - if (referToInternals) { - rank += 1000; - } - if (!pureSEAC) { - rank += 100 - } - if (!hasLocks) { - rank += 10; - } - return rank; - }; - queue.sort((a,b) => constraintRank(a) - constraintRank(b)); - - schedule(queue); - - console.log("SOLVER SCHEDULING RESULTS:"); - console.dir(this.clusters); - - this.clusterTransactions = this.clusters.map(cluster => cluster.startTransaction()); - - } - - cleanup() { - this.locked.clear(); - this.scheduled.clear(); - this.internalObjects.clear(); - this.decayedObjects.clear(); - this.clusters = []; - this.clusterTransactions = undefined; - } - - relaxObjects() { - this.clusterTransactions.forEach(ct => { - ct.cluster.ownObjects.forEach(o => { - if (o instanceof GCCircle) { - if (o.r.get() <= 5) { - o.r.set(20); - } - } - }); - }); - } - - solve = rough => { - this.relaxObjects(); - this.clusterTransactions.forEach(t => t.solve(rough)); - } -} - - -class Cluster { - - constructor(systemTransaction, constraints) { - this.systemTransaction = systemTransaction; - this.constraints = []; - this.ownObjects = new Set(); - constraints.forEach(c => this.add(c)); - } - - consumeObject(o) { - this.systemTransaction.lock(o); - this.ownObjects.add(o); - } - - add(constraint) { - this.systemTransaction.schedule(constraint, this); - constraint.objects.forEach(o => { - if (!this.systemTransaction.isLocked(o)) { - this.consumeObject(o); - o.visitChildren(c => { - if (!this.systemTransaction.isLocked(c)) { - this.consumeObject(c); - } - }); - } - }); - this.constraints.push(constraint); - } - - get capacity() { - return this.constraints.length; - } - - startTransaction() { - - const residuals = []; - const solverConstrs = []; - const transState = new TransactionState(); - - this.constraints.forEach(c => c.collectResiduals(residuals)); - - for (let i = 0; i < residuals.length; ++i) { - - const [fn, gcParams, constants] = residuals[i]; - - const solverParams = gcParams.map(gcParam => { - const solverParam = transState.createSolverParam(gcParam); - - solverParam.constant = !this.ownObjects.has(gcParam.object); - return solverParam; - }); - - const constr = fn(solverParams, constants); - solverConstrs.push(constr); - } - const solver = prepare(solverConstrs); - return new ClusterTransaction(solver, transState, this); - } -} - -class TransactionState { - - paramMap = new Map(); - - createSolverParam(gcParam) { - let solverParam = this.paramMap.get(gcParam); - if (!solverParam) { - solverParam = new Param(gcParam.get()); - this.paramMap.set(gcParam, solverParam); - } - return solverParam; - } -} - -class ClusterTransaction { - - constructor(solver, state, cluster) { - this.solver = solver; - this.state = state; - this.cluster = cluster; - } - - solve(rough) { - this.state.paramMap.forEach((solverParam, gcParam) => { - solverParam.reset(gcParam.get()); - }); - - this.solver.solveSystem(rough); - - this.state.paramMap.forEach((solverParam, gcParam) => { - if (!solverParam.constant) { - gcParam.set(solverParam.get()); - } - }); - } - -} diff --git a/web/app/sketcher/constr/constractibles.js b/web/app/sketcher/constr/constractibles.js index f11f8e56..d47d199d 100644 --- a/web/app/sketcher/constr/constractibles.js +++ b/web/app/sketcher/constr/constractibles.js @@ -1,4 +1,3 @@ -import {Param} from "./solver"; import {Generator} from "../id-generator"; export class ContractibleObject { @@ -6,28 +5,46 @@ export class ContractibleObject { constraints = []; constructor() { - } - - createParam(value) { - return new GCParam(this, value); + this.id = Generator.genID(); } collectParams(out) { this.visitParams(p => out.push(p)); } + init() {}; + visitParams() {}; visitChildren() {}; + traverse(visitor) { + visitor(this); + } + + write() { + const out = []; + this.visitChildren(c => out.push(c.id)); + return out; + } + + read(data, resolve) { + this.init.apply(this, data.map(resolve)); + } } export class GCPoint extends ContractibleObject { - constructor() { - super(); - this.x = this.createParam(0); - this.y = this.createParam(0); + static TYPE = 'GCPoint'; + + static newInstance(x = 0, y = 0) { + return new GCPoint().init(new GCParam(x), new GCParam(y)); + } + + init(x, y) { + this.x = x; + this.y = y; + return this; } visitParams(visitor) { @@ -35,14 +52,35 @@ export class GCPoint extends ContractibleObject { visitor(this.y); } + visitChildren(visitor) { + visitor(this.x); + visitor(this.y); + } + + traverse(visitor) { + super.traverse(visitor); + this.x.traverse(visitor); + this.y.traverse(visitor); + } + + write() { + return [this.x.id, this.y.id]; + } + } export class GCLine extends ContractibleObject { - constructor() { - super(); - this.ang = this.createParam(0); - this.w = this.createParam(0); + static TYPE = 'GCLine'; + + static newInstance(ang = 0, w = 0) { + return new GCLine().init(new GCParam(ang), new GCParam(w)); + } + + init(ang, w) { + this.ang = ang; + this.w = w; + return this; } visitParams(visitor) { @@ -50,14 +88,31 @@ export class GCLine extends ContractibleObject { visitor(this.w); } + visitChildren(visitor) { + visitor(this.ang); + visitor(this.w); + } + + traverse(visitor) { + super.traverse(visitor); + this.ang.traverse(visitor) + this.w.traverse(visitor) + } + } export class GCCircle extends ContractibleObject { - constructor() { - super(); - this.c = new GCPoint(); - this.r = this.createParam(0); + static TYPE = 'GCCircle'; + + static newInstance(x, y, r) { + return GCCircle().init(new GCPoint(x, y), new GCParam(r)) + } + + init(c, r) { + this.c = c; + this.r = r; + return this; } visitParams(visitor) { @@ -67,14 +122,27 @@ export class GCCircle extends ContractibleObject { visitChildren(visitor) { visitor(this.c); + visitor(this.r); + } + + traverse(visitor) { + visitor(this); + this.c.traverse(visitor); + this.r.traverse(visitor); } } -export class GCParam { +export class GCParam extends ContractibleObject { - constructor(object, value) { - this.object = object; + static TYPE = 'GCParam'; + + static newInstance(value = 0) { + return new GCParam(value); + } + + constructor(value) { + super(); this.value = value; } @@ -86,4 +154,28 @@ export class GCParam { return this.value; } -} \ No newline at end of file + visitChildren(visitor) { + } + + visitParams(visitor) { + visitor(this); + } + + write() { + return this.value; + } + + read(data) { + this.value = data; + } + +} + +export const GC_TYPES = { + + [GCParam.TYPE]: GCParam, + [GCPoint.TYPE]: GCPoint, + [GCLine.TYPE]: GCLine, + [GCCircle.TYPE]: GCCircle + +}; \ No newline at end of file diff --git a/web/app/sketcher/constr/polynomial.js b/web/app/sketcher/constr/polynomial.js new file mode 100644 index 00000000..70f66923 --- /dev/null +++ b/web/app/sketcher/constr/polynomial.js @@ -0,0 +1,348 @@ +import {eqEps} from "../../brep/geom/tolerance"; +import {sq} from "../../math/math"; + +export class Polynomial { + + monomials = []; + constant = 0; + + constructor(constant) { + this.constant = constant; + } + + term(p, fn) { + this.monomials[this.monomials.length - 1].addParam(p, fn); + return this; + } + + monomial(k = 1) { + this.monomials.push(new Monomial(k)); + return this; + } + + eliminate(param, value) { + for (let m of this.monomials) { + for (let i = 0; i < m.terms.length; ++i) { + if (m.terms[i].param === param) { + m.eliminate(i, value); + } + } + } + } + + substitute(param, toParam, dotConstant) { + for (let m of this.monomials) { + for (let i = 0; i < m.terms.length; ++i) { + if (m.terms[i].param === param) { + m.substitute(i, toParam, dotConstant); + } + } + } + } + + linerSub(param, toParam, b, c) { + // for (let m of this.monomials) { + // for (let i = 0; i < m.terms.length; ++i) { + // if (m.terms[i].param === param) { + // m.substitute(i, toParam, dotConstant); + // } + // } + // } + } + + + compact() { + for (let i = 0; i < this.monomials.length; ++i) { + const m1 = this.monomials[i]; + if (m1 === null) { + continue; + } + for (let j = i + 1; j < this.monomials.length; ++j) { + const m2 = this.monomials[j]; + if (m2 === null) { + continue; + } + + if (m1.equalVars(m2)) { + m1.constant += m2.constant; + this.monomials[j] = null; + } + } + if (eqEps(m1.constant, 0)) { + this.monomials[i] = null; + } else if (m1.terms.length === 0) { + this.constant += m1.constant; + this.monomials[i] = null; + } + } + this.monomials = this.monomials.filter(m => m !== null); + } + + get isLinear() { + for (let m of this.monomials) { + if (m.terms.length !== 1 || m.terms[0].fn.degree !== 1) { + return false; + } + } + return true; + } + + value(valueFn = GET_VALUE) { + let res = this.constant; + for (let m of this.monomials) { + res += m.value(valueFn); + } + return res; + } + + asResidual() { + + const paramsSet = new Set(); + + for (let m of this.monomials) { + for (let t of m.terms) { + paramsSet.add(t.param.solverParam); + } + } + + const params = Array.from(paramsSet); + + const solverValue = p => p.solverParam.get(); + + return { + + params, + + error: () => this.value(solverValue), + + gradient: out => { + + for (let i = 0; i < params.length; i++) { + + out[i] = 0; + + for (let m of this.monomials) { + out[i] += m.differentiate(params[i].objectParam, solverValue); + } + } + + }, + + }; + + + } + + toString() { + + return this.monomials.map(m => { + + let out = ''; + if (m.constant === 1) { + out += '+'; + } else if (m.constant === -1) { + out += '-'; + } else { + out += (m.constant >= 0 ? '+' : '') + m.constant.toFixed(2); + if (m.terms.length) { + out += '*'; + } + } + + out += m.terms.map(t => { + let out = 'X' + t.param.id; + if (t.fn.degree === 1) { + + } else if (t.fn.degree !== Infinity) { + out += t.fn.id; + } else { + out = t.fn.id + '(' + out + ')'; + } + return out; + }).join('*'); + + return out; + } + ).join(' ') + (this.constant >= 0 ? ' + ' : ' ') + this.constant.toFixed(2); + + } + +} + +export class Monomial { + + terms = []; + constant = 1; + + constructor(constant) { + this.constant = constant; + } + + addParam(param, fn) { + this.terms.push({param, fn}); + this.terms.sort(t => t.param.id); + } + + eliminate(i, value) { + const fn = this.terms[i].fn; + this.constant *= fn.apply(value); + this.terms.splice(i, 1); + } + + substitute(i, toParam, dotConstant) { + this.constant *= dotConstant; + let wasMerge = false; + for (let i = 0; i < this.terms.length; ++i) { + const merger = this.terms[i]; + if (!merger) { + continue; + } + for (let j = i + 1; j < this.terms.length; ++j) { + const term = this.terms[j]; + if (merger.param === term.param) { + let mergedFn = merger.fn.merge(term.fn); + if (mergedFn) { + merger.fn = mergedFn; + this.terms[j] = null; + wasMerge = true; + } + } + } + } + if (wasMerge) { + this.terms = this.terms.filter(t => t); + } + + } + + equalVars(other) { + if (this.terms.length !== other.terms.length) { + return false; + } + + for (let i = 0; i < this.terms.length; ++i) { + const t1 = this.terms[i]; + const t2 = other.terms[i]; + if (t1.fn.id !== t2.fn.id || t1.param.id !== t2.param.id) { + return false; + } + } + return true; + } + + differentiate(partialParam, valueFn = GET_VALUE) { + + let cnst = this.constant; + + let diffProduct = 0; + let freeProduct = 1; + + for (let term of this.terms) { + const pVal = valueFn(term.param); + const d0 = term.fn.apply(pVal); + if (partialParam === term.param) { + const d1 = term.fn.derivative1(pVal); + diffProduct = diffProduct*d0 + freeProduct * d1; + freeProduct *= d0; + } else { + cnst *= d0; + } + } + + return cnst * diffProduct; + } + + value(valueFn) { + let res = this.constant; + for (let t of this.terms) { + res *= t.fn.apply(valueFn(t.param)); + } + return res; + } +} + +export class ToThePowerFunction { + + degree = 1; + + static get(degree) { + switch (degree) { + case 0: return POW_0_FN; + case 1: return POW_1_FN; + case 2: return POW_2_FN; + case 3: return POW_3_FN; + case 4: return POW_4_FN; + default: return new ToThePowerFunction(degree, x => { + let val = 1; + for (let i = 0; i < degree; ++i) { + val *= x; + } + return val + }) + } + } + + constructor(degree, fn, d1) { + this.fn = fn; + this.d1 = d1; + this.id = '^' + degree; + } + + apply(x) { + return this.fn(x); + } + + merge(fn) { + if (fn.constructor.name === this.constructor.name) { + return ToThePowerFunction.get(fn.degree + this.degree); + } + return null; + } + + derivative1(x) { + return this.d1(x) + } + +} + + +export class FreeFunction { + + fn; + + constructor(fn, d1, id) { + this.fn = fn; + this.d1 = d1; + this.id = id; + } + + apply(x) { + return this.fn(x); + } + + get degree() { + return Infinity; + } + + merge(fn) { + return null; + } + + derivative1(x) { + return this.d1(x) + } +} + + +const GET_VALUE = param => param.get(); + +export const POW_0_FN = new ToThePowerFunction(0, x => 1, x => 0); +export const POW_1_FN = new ToThePowerFunction(1, x => x, x => 1); +export const POW_2_FN = new ToThePowerFunction(2, x => x*x, x => 2*x); +export const POW_3_FN = new ToThePowerFunction(3, x => x*x*x, x => 3*x*x); +export const POW_4_FN = new ToThePowerFunction(3, x => x*x*x*x, x => 4*x*x*x); + +export const COS_FN = new FreeFunction(x => Math.cos(x), x => -Math.sin(x), 'cos'); +export const SIN_FN = new FreeFunction(x => Math.sin(x), x => Math.cos(x), 'sin'); + + diff --git a/web/app/sketcher/constr/residuals.js b/web/app/sketcher/constr/residuals.js index c367c41b..caa3e888 100644 --- a/web/app/sketcher/constr/residuals.js +++ b/web/app/sketcher/constr/residuals.js @@ -1,6 +1,19 @@ -import {NumericGradient} from "./solverConstraints"; import {sq} from "../../math/math"; +export function R_Equal(params, [value]) { + + return { + + params, + + error: () => params[0].get() - value, + + gradient: out => { + out[0] = 1; + } + }; +} + export function R_TangentLC(params, [inverted]) { const ANG = 0; diff --git a/web/app/sketcher/constr/solver.js b/web/app/sketcher/constr/solver.js index 3cc3f45d..2849e4df 100644 --- a/web/app/sketcher/constr/solver.js +++ b/web/app/sketcher/constr/solver.js @@ -7,14 +7,15 @@ import {dog_leg} from '../../math/optim' import {newVector} from '../../math/vec'; /** @constructor */ -function Param(value) { +function Param(value, objectParam) { this.reset(value); - this.constant = false; - this.j = -1; + this.objectParam = objectParam; } Param.prototype.reset = function(value) { - this.value = value; + this.set(value); + this.constant = false; + this.j = -1; }; Param.prototype.set = function(value) { diff --git a/web/app/sketcher/generators/boundaryGenerator.js b/web/app/sketcher/generators/boundaryGenerator.js new file mode 100644 index 00000000..0e65753a --- /dev/null +++ b/web/app/sketcher/generators/boundaryGenerator.js @@ -0,0 +1,5 @@ + + +export function boundaryGenerator() { + +} \ No newline at end of file diff --git a/web/app/sketcher/io.js b/web/app/sketcher/io.js index 47502c69..8a854c70 100644 --- a/web/app/sketcher/io.js +++ b/web/app/sketcher/io.js @@ -16,9 +16,10 @@ import exportTextData from 'gems/exportTextData'; import NurbsCurve from '../brep/geom/curves/nurbsCurve'; import {NurbsObject} from './shapes/nurbsObject'; import {System} from './system'; +import {AlgNumConstraint} from "./constr/ANConstraints"; const Types = { - END_POINT : 'TCAD.TWO.EndPoint', + POINT : 'TCAD.TWO.EndPoint', SEGMENT : 'TCAD.TWO.Segment', ARC : 'TCAD.TWO.Arc', CIRCLE : 'TCAD.TWO.Circle', @@ -51,22 +52,22 @@ IO.prototype._loadSketch = function(sketch) { this.cleanUpData(); - var index = {}; + const index = {}; function endPoint(p) { - var id = p[0]; - var ep = index[id]; + const [id, [xref, x], [yref, y]] = p; + let ep = index[id]; if (ep !== undefined) { - return + return; } - ep = new EndPoint(p[1][1], p[2][1]); - index[p[1][0]] = ep._x; - index[p[2][0]] = ep._y; + ep = new EndPoint(x, y); + index[xref] = ep.params.x; + index[yref] = ep.params.y; index[id] = ep; return ep; } - var layerIdGen = 0; + let layerIdGen = 0; function getLayer(viewer, name) { if (name === undefined) { name = "layer_" + layerIdGen++; @@ -74,7 +75,7 @@ IO.prototype._loadSketch = function(sketch) { if (name === viewer.dimLayer.name) { return viewer.dimLayer; } - for (var i = 0; i < viewer.layers.length; ++i) { + for (let i = 0; i < viewer.layers.length; ++i) { if (name === viewer.layers[i].name) { return viewer.layers[i]; } @@ -84,6 +85,7 @@ IO.prototype._loadSketch = function(sketch) { viewer.layers.push(layer); return layer; } + const version = sketch.version || 1; var T = Types; var maxEdge = 0; var sketchLayers = sketch['layers']; @@ -93,7 +95,7 @@ IO.prototype._loadSketch = function(sketch) { for (var l = 0; l < sketchLayers.length; ++l) { var ioLayer = sketchLayers[l]; var layerName = ioLayer['name']; - var boundaryProcessing = layerName == IO.BOUNDARY_LAYER_NAME && boundaryNeedsUpdate; + var boundaryProcessing = layerName === IO.BOUNDARY_LAYER_NAME && boundaryNeedsUpdate; var layer = getLayer(this.viewer, layerName); if (!!ioLayer.style) layer.style = ioLayer.style; layer.readOnly = !!ioLayer.readOnly; @@ -111,28 +113,28 @@ IO.prototype._loadSketch = function(sketch) { } if (boundaryProcessing) { - if (_class === T.SEGMENT && boundary.lines.length == 0) continue; - else if (_class === T.ARC && boundary.arcs.length == 0) continue; - else if (_class === T.CIRCLE && boundary.circles.length == 0) continue; + if (_class === T.SEGMENT && boundary.lines.length === 0) continue; + else if (_class === T.ARC && boundary.arcs.length === 0) continue; + else if (_class === T.CIRCLE && boundary.circles.length === 0) continue; } if (_class === T.SEGMENT) { - const points = obj['points']; - const a = endPoint(points[0]); - const b = endPoint(points[1]); + const [aRef, bRef] = obj.points; + const a = endPoint(aRef); + const b = endPoint(bRef); skobj = new Segment(a, b); - } else if (_class === T.END_POINT) { - skobj = endPoint(obj['location']); + } else if (_class === T.POINT) { + skobj = endPoint(obj.location); } else if (_class === T.ARC) { - const points = obj['points']; + const points = obj.points; const a = endPoint(points[0]); const b = endPoint(points[1]); const c = endPoint(points[2]); skobj = new Arc(a, b, c); } else if (_class === T.CIRCLE) { - const c = endPoint(obj['c']); + const c = endPoint(obj.c); skobj = new Circle(c); - skobj.r.set(obj['r']); + skobj.r.set(obj.r); } else if (_class === T.ELLIPSE) { const ep1 = endPoint(obj['ep1']); const ep2 = endPoint(obj['ep2']); @@ -214,8 +216,12 @@ IO.prototype._loadSketch = function(sketch) { if (sketchConstraints !== undefined) { for (var i = 0; i < sketchConstraints.length; ++i) { try { - const c = this.parseConstr(sketchConstraints[i], index); - this.viewer.parametricManager._add(c); + if (version > 1) { + this.viewer.parametricManager.algnNumSystem.addConstraint(AlgNumConstraint.read(sketchConstraints[i], index)); + } else { + const c = this.parseConstr(sketchConstraints[i], index); + this.viewer.parametricManager._add(c); + } } catch (msg) { console.info("Skipping. " + msg); } @@ -232,7 +238,7 @@ IO.prototype.linkEndPoints = function(objects) { const index = HashTable.forVector2d(); for (let obj of objects) { obj.accept((o) => { - if (o._class == Types.END_POINT) { + if (o._class == Types.POINT) { const equalPoint = index.get(o); if (equalPoint == null) { index.put(o, o); @@ -336,9 +342,9 @@ IO.prototype.cleanUpData = function() { IO.prototype._serializeSketch = function(metadata) { var sketch = {}; //sketch.boundary = boundary; - sketch['layers'] = []; + sketch.layers = []; function point(p) { - return [ p.id, [p._x.id, p.x], [p._y.id, p.y] ]; + return [ p.id, [p.params.x.id, p.x], [p.params.y.id, p.y] ]; } var T = Types; var toSave = [this.viewer.dimLayers, this.viewer.layers]; @@ -346,76 +352,74 @@ IO.prototype._serializeSketch = function(metadata) { var layers = toSave[t]; for (var l = 0; l < layers.length; ++l) { var layer = layers[l]; - var toLayer = {'name' : layer.name, style : layer.style, readOnly: layer.readOnly, 'data' : []}; - sketch['layers'].push(toLayer); + var toLayer = {name : layer.name, style : layer.style, readOnly: layer.readOnly, data : []}; + sketch.layers.push(toLayer); for (var i = 0; i < layer.objects.length; ++i) { var obj = layer.objects[i]; - var to = {'id': obj.id, '_class': obj._class, role: obj.role}; + var to = {id: obj.id, _class: obj._class, role: obj.role}; if (obj.aux) to.aux = obj.aux; if (obj.edge !== undefined) to.edge = obj.edge; - toLayer['data'].push(to); + toLayer.data.push(to); if (obj._class === T.SEGMENT) { - to['points'] = [point(obj.a), point(obj.b)]; - } else if (obj._class === T.END_POINT) { - to['location'] = point(obj); + to.points = [point(obj.a), point(obj.b)]; + } else if (obj._class === T.POINT) { + to.location = point(obj); } else if (obj._class === T.ARC) { - to['points'] = [point(obj.a), point(obj.b), point(obj.c)]; + to.points = [point(obj.a), point(obj.b), point(obj.c)]; } else if (obj._class === T.CIRCLE) { - to['c'] = point(obj.c); - to['r'] = obj.r.get(); + to.c = point(obj.c); + to.r = obj.r.get(); } else if (obj._class === T.ELLIPSE) { - to['ep1'] = point(obj.ep1); - to['ep2'] = point(obj.ep2); - to['r'] = obj.r.get(); + to.ep1 = point(obj.ep1); + to.ep2 = point(obj.ep2); + to.r = obj.r.get(); } else if (obj._class === T.ELL_ARC) { - to['ep1'] = point(obj.ep1); - to['ep2'] = point(obj.ep2); - to['a'] = point(obj.a); - to['b'] = point(obj.b); - to['r'] = obj.r.get(); + to.ep1 = point(obj.ep1); + to.ep2 = point(obj.ep2); + to.a = point(obj.a); + to.b = point(obj.b); + to.r = obj.r.get(); } else if (obj._class === T.BEZIER) { - to['a'] = point(obj.a); - to['b'] = point(obj.b); - to['cp1'] = point(obj.cp1); - to['cp2'] = point(obj.cp2); + to.a = point(obj.a); + to.b = point(obj.b); + to.cp1 = point(obj.cp1); + to.cp2 = point(obj.cp2); } else if (obj._class === T.DIM || obj._class === T.HDIM || obj._class === T.VDIM) { - to['a'] = obj.a.id; - to['b'] = obj.b.id; - to['flip'] = obj.flip; + to.a = obj.a.id; + to.b = obj.b.id; + to.flip = obj.flip; } else if (obj._class === T.DDIM) { - to['obj'] = obj.obj.id; + to.obj = obj.obj.id; } const children = nonPointChildren(obj).map(c => c.id); - if (children.length != 0) { - to['children'] = children; + if (children.length !== 0) { + to.children = children; } } } } - var constrs = sketch['constraints'] = []; - var subSystems = this.viewer.parametricManager.system.subSystems; - for (var j = 0; j < subSystems.length; j++) { - var sub = subSystems[j]; - for (i = 0; i < sub.constraints.length; ++i) { - if (!sub.constraints[i].aux) { - constrs.push(this.serializeConstr(sub.constraints[i])); - } + sketch.constraints = []; + const systemConstraints = this.viewer.parametricManager.algnNumSystem.constraints; + for (let sc of systemConstraints) { + if (!sc.internal) { + sketch.constraints.push(sc.write()); } } var constantDefinition = this.viewer.params.constantDefinition; if (constantDefinition !== undefined && constantDefinition != null && !/^\s*$/.test(constantDefinition)) { - sketch['constants'] = constantDefinition; + sketch.constants = constantDefinition; } sketch.metadata = metadata; + sketch.version = 2; return sketch; }; function nonPointChildren(obj){ const children = []; obj.accept((o) => { - if (o._class !== Types.END_POINT) { + if (o._class !== Types.POINT) { children.push(o); } return true; @@ -440,9 +444,6 @@ IO.prototype.parseConstr = function (c, index) { return constrCreate(find, ps); }; -IO.prototype.serializeConstr = function (c) { - return c.serialize(); -}; function _format(str, args) { if (args.length == 0) return str; @@ -499,7 +500,7 @@ function BBox() { if (obj._class === T.SEGMENT) { this.checkBounds(obj.a.x, obj.a.y); this.checkBounds(obj.b.x, obj.b.y); - } else if (obj._class === T.END_POINT) { + } else if (obj._class === T.POINT) { this.checkBounds(obj.x, obj.y); } else if (obj._class === T.ARC) { this.checkCircBounds(obj.c.x, obj.c.y, obj.r.get()); @@ -509,7 +510,7 @@ function BBox() { this.checkCircBounds(obj.centerX, obj.centerY, Math.max(obj.radiusX, obj.radiusY)); } else if (obj) { obj.accept((o) => { - if (o._class == T.END_POINT) { + if (o._class == T.POINT) { this.checkBounds(o.x, o.y); } return true; @@ -589,7 +590,7 @@ IO.prototype.svgExport = function () { out.fline('', [layer.name, "none", color, '2']); for (var i = 0; i < layer.objects.length; ++i) { var obj = layer.objects[i]; - if (obj._class !== T.END_POINT) bbox.check(obj); + if (obj._class !== T.POINT) bbox.check(obj); if (obj._class === T.SEGMENT) { out.fline('', [obj.a.x, obj.a.y, obj.b.x, obj.b.y]); } else if (obj._class === T.ARC) { @@ -686,7 +687,7 @@ IO.prototype.dxfExport = function () { var layer = toExport[l]; for (i = 0; i < layer.objects.length; ++i) { var obj = layer.objects[i]; - if (obj._class === T.END_POINT) { + if (obj._class === T.POINT) { out.line("0"); out.line("POINT"); out.line("8"); diff --git a/web/app/sketcher/parametric.js b/web/app/sketcher/parametric.js index d218868b..687fa030 100644 --- a/web/app/sketcher/parametric.js +++ b/web/app/sketcher/parametric.js @@ -14,7 +14,7 @@ class ParametricManager { constructor(viewer) { this.viewer = viewer; this.system = new System(); - this.seacSystem = new SEACSystem(); + this.algnNumSystem = new AlgNumSubSystem(); this.constantTable = {}; this.viewer.params.define('constantDefinition', null); @@ -23,55 +23,76 @@ class ParametricManager { this.externalConstantResolver = null; this.messageSink = msg => alert(msg); - setTimeout(() => { - - let s1 = viewer.addSegment(100, 100, 300, 300, viewer.activeLayer); - let s2 = viewer.addSegment(200, 100, 400, 300, viewer.activeLayer); - let c1 = new Circle(new EndPoint(500, 500)); - c1.r.set(500); - viewer.add(c1, viewer.activeLayer); - let c2 = new Circle(new EndPoint(0, 0)); - c2.r.set(50); - viewer.add(c2, viewer.activeLayer); - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s1, c1], { - inverted: false - })); - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s2, c1], { - inverted: true - })); - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s1, c2], { - inverted: false - })); - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s2, c2], { - inverted: true - })); - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.PointOnLine, [s1.a, s1])); - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.PointOnLine, [s1.b, s1])); - - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.PointOnLine, [s2.a, s2])); - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.PointOnLine, [s2.b, s2])); - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.DistancePP, [s1.a, s1.b], { - distance: 350 - })); - - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.DistancePP, [s2.a, s2.b], { - distance: 500 - })); - - this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.DistancePP, [c1.c, c2.c], { - distance: 700 - })); - - - this.refresh(); - - }); + // setTimeout(() => { + // + // let s1 = viewer.addSegment(-100, 0, -100, 600, viewer.activeLayer); + // let s2 = viewer.addSegment(700, 0, 700, 600, viewer.activeLayer); + // + // let s3 = viewer.addSegment(0, -100, 600, -100, viewer.activeLayer); + // let s4 = viewer.addSegment(0, 700, 600, 700, viewer.activeLayer); + // + // const newCircle = (cx, cy) => { + // let c1 = new Circle(new EndPoint(cx, cy)); + // c1.r.set(100); + // viewer.add(c1, viewer.activeLayer); + // return c1; + // }; + // + // const c1 = newCircle(600, 600); + // + // let c2 = newCircle(0, 0); + // + // + // let c3 = newCircle(600, 0); + // let c4 = newCircle(0, 600); + // + // // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s1, c4], { + // // inverted: true + // // })); + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s1, c2], { + // // inverted: true + // // })); + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s2, c1], { + // // inverted: false + // // })); + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s2, c3], { + // // inverted: false + // // })); + // // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s4, c4], { + // // inverted: true + // // })); + // // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s4, c1], { + // // inverted: true + // // })); + // // + // // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s3, c2], { + // // inverted: false + // // })); + // // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.TangentLC, [s3, c3], { + // // inverted: false + // // })); + // + // + // + // // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.DistancePP, [s1.a, s1.b], { + // // distance: 350 + // // })); + // + // + // // this.seacSystem.addConstraint(new SEACConstraint(ConstraintDefinitions.DistancePP, [s2.a, s2.b], { + // // distance: 500 + // // })); + // + // + // this.refresh(); + // + // }); } @@ -79,8 +100,8 @@ class ParametricManager { return this.system.subSystems; } - addSEAC(constr) { - this.seacSystem.addConstraint(constr); + addAlgNum(constr) { + this.algnNumSystem.addConstraint(constr); this.refresh(); } @@ -336,7 +357,14 @@ ParametricManager.prototype.tangent = function(objs) { this.add(new Constraints.CurveTangent(lines[0], curves[0])); } else { const arcs = fetch.generic(objs, ['TCAD.TWO.Arc', 'TCAD.TWO.Circle'], 1); - this.addSEAC(new TangentLC(lines[0], arcs[0], falses)); + + this.addAlgNum( + new AlgNumConstraint(ConstraintDefinitions.TangentLC, [lines[0], arcs[0]], { + inverted: false + } + ) + ); + } }; @@ -537,10 +565,21 @@ ParametricManager.prototype.findCoincidentConstraint = function(point1, point2) }; ParametricManager.prototype.coincident = function(objs) { - if (objs.length == 0) return; - this.linkObjects(objs); - this.solve(); - this.viewer.refresh(); + + const [first, ...others] = objs; + + for (let obj of others) { + this.algnNumSystem.addConstraint( + new AlgNumConstraint(ConstraintDefinitions.PCoincident, [first, obj]) + ); + } + + this.refresh(); + + // if (objs.length === 0) return; + // this.linkObjects(objs); + // this.solve(); + // this.viewer.refresh(); }; ParametricManager.prototype.getSolveData = function() { @@ -563,9 +602,9 @@ ParametricManager.prototype.__getSolveData = function(constraints, out) { return out; }; -ParametricManager.prototype.solve = function(lock, extraConstraints, disabledObjects) { - const solver = this.prepare(lock, extraConstraints, disabledObjects); - solver.solve(false); +ParametricManager.prototype.solve = function() { + this.algnNumSystem.prepare(); + this.algnNumSystem.solve(false); }; ParametricManager.prototype.prepare = function(locked, extraConstraints, disabledObjects) { @@ -949,8 +988,8 @@ ParametricManager.prototype.updateConstraintConstants = function(constr) { import {Constraints} from './constraints'; import {askNumber} from '../utils/utils'; -import {SEACSystem} from "./constr/SEACSystem"; -import {ConstraintDefinitions, SEACConstraint, TangentLC} from "./constr/SEACConstraints"; +import {ConstraintDefinitions, AlgNumConstraint, TangentLC} from "./constr/ANConstraints"; import {Circle} from "./shapes/circle"; import {EndPoint} from "./shapes/point"; +import {AlgNumSubSystem} from "./constr/AlgNumSystem"; export {Constraints, ParametricManager} \ No newline at end of file diff --git a/web/app/sketcher/selectionMatcher.js b/web/app/sketcher/selectionMatcher.js new file mode 100644 index 00000000..f969228e --- /dev/null +++ b/web/app/sketcher/selectionMatcher.js @@ -0,0 +1,26 @@ +export function SelectionMatcher() { + + + const lowerBounds = []; + + return { + + + moreThan(amount, type) { + lowerBounds.push(amount, type); + return this; + }, + + + match: selection => { + + + for (let [amount, type] of lowerBounds) { + + } + } + } + + + +} \ No newline at end of file diff --git a/web/app/sketcher/shapes/arc.js b/web/app/sketcher/shapes/arc.js index c0febaca..ad164a7d 100644 --- a/web/app/sketcher/shapes/arc.js +++ b/web/app/sketcher/shapes/arc.js @@ -4,6 +4,7 @@ import Vector from 'math/vector'; import {Ref} from './ref' import {Constraints} from '../parametric' import {pointIterator, SketchObject} from './sketch-object'; +import {EndPoint} from "./point"; export class Arc extends SketchObject { @@ -140,3 +141,4 @@ export class Arc extends SketchObject { } Arc.prototype._class = 'TCAD.TWO.Arc'; +Arc.prototype.TYPE = 'ARC'; \ No newline at end of file diff --git a/web/app/sketcher/shapes/bezier-curve.js b/web/app/sketcher/shapes/bezier-curve.js index 0bf9378b..b0a73e4f 100644 --- a/web/app/sketcher/shapes/bezier-curve.js +++ b/web/app/sketcher/shapes/bezier-curve.js @@ -6,6 +6,8 @@ import {ConvexHull2D} from '../../math/convex-hull' import * as draw_utils from '../shapes/draw-utils' import * as math from '../../math/math'; +import {Arc} from "./arc"; +import {EndPoint} from "./point"; export class BezierCurve extends SketchObject { @@ -77,5 +79,6 @@ export class BezierCurve extends SketchObject { } } BezierCurve.prototype._class = 'TCAD.TWO.BezierCurve'; +BezierCurve.prototype.TYPE = 'BEZIER'; const RECOVER_LENGTH = 100; \ No newline at end of file diff --git a/web/app/sketcher/shapes/circle.js b/web/app/sketcher/shapes/circle.js index 2ce309ee..a5afd60f 100644 --- a/web/app/sketcher/shapes/circle.js +++ b/web/app/sketcher/shapes/circle.js @@ -5,18 +5,16 @@ import {EditCircleTool} from '../tools/circle' import {EndPoint} from './point' import {Ref} from './ref' import {SketchObject} from './sketch-object' -import {GCCircle} from "../constr/constractibles"; export class Circle extends SketchObject { - + constructor(c) { super(); - this.gcCircle = new GCCircle(); this.c = c; c.parent = this; this.children.push(c); - this.r = this.gcCircle.r; - this.c.coincideWith(this.gcCircle.c) + this.r = new Ref(0); + this.r.obj = this; } visitParams(callback) { @@ -34,7 +32,7 @@ export class Circle extends SketchObject { drawImpl(ctx, scale) { ctx.beginPath(); - ctx.arc(this.gcCircle.c.x.get(), this.gcCircle.c.y.get(), this.gcCircle.r.get(), 0, 2 * Math.PI); + ctx.arc(this.c.x, this.c.y, this.r.get(), 0, 2 * Math.PI); ctx.stroke(); } @@ -44,3 +42,4 @@ export class Circle extends SketchObject { } Circle.prototype._class = 'TCAD.TWO.Circle'; +Circle.prototype.TYPE = 'CIRCLE'; \ No newline at end of file diff --git a/web/app/sketcher/shapes/ellipse.js b/web/app/sketcher/shapes/ellipse.js index ccd0b78e..e2530411 100644 --- a/web/app/sketcher/shapes/ellipse.js +++ b/web/app/sketcher/shapes/ellipse.js @@ -3,6 +3,8 @@ import {SketchObject} from './sketch-object' import {Constraints} from '../parametric' import * as math from '../../math/math'; +import {Circle} from "./circle"; +import {EndPoint} from "./point"; export class Ellipse extends SketchObject { @@ -91,6 +93,7 @@ export class Ellipse extends SketchObject { } } Ellipse.prototype._class = 'TCAD.TWO.Ellipse'; +Ellipse.prototype.TYPE = 'ELLIPSE'; const sq = (a) => a * a; const RECOVER_LENGTH = 100; \ No newline at end of file diff --git a/web/app/sketcher/shapes/elliptical-arc.js b/web/app/sketcher/shapes/elliptical-arc.js index 0114bd32..8da16714 100644 --- a/web/app/sketcher/shapes/elliptical-arc.js +++ b/web/app/sketcher/shapes/elliptical-arc.js @@ -52,3 +52,4 @@ export class EllipticalArc extends Ellipse { } EllipticalArc.prototype._class = 'TCAD.TWO.EllipticalArc'; +EllipticalArc.prototype.TYPE = 'ELLIPTICAL_ARC'; diff --git a/web/app/sketcher/shapes/nurbsObject.js b/web/app/sketcher/shapes/nurbsObject.js index f225e67f..ad24ee69 100644 --- a/web/app/sketcher/shapes/nurbsObject.js +++ b/web/app/sketcher/shapes/nurbsObject.js @@ -1,6 +1,7 @@ import {SketchObject} from './sketch-object' import * as vec from '../../math/vec'; import {curveTessellate} from '../../brep/geom/impl/nurbs-ext'; +import {Ellipse} from "./ellipse"; const __v = [0, 0, 0]; @@ -89,3 +90,4 @@ export class NurbsObject extends SketchObject { } NurbsObject.prototype._class = 'TCAD.TWO.NurbsObject'; +NurbsObject.prototype.TYPE = 'NURBS'; diff --git a/web/app/sketcher/shapes/param.js b/web/app/sketcher/shapes/param.js new file mode 100644 index 00000000..5727cb50 --- /dev/null +++ b/web/app/sketcher/shapes/param.js @@ -0,0 +1,20 @@ +import {Generator} from "../id-generator"; +import {Param as SolverParam} from '../constr/solver'; + +export class Param { + + constructor(value) { + this.id = Generator.genID(); + this.value = value; + this.solverParam = new SolverParam(value, this); + } + + set(value) { + this.value = value; + } + + get() { + return this.value; + } + +} \ No newline at end of file diff --git a/web/app/sketcher/shapes/point.js b/web/app/sketcher/shapes/point.js index e7eb3b9d..761a2b02 100644 --- a/web/app/sketcher/shapes/point.js +++ b/web/app/sketcher/shapes/point.js @@ -2,46 +2,39 @@ import {SketchObject} from './sketch-object' import {DrawPoint} from './draw-utils' import {Generator} from '../id-generator' import Vector from 'math/vector'; -import {GCPoint} from "../constr/constractibles"; +import {Param} from "./param"; + export class EndPoint extends SketchObject { constructor(x, y) { super(); this.parent = null; - this.gcPoint = new GCPoint(); - this._x = this.gcPoint.x; // legacy - yet to remove - this._y = this.gcPoint.y; - this.x = x; - this.y = y; + this.params = { + x: new Param(x), + y: new Param(y) + }; } get x() { - return this.gcPoint.x.get(); + return this.params.x.get(); } set x(val) { - return this.gcPoint.x.set(val); + return this.params.x.set(val); } get y() { - return this.gcPoint.y.get(); + return this.params.y.get(); } set y(val) { - return this.gcPoint.y.set(val); - } - - coincideWith(gcPoint) { - if (!this.parkedOwnGeometry) { - this.parkedOwnGeometry = this.gcPoint; - } - this.gcPoint = gcPoint; + return this.params.y.set(val); } visitParams(callback) { - callback(this._x); - callback(this._y); + callback(this.params.x); + callback(this.params.y); } normalDistance(aim) { @@ -88,20 +81,7 @@ export class EndPoint extends SketchObject { dest.y = y; } } + EndPoint.prototype._class = 'TCAD.TWO.EndPoint'; +EndPoint.prototype.TYPE = 'POINT'; -export class Param { - constructor(obj, prop) { - this.id = Generator.genID(); - this.obj = obj; - this.prop = prop; - } - - set(value) { - this.obj[this.prop] = value; - } - - get() { - return this.obj[this.prop]; - } -} diff --git a/web/app/sketcher/shapes/ref.js b/web/app/sketcher/shapes/ref.js index f580f3a4..a6feeff8 100644 --- a/web/app/sketcher/shapes/ref.js +++ b/web/app/sketcher/shapes/ref.js @@ -1,14 +1,3 @@ -import {Generator} from '../id-generator' +import {Param} from "./param"; -export function Ref(value) { - this.id = Generator.genID(); - this.value = value; -} - -Ref.prototype.set = function(value) { - this.value = value; -}; - -Ref.prototype.get = function() { - return this.value; -}; +export {Param as Ref}; diff --git a/web/app/sketcher/shapes/segment.js b/web/app/sketcher/shapes/segment.js index c8f169e7..f0b879e8 100644 --- a/web/app/sketcher/shapes/segment.js +++ b/web/app/sketcher/shapes/segment.js @@ -1,10 +1,12 @@ import {SketchObject} from './sketch-object' import Vector from 'math/vector'; -import {Constraints} from '../parametric' import * as math from '../../math/math' -import {GCLine} from "../constr/constractibles"; import {Styles} from "../styles"; import * as draw_utils from "./draw-utils"; +import {Param} from "./param"; +import {Constraints} from "../constraints"; +import {ConstraintDefinitions, AlgNumConstraint} from "../constr/ANConstraints"; +import {Ellipse} from "./ellipse"; export class Segment extends SketchObject { @@ -14,23 +16,36 @@ export class Segment extends SketchObject { this.b = b; a.parent = this; b.parent = this; - this.gcLine = new GCLine(); + this.children.push(a, b); + this.params = { + ang: new Param(undefined), + w: new Param(undefined) + } + } - - const dx = b.x - a.x; - const dy = b.y - a.y; + syncLine() { + const dx = this.b.x - this.a.x; + const dy = this.b.y - this.a.y; const l = Math.sqrt(dx*dx + dy*dy); - let nx = - dy / l; - let ny = dx / l; + let nx = (- dy / l) || 0; + let ny = (dx / l) || 0; const ang = Math.atan2(ny, nx); - this.gcLine.ang.set(ang); - this.gcLine.w.set(nx * a.x + ny * a.y); - - this.children.push(a, b); + this.params.ang.set(ang||0); + this.params.w.set(nx * this.a.x + ny * this.a.y); } - + + stabilize(viewer) { + this.syncLine(); + const c1 = new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [this.a, this]); + const c2 = new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [this.b, this]); + c1.internal = true; + c2.internal = true; + viewer.parametricManager.addAlgNum(c1); + viewer.parametricManager.addAlgNum(c2); + } + recoverIfNecessary() { if (math.distanceAB(this.a, this.b) > math.TOLERANCE) { return false; @@ -45,6 +60,8 @@ export class Segment extends SketchObject { visitParams(callback) { this.a.visitParams(callback); this.b.visitParams(callback); + callback(this.params.ang); + callback(this.params.w); } normalDistance(aim) { @@ -89,10 +106,10 @@ export class Segment extends SketchObject { // ctx.restore(); - let ang = this.gcLine.ang.get(); + let ang = this.params.ang.get(); let nx = Math.cos(ang) ; let ny = Math.sin(ang) ; - let w = this.gcLine.w.get(); + let w = this.params.w.get(); ctx.save(); draw_utils.SetStyle(Styles.CONSTRUCTION_OF_OBJECT, ctx, scale ); @@ -122,3 +139,4 @@ export class Segment extends SketchObject { Segment.prototype._class = 'TCAD.TWO.Segment'; +Segment.prototype.TYPE = 'SEGMENT'; diff --git a/web/app/sketcher/shapes/sketch-object.js b/web/app/sketcher/shapes/sketch-object.js index 4aacabff..88f50ebd 100644 --- a/web/app/sketcher/shapes/sketch-object.js +++ b/web/app/sketcher/shapes/sketch-object.js @@ -131,7 +131,7 @@ export class SketchObject extends Shape { export function pointIterator(shape, func) { shape.accept(o => { - if (o._class === Types.END_POINT) { + if (o._class === Types.POINT) { func(o); } return true; diff --git a/web/app/sketcher/sketcher-app.js b/web/app/sketcher/sketcher-app.js index c7ac96c9..b9b669a0 100644 --- a/web/app/sketcher/sketcher-app.js +++ b/web/app/sketcher/sketcher-app.js @@ -1,8 +1,8 @@ import {Viewer} from './viewer2d.js' import * as ui from '../ui/ui' import {Terminal} from '../ui/terminal' -import {IO, BBox} from './io' -import {AddFreeDimTool, AddHorizontalDimTool, AddVerticalDimTool, AddCircleDimTool} from './tools/dim' +import {BBox, IO} from './io' +import {AddCircleDimTool, AddFreeDimTool, AddHorizontalDimTool, AddVerticalDimTool} from './tools/dim' import {AddPointTool} from './tools/point' import {AddSegmentTool} from './tools/segment' import {AddArcTool} from './tools/arc' @@ -15,6 +15,10 @@ import {OffsetTool} from './tools/offset' import {ReferencePointTool} from './tools/origin' import {InputManager} from './input-manager' import genSerpinski from '../utils/genSerpinski'; +import context from 'context'; +import ReactDOM from "react-dom"; +import React from "react"; +import {RightSideControls} from "./components/RightSideControls"; function App2D() { var app = this; @@ -54,8 +58,9 @@ function App2D() { this.terminalHandler = undefined; this.terminal = new Terminal(this.commandsWin, (command) => this.handleTerminalInput(command), () => this.getAllCommandList()); this.bindToolsToTerminal(); - - + + startReact(this.viewer); + this.winManager.registerResize(dockEl, ui.DIRECTIONS.EAST, function() {$('body').trigger('layout'); }); $('body').on('layout', this.viewer.onWindowResize); @@ -470,6 +475,20 @@ App2D.prototype.handleTerminalInput = function(commandStr) { } }; +function startReact(viewer) { + context.streams.sketcherApp = viewer.streams; + context.viewer = viewer; + let reactControls = document.getElementById('react-controls'); + reactControls.onkeydown = e => { + e.stopPropagation(); + // e.preventDefault(); + }; + ReactDOM.render( + , + reactControls + ); +} + App2D.STORAGE_PREFIX = "TCAD.projects."; export default App2D; \ No newline at end of file diff --git a/web/app/sketcher/sketcherStreams.js b/web/app/sketcher/sketcherStreams.js index b582e1d2..f2797f6e 100644 --- a/web/app/sketcher/sketcherStreams.js +++ b/web/app/sketcher/sketcherStreams.js @@ -17,5 +17,7 @@ export default function(viewer) { streams.constraintsUpdate = stream(); + streams.constraintEditRequest = state(null); + return streams; }; \ No newline at end of file diff --git a/web/app/sketcher/system.js b/web/app/sketcher/system.js index 6ea09bb8..cbac90c8 100644 --- a/web/app/sketcher/system.js +++ b/web/app/sketcher/system.js @@ -25,6 +25,10 @@ class SubSystem { }); other.nativeParams.forEach(p => this.nativeParams.add(p)); } + + prepare() { + + } } class Index { diff --git a/web/app/sketcher/tools/drag.js b/web/app/sketcher/tools/drag.js index 85e5476c..c8cef0b4 100644 --- a/web/app/sketcher/tools/drag.js +++ b/web/app/sketcher/tools/drag.js @@ -25,7 +25,7 @@ export class DragTool extends Tool { this.obj.translate(dx, dy); if (!Tool.dumbMode(e)) { - this.solverTransaction.solve(true); + this.viewer.parametricManager.algnNumSystem.solve(true); } this.viewer.refresh(); } @@ -35,12 +35,12 @@ export class DragTool extends Tool { this.origin.y = e.offsetY; this.viewer.screenToModel2(e.offsetX, e.offsetY, this._point); - this.solverTransaction = this.viewer.parametricManager.seacSystem.startTransaction([this.obj.gcPoint]); + this.viewer.parametricManager.algnNumSystem.prepare(); } mouseup(e) { - this.solverTransaction.solve(false); + this.viewer.parametricManager.algnNumSystem.solve(false); this.viewer.refresh(); this.viewer.toolManager.releaseControl(); var traveled = math.distance(this.origin.x, this.origin.y, e.offsetX, e.offsetY); diff --git a/web/app/sketcher/tools/segment.js b/web/app/sketcher/tools/segment.js index da872918..ca55ca77 100644 --- a/web/app/sketcher/tools/segment.js +++ b/web/app/sketcher/tools/segment.js @@ -1,6 +1,4 @@ import {Tool} from './tool' -import {Constraints} from "../constraints"; -import {distanceAB} from "../../math/math"; export class AddSegmentTool extends Tool { @@ -45,7 +43,7 @@ export class AddSegmentTool extends Tool { } this.line = this.viewer.addSegment(a.x, a.y, b.x, b.y, this.viewer.activeLayer); if (needSnap) { - this.viewer.parametricManager.linkObjects([this.line.a, a]); + this.line.a.coincideWith(a); } this.firstPointPicked(); this.viewer.refresh(); @@ -55,7 +53,7 @@ export class AddSegmentTool extends Tool { this.viewer.cleanSnap(); this.line.b.x = p.x; this.line.b.y = p.y; - this.viewer.parametricManager.linkObjects([this.line.b, p]); + this.line.b.coincideWith(p); } this.nextPointPicked(); } @@ -63,11 +61,10 @@ export class AddSegmentTool extends Tool { nextPointPicked() { this.pointPicked(this.line.b.x, this.line.b.y); - this.viewer.parametricManager.add(new Constraints.P2PDistance(this.line.a, this.line.b, distanceAB(this.line.a, this.line.b))); if (this.multi) { const b = this.line.b; this.line = this.viewer.addSegment(b.x, b.y, b.x, b.y, this.viewer.activeLayer); - this.viewer.parametricManager.linkObjects([this.line.a, b]); + this.line.a.coincideWith(b); } else { this.restart() } @@ -87,7 +84,7 @@ export class AddSegmentTool extends Tool { } keydown(e) { - if (e.keyCode == 27) { + if (e.keyCode === 27) { this.cancelSegment(); } } diff --git a/web/app/sketcher/viewer2d.js b/web/app/sketcher/viewer2d.js index d4c094b5..bb8047c7 100644 --- a/web/app/sketcher/viewer2d.js +++ b/web/app/sketcher/viewer2d.js @@ -121,6 +121,7 @@ class Viewer { var b = new EndPoint(x2, y2); var line = new Segment(a, b); layer.add(line); + line.stabilize(this); return line; }; @@ -329,7 +330,7 @@ class Viewer { return out; }; - accept(visitor) { + accept = visitor => { for (let layer of this.layers) { for (let object of layer.objects) { if (!object.accept(visitor)) { diff --git a/web/css/app.less b/web/css/app.less index f15dd4a8..a4369bb8 100644 --- a/web/css/app.less +++ b/web/css/app.less @@ -346,4 +346,4 @@ input[type=checkbox], input[type=radio] { font-size: 11px; cursor: default; pointer-events: none; -} \ No newline at end of file +} diff --git a/web/sketcher.html b/web/sketcher.html index eba6ea7d..a996d478 100644 --- a/web/sketcher.html +++ b/web/sketcher.html @@ -70,6 +70,7 @@
+