Compare commits
875 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d0117f701 | |||
| 08fe6f07cf | |||
| 37be889932 | |||
| ed124dd288 | |||
| 760d8a033d | |||
| 96cd122645 | |||
| 533768eb14 | |||
| c88d3c632d | |||
| 72f5185992 | |||
| b8db07a66b | |||
| 366b961039 | |||
| e244a7e2e5 | |||
| 9b55764b38 | |||
| 4b8012f94b | |||
| 9871000d84 | |||
| 17a056b3b9 | |||
| a8c5bd573f | |||
| 1d2fe047bf | |||
| 3465f86605 | |||
| 540e727b73 | |||
| 8c576f820c | |||
| df3b673064 | |||
| 69bde5539c | |||
| aa12b7b11b | |||
| e0b3b3cca2 | |||
| ea9a5741d7 | |||
| 1fe1c8d5a8 | |||
| ac58560a69 | |||
| d0b234bf3d | |||
| 981fe05c15 | |||
| 918550465b | |||
| 963d4f1738 | |||
| d809732280 | |||
| c4e38a11b7 | |||
| 7abe215bc2 | |||
| 0f269430bc | |||
| b8a298c54b | |||
| e1d33cd250 | |||
| 4563dec9bc | |||
| de284083fd | |||
| 85ee66ca4d | |||
| 5365dc858e | |||
| 69f9544638 | |||
| bf5409cf90 | |||
| bd4f668526 | |||
| d21df0c6fd | |||
| d808fdaa09 | |||
| b32d23b9d6 | |||
| d1b8ba95df | |||
| 5e6825bbdb | |||
| 3e5ba136be | |||
| 294b49a11c | |||
| 9e0cca447f | |||
| bc3f23d8dd | |||
| 4fcf11eaa8 | |||
| 1f45902837 | |||
| bc55543e23 | |||
| c1b8ec7036 | |||
| abe9de1b4a | |||
| 09cc83eb14 | |||
| 8f7b3fce0c | |||
| f121f1adfe | |||
| 522ace443a | |||
| 8f97f59a9a | |||
| 8e9d98cd55 | |||
| 746230caa0 | |||
| 03e80c6361 | |||
| bc547c13db | |||
| 173df6128e | |||
| 666d882ec6 | |||
| 6ea13c4edc | |||
| 39bdff1c02 | |||
| 45fc851722 | |||
| ede81f3a7d | |||
| bae9f30445 | |||
| 527cefa3ef | |||
| 830aee116e | |||
| 243fbea769 | |||
| 9c885258d9 | |||
| 0ad4e15f0a | |||
| a2ba6823a3 | |||
| 3713abd860 | |||
| 5ae78fb192 | |||
| 0b694833fb | |||
| dfd82e6d40 | |||
| 1694786177 | |||
| ec72cc1454 | |||
| 37ba4d9a80 | |||
| 526e497037 | |||
| a81a723ba8 | |||
| 5976898f07 | |||
| 09fec26841 | |||
| 165eb238b5 | |||
| de305a3508 | |||
| d124478a92 | |||
| d9acc5b39d | |||
| 5b91a04e1e | |||
| d145ce52f7 | |||
| ec598bf4a8 | |||
| 568e679e3f | |||
| 85cc49f4d9 | |||
| 65b07d44ab | |||
| f87bc98169 | |||
| 04270a8abc | |||
| 65fa47c9ac | |||
| 9367ea7e17 | |||
| b7ac9d1fee | |||
| f20b26045c | |||
| 4bcbc5131b | |||
| eeea2d7c8d | |||
| 68e9468294 | |||
| 694da73b77 | |||
| 15657bf0ec | |||
| 78c6898ff0 | |||
| 836e97a57c | |||
| 7f708108f2 | |||
| 2d3e282150 | |||
| 75b933ef0a | |||
| c19799f081 | |||
| 848ed922b5 | |||
| 16e96b69a4 | |||
| fbdd7ee7b6 | |||
| d3df5fceec | |||
| 948925d156 | |||
| 7ab6b1b167 | |||
| ca3a109bc8 | |||
| ebe9551e72 | |||
| d2836189c3 | |||
| 3703963ec8 | |||
| 02ca8e3a59 | |||
| 7c73658fa5 | |||
| e03b448618 | |||
| c8d635752b | |||
| df9a5600d9 | |||
| dd730ea517 | |||
| 5bbfe1be59 | |||
| dd04bd8792 | |||
| 20bc1f41f0 | |||
| 9e838734c0 | |||
| 17aeca5e70 | |||
| 98852d8c22 | |||
| 674d9cd37d | |||
| c35f9c22ae | |||
| e5f861c0dc | |||
| 7a31143ef4 | |||
| 9daa0e4059 | |||
| eaedeb4f8b | |||
| bd2f1d2777 | |||
| 52e43bac2d | |||
| daef503970 | |||
| 4d0ba3cd13 | |||
| bd111a2106 | |||
| 22daade5d2 | |||
| a39d72a59d | |||
| 3a2e523589 | |||
| 8bd27c6ad1 | |||
| 4c20cf67c8 | |||
| 337da3d523 | |||
| 3050803e68 | |||
| beb803fc63 | |||
| b056bcae2c | |||
| 6a2c297640 | |||
| 8b3127eda4 | |||
| b63d51ab1d | |||
| 3b2dfd27d6 | |||
| b8753195df | |||
| e76eb113a5 | |||
| d285e10565 | |||
| 66803f9036 | |||
| d7cff48374 | |||
| d66ed0566b | |||
| fde2b3bf0f | |||
| dcc8270cc6 | |||
| 23b6ca7552 | |||
| 437173e6a4 | |||
| 1274facbed | |||
| 180fc2ca59 | |||
| 187cf8f55e | |||
| dd8868b2a2 | |||
| 921e8ea1a8 | |||
| 54e8782e12 | |||
| 9e690dd304 | |||
| 373b42041b | |||
| 30ead47fb9 | |||
| 2dc7206f17 | |||
| ab7594bd23 | |||
| 710a22aea7 | |||
| 2c7c801efc | |||
| 41ebe058e7 | |||
| 288c71b3e0 | |||
| d7a8afcb31 | |||
| a1d120ee22 | |||
| b58d6d0d82 | |||
| 5ab65391ca | |||
| 2b40d58996 | |||
| 96129388ec | |||
| 6f6058a2d1 | |||
| 063ef91613 | |||
| 5233072099 | |||
| cd063b0e8a | |||
| 719d796e20 | |||
| 9767bf5261 | |||
| a9998bf12d | |||
| 2b7dc899e1 | |||
| cbc3f51d4c | |||
| ff1c8c1bfe | |||
| 6d4222bb1a | |||
| 7692a6f258 | |||
| ed8e6b00c6 | |||
| 85db9570dd | |||
| be174b9cce | |||
| f0099090b4 | |||
| b72bdfcbf0 | |||
| e1f5addddc | |||
| 87d3145317 | |||
| 84fc89cbbe | |||
| fbbddd8365 | |||
| dcfb787563 | |||
| 6822b2db9b | |||
| d89b3772de | |||
| e4a4eb0455 | |||
| 759efa7da3 | |||
| bf1c271784 | |||
| 9754893181 | |||
| 5b05182a1c | |||
| 20528f72df | |||
| 7de08d9f24 | |||
| d047f5e6d2 | |||
| 71071f817c | |||
| ad9af9bb3d | |||
| c7c864b8c0 | |||
| 67d4862f6b | |||
| 6a16327c30 | |||
| 060b1b8f41 | |||
| da2e228293 | |||
| ab7397bf55 | |||
| f79547f781 | |||
| 2152f1c90e | |||
| 503fb4d246 | |||
| 2befefa752 | |||
| 85e64c5076 | |||
| 60523dc994 | |||
| 6d780bf350 | |||
| eec02a1f84 | |||
| ace2fe6fe3 | |||
| 188794af8d | |||
| 92a941a944 | |||
| 0e1ddf2b11 | |||
| b57fa010d1 | |||
| f816452b78 | |||
| 133308141b | |||
| 120083bb31 | |||
| a5bb5c4686 | |||
| e5e2729f0a | |||
| ba8b902f48 | |||
| 2d67815ae6 | |||
| ec0c9066d2 | |||
| 58ae858077 | |||
| 613e7938b3 | |||
| e07ff1a445 | |||
| de7dd34ea2 | |||
| 0552f695f9 | |||
| b8d4f3d946 | |||
| fa112a70b1 | |||
| 97309ebb88 | |||
| 556ddc0b6f | |||
| a0dde690ea | |||
| a76dab5be6 | |||
| 2d10bc5bbf | |||
| 4042632e0b | |||
| 7ccf83e1a4 | |||
| 019cd0756f | |||
| 0e5f9f80be | |||
| fc6767860a | |||
| 5b5b4be7b2 | |||
| 45351d082e | |||
| 22ccd07491 | |||
| 2cf9ee647b | |||
| efb567824b | |||
| f0aac39486 | |||
| f278055805 | |||
| 6f2e36fd11 | |||
| 119d80ffc8 | |||
| 771c3df864 | |||
| 7d1963e458 | |||
| dde85972b3 | |||
| 5e061da910 | |||
| 46bea345de | |||
| bc53462ad6 | |||
| 884df40a1d | |||
| b23168c067 | |||
| 5568212f15 | |||
| 79db79d799 | |||
| 18a4008efd | |||
| 83e4abd1ee | |||
| 243bcfba72 | |||
| af2c8c560f | |||
| 6a37f3871c | |||
| 759a388448 | |||
| 360e89d7ce | |||
| 64655a3284 | |||
| c58356bde8 | |||
| f74117632d | |||
| 5f2b8b8e2b | |||
| f0ce7481fd | |||
| 6e7a5c6140 | |||
| 079c18ee19 | |||
| 26ef6a3815 | |||
| cf6dcefe6c | |||
| 35590eb979 | |||
| 583a8b8a76 | |||
| c6e8a664de | |||
| 24a5e0a579 | |||
| a242c197fb | |||
| 08bdd23149 | |||
| c5b35b2d98 | |||
| 5407fecd1f | |||
| 12d2821d26 | |||
| 2e6d58e6a3 | |||
| 0f0c0fd5ae | |||
| ef9ec1c223 | |||
| f4387bae30 | |||
| 49b238a23a | |||
| 4b75c01b28 | |||
| e7f641046b | |||
| 9aaabe0fab | |||
| b6e4b711bf | |||
| 58fcfd210b | |||
| 5868b9c234 | |||
| 0dc6d9cf9d | |||
| 67f50cf2fd | |||
| 8b26a4d49a | |||
| a1d0bb0aa1 | |||
| bfe08560b1 | |||
| ac3ac24272 | |||
| 03e7191d0e | |||
| b3a42f8723 | |||
| 9c4b740300 | |||
| 2a12bdadf2 | |||
| c90e8ad4d2 | |||
| 4a52c51c86 | |||
| 0efc817ddc | |||
| fee54fc98c | |||
| 210f751396 | |||
| 2e2bc3fe29 | |||
| a6f05f86d1 | |||
| fc144e4848 | |||
| cef9531428 | |||
| abd4faefbf | |||
| 966f7aaed5 | |||
| 51b59fa995 | |||
| 0b713736bd | |||
| 9e33581c66 | |||
| b97792b596 | |||
| c0746f8eff | |||
| a826a9d692 | |||
| 6d04203d37 | |||
| 33b2ed8a94 | |||
| 4f16da7ad0 | |||
| 36f92c7655 | |||
| 1d4f5a759e | |||
| 28af9bc811 | |||
| cf04cd4094 | |||
| 23e9efb28a | |||
| 57322df29c | |||
| fff82d34c6 | |||
| 7211860c21 | |||
| 9fc69044f8 | |||
| 76467c3e81 | |||
| 4cfc04e2f3 | |||
| 6bfc7771d0 | |||
| 7055135257 | |||
| 85312cf20d | |||
| 4051cae5b0 | |||
| c64bb6dedf | |||
| 7bb17ae0db | |||
| 3244eb9b15 | |||
| 6e173847ba | |||
| 8cbc1f763d | |||
| 2d45abeb28 | |||
| 2fd0ad77bc | |||
| 53c1167831 | |||
| 48d4a1d7bb | |||
| 4bbbba6e89 | |||
| 0a13575325 | |||
| d6075209df | |||
| 9d1b39422b | |||
| 38e297d32b | |||
| 459ef3c9f1 | |||
| 3bb12487ba | |||
| 8b2428c60d | |||
| f9befd9051 | |||
| 1020a9bd5d | |||
| 2844c4ea86 | |||
| 3344188fee | |||
| 67138c2f78 | |||
| f59d19ef21 | |||
| d0fa7e638f | |||
| 63011e466e | |||
| 92d1976300 | |||
| a0cdc9fa07 | |||
| 60f0006597 | |||
| 67a3c62a08 | |||
| 21d5dc1e0b | |||
| 0d810f59f9 | |||
| dba60b700f | |||
| ad9a56bd20 | |||
| 82585e9104 | |||
| dfbb241b0b | |||
| f69ea91f45 | |||
| 768d916092 | |||
| c0acecae10 | |||
| f741d1c7e4 | |||
| d25c70f1d1 | |||
| 6f954467ef | |||
| 867987a5f5 | |||
| 61e0a6351c | |||
| 558264c23b | |||
| cf7aa371af | |||
| ffedaf6a73 | |||
| d9ae272b36 | |||
| 3c5d1f5c9e | |||
| b69b8b7585 | |||
| 8c7e498fb2 | |||
| 359413a296 | |||
| cef7d894c4 | |||
| 7d9cc1aacc | |||
| a6780a756b | |||
| 4e37d17b42 | |||
| 793923348f | |||
| 700db1788d | |||
| 68a5a44702 | |||
| 464891f5a1 | |||
| d2e5db2680 | |||
| a9c41d0545 | |||
| 0bc458d307 | |||
| 36c7cc285e | |||
| f9fbd31a0f | |||
| 8789c1f855 | |||
| f65073480a | |||
| 157ed831a7 | |||
| 0799a59a15 | |||
| 60ea3e83e1 | |||
| 4b874deb50 | |||
| 6726b9ad50 | |||
| 75e6fe609e | |||
| b40d1ccf7d | |||
| 4f7ba23904 | |||
| 49084706f2 | |||
| 1852e5f6bf | |||
| be0ed5428a | |||
| f428efde6a | |||
| e79f04f33e | |||
| 04db8c281f | |||
| 2681698186 | |||
| 1682c66da6 | |||
| 38bc1032d8 | |||
| ba37eef9ca | |||
| 3db179882a | |||
| ba8f25a807 | |||
| eac3d3089b | |||
| 0454a9e147 | |||
| f390cd1c68 | |||
| aaa1ba5aa9 | |||
| fb9556abd9 | |||
| 3cfb3b1f84 | |||
| b071bab137 | |||
| 5dbc68ea76 | |||
| e76fbc3b77 | |||
| 09ca0514d1 | |||
| faec32a5bb | |||
| 1f5793ab79 | |||
| df22bd1cec | |||
| 61dba4e78d | |||
| bb678117f8 | |||
| 2dc08e71b7 | |||
| df2f844e74 | |||
| 621c602fc0 | |||
| f809169757 | |||
| 848b135612 | |||
| db6af66705 | |||
| 728e1f5a86 | |||
| d02d95bc9e | |||
| c1180c5373 | |||
| 4e01fb64e6 | |||
| d0fcb1da34 | |||
| 48352d06b3 | |||
| d995652569 | |||
| 5a958fa51c | |||
| cfb9e2cacd | |||
| 7932fc1cab | |||
| e0fc1715b5 | |||
| 1cfd6caa64 | |||
| 54bf18888d | |||
| 6b60fc4259 | |||
| 216af8697c | |||
| d97e7314d0 | |||
| 67d9dda143 | |||
| 5c60e303cb | |||
| e36b8f3a56 | |||
| 1e7a19b463 | |||
| 8004249a29 | |||
| 6cd800e4ef | |||
| e182bc7ed2 | |||
| c338d51c9d | |||
| c1912250df | |||
| 994c41980e | |||
| 385b7a514d | |||
| 3aaade9228 | |||
| b4dd19b711 | |||
| 9e9b83b4ba | |||
| 5ff5114669 | |||
| f63039f3fb | |||
| b39399463d | |||
| 8217ef7fbb | |||
| b4851c34e1 | |||
| 8e39832d3e | |||
| ce7ac6da16 | |||
| 57879a57cd | |||
| 6f5b367028 | |||
| 291948c1ef | |||
| 0ca6888fb1 | |||
| 5e92236396 | |||
| 28db40112b | |||
| b2153883ef | |||
| 807cbeb75d | |||
| 4730aebdc6 | |||
| c2279eec87 | |||
| f5567ae9ad | |||
| 2fcba04cf5 | |||
| 0eefd1605f | |||
| 7b88ea022d | |||
| 7a922114ab | |||
| f717c270fd | |||
| ee5ed468c3 | |||
| a9ded445a7 | |||
| 9ecad52df2 | |||
| 8c500985bf | |||
| 3567f9ba5d | |||
| 852bfb716c | |||
| 473956aa3c | |||
| b22fc19400 | |||
| f6a8ece992 | |||
| afc0c21aae | |||
| 08e6106c1f | |||
| 1f9f654e3d | |||
| 501e923760 | |||
| 37b716cfc5 | |||
| 8584db0ab5 | |||
| 5aff3e9692 | |||
| 5d8f03cbc3 | |||
| b6c85ac15a | |||
| c2af757f4e | |||
| 26252c55ac | |||
| 3ce547bcea | |||
| e0492f589e | |||
| 4931288ffd | |||
| 52e0f77a3c | |||
| 20c016f1bf | |||
| 00a271cbba | |||
| 3a6b69634d | |||
| 78cf7f42e3 | |||
| 2b52454c00 | |||
| e032e4561b | |||
| 23ce1e0476 | |||
| 825b7b98d7 | |||
| 3de3128a2a | |||
| 92d70ccc8b | |||
| f8c5154457 | |||
| 3a3874e9f1 | |||
| 61b94c519b | |||
| ac0f46ca66 | |||
| e538cdb509 | |||
| df8a63c5f5 | |||
| 6827a61bf8 | |||
| 20e56c05b3 | |||
| 5494139319 | |||
| c491ba087a | |||
| 5cf045c941 | |||
| 55fea5f077 | |||
| 3a9cb73950 | |||
| d99f613b79 | |||
| 68db8ddff3 | |||
| c7490b264b | |||
| a8e84cfad3 | |||
| 5ec4f7e4e8 | |||
| b019ce2f2e | |||
| dd90e41610 | |||
| bdf78e374d | |||
| 163197f583 | |||
| 8e13bba44a | |||
| e4478b86a5 | |||
| 1b1ae59ed7 | |||
| a54f44d22a | |||
| d2394274eb | |||
| 0eedb0b397 | |||
| 782e9db1f6 | |||
| 2977472c56 | |||
| 8f3a8bfa91 | |||
| 914f6e9516 | |||
| fa6f65cb7c | |||
| dafbe6f995 | |||
| 4a8ac6377b | |||
| c91fac4ec4 | |||
| 00d23b084e | |||
| e0a3b82cea | |||
| e49036d8c4 | |||
| 4a8356692b | |||
| adfb7bc63a | |||
| 3f41cefa88 | |||
| 2074ff1987 | |||
| 65fe189421 | |||
| bb7590eab4 | |||
| 6a04457219 | |||
| 7f8b1d9586 | |||
| 4362cc53bb | |||
| 959e6b2a12 | |||
| 2d91070e5a | |||
| cc038ece32 | |||
| 7af1292f29 | |||
| 2c3fd7a84f | |||
| d36f70dbc7 | |||
| 77f5f29c90 | |||
| ead45ed843 | |||
| a6259bb7eb | |||
| 0249ebeb95 | |||
| 1c3a35a137 | |||
| c12319b417 | |||
| 90ac00a628 | |||
| e5c71cb3a4 | |||
| ebf0681574 | |||
| 642643f412 | |||
| 8dd65ac255 | |||
| 8df58f226b | |||
| 58b4f2ce14 | |||
| 65f0c836fe | |||
| 2aa16085a5 | |||
| 25f05a69c6 | |||
| 5c2a94da16 | |||
| 162325e90e | |||
| b022a8c608 | |||
| 1e404f5e6b | |||
| 8ef7780332 | |||
| 51b4f7b3f7 | |||
| 9b3fa33e50 | |||
| 9349d0813d | |||
| 8d9bc500a1 | |||
| cae7b6e29f | |||
| 0b4ac732b2 | |||
| acacbb91e3 | |||
| fd6d3d9a2f | |||
| 3b531e30b1 | |||
| 0c78085b2e | |||
| 4ab53308f7 | |||
| 06ae161793 | |||
| 94e005d2f8 | |||
| 717072c3a5 | |||
| 02b4bde92a | |||
| a397d1e233 | |||
| d2198c0765 | |||
| 549bf63198 | |||
| 8b824f5342 | |||
| d052d5e8a1 | |||
| 610c22dd02 | |||
| a5496ab6a9 | |||
| c29c8f009c | |||
| fb7e9f2a6c | |||
| 62c2bbb9fa | |||
| 7656772194 | |||
| f22ff050e0 | |||
| 708f0b44fd | |||
| 2ee4581f17 | |||
| e337fb869f | |||
| 4a8b0147e1 | |||
| c95a1c7e1f | |||
| a07cd5c3f5 | |||
| 4d1473582e | |||
| 3bc9f4c452 | |||
| 11377038a0 | |||
| 6053d3eac1 | |||
| 529830f36b | |||
| 505cf6265c | |||
| 8cd9673eec | |||
| ac9cb28558 | |||
| 6eefe0698c | |||
| d0113532b6 | |||
| 5a1d2ad5f4 | |||
| dfe02f4c74 | |||
| 5349d556b9 | |||
| 7f7d8a501b | |||
| cbee0e465b | |||
| faf00f0b0d | |||
| fa97da5781 | |||
| e7bab546db | |||
| e2f4a30b5b | |||
| ccff1df041 | |||
| 2037cfc25a | |||
| 268a091d8a | |||
| 6b52dfab3e | |||
| 839999851f | |||
| a87470b7b5 | |||
| e76aeb80fd | |||
| 28292e59e2 | |||
| 25dda481b2 | |||
| bbda233fd8 | |||
| 349218f0b5 | |||
| f18d00a69d | |||
| 25ea6504de | |||
| 43081c70e2 | |||
| 0fde1c2026 | |||
| b35383571e | |||
| 45a84847a2 | |||
| 70d92f26d6 | |||
| 737ed86e69 | |||
| 3865057b7a | |||
| 42a84dcd86 | |||
| e2d819c59d | |||
| bfee6a88dc | |||
| 68f3be17df | |||
| 929c97ff5e | |||
| 4b66a2e4d0 | |||
| 46e740154e | |||
| 05e89f49da | |||
| 358e9071e3 | |||
| e37b6a1cc0 | |||
| 266721973b | |||
| 7c27ac85cb | |||
| 4b79732e38 | |||
| 2ceeea5298 | |||
| 3ec659a59b | |||
| 4309056851 | |||
| b768f20f7a | |||
| b3a71bcf53 | |||
| 50c199bc03 | |||
| d6302ac128 | |||
| 6a5d9e1394 | |||
| ac5fc3e6ea | |||
| aa6cbceeb9 | |||
| 46a7395382 | |||
| a782811dad | |||
| d22039bc96 | |||
| 15539c258e | |||
| f9ca4acf16 | |||
| 66d2a9b7fe | |||
| 6dff8c3221 | |||
| f13c65e083 | |||
| 56a7d5cb86 | |||
| c63c0675d5 | |||
| 22bc4b8c9e | |||
| ead17b8906 | |||
| 1a211daac2 | |||
| c7e502da4e | |||
| 70df5e6cec | |||
| 793f287c35 | |||
| 8e8b1c60b8 | |||
| ba2cd51852 | |||
| 3986a4ef60 | |||
| c99d89fb11 | |||
| 0a9c33de91 | |||
| 88c940fc53 | |||
| 88c861cbde | |||
| cd071ca144 | |||
| 9970b8ec36 | |||
| ffc564becd | |||
| 6289fd5941 | |||
| 7f44fe76c7 | |||
| ea86dc6785 | |||
| 754f9e1ed1 | |||
| 9a1a02e664 | |||
| 1e92195355 | |||
| bb63c1c990 | |||
| 5af494de76 | |||
| 5bcac73a45 | |||
| 7a709e48d7 | |||
| d2d9fe010e | |||
| 4b6b129934 | |||
| 7645146d77 | |||
| 9b8cca38ae | |||
| e872fda3f2 | |||
| 6ccb03c557 | |||
| b8237b1be7 | |||
| 68e19cf8e4 | |||
| 91fba729b8 | |||
| 0af0e09b6e | |||
| 1276d56627 | |||
| bd2773f9c1 | |||
| b8b1b8ef36 | |||
| 0f98d72e30 | |||
| 44ec7734d4 | |||
| c5adb063ca | |||
| 83ef8f9789 | |||
| b8a803a641 | |||
| 31c0effebe | |||
| e90c4b3eb5 | |||
| ae6efcc27d | |||
| 7922ac127b | |||
| 559f0443af | |||
| 4e3faf1573 | |||
| e3039bd2d8 | |||
| e668cba839 | |||
| fcc47c0b7f | |||
| 2f189bc2ff | |||
| ee9d134f0e | |||
| c1fad7cc02 | |||
| 3a48b4ee4f | |||
| ec626f9741 | |||
| 90906cdc0e | |||
| a694527645 | |||
| bd68b20d31 | |||
| 080d976400 | |||
| e200a8525b | |||
| 5bdf7abc2b | |||
| 9faa1d2e1e | |||
| f586353cdb | |||
| 2c9f2ee5cf | |||
| d8bd45bf54 | |||
| 9341e0b500 | |||
| eebf303f3e | |||
| df487d5488 | |||
| 7b66da5823 | |||
| 3a929c2a03 | |||
| a8ac3c4771 | |||
| 8d223786b9 | |||
| 068c6a6ea0 | |||
| 0db677121a | |||
| 7591170bca | |||
| 7272541826 | |||
| 2460912532 | |||
| c1e4c22e3f | |||
| 17bd17fa3c | |||
| 05da3ccb66 | |||
| 578044884c | |||
| 226148d04f | |||
| c44c7516ff | |||
| c3c0749222 | |||
| b7e63b0e54 | |||
| 60cd2cb0f9 | |||
| e6dd630caf | |||
| cad14b318a | |||
| 4df1cc17bf | |||
| 884bb331b3 | |||
| ff4a83af47 | |||
| f6f323b307 | |||
| 330280d611 | |||
| 809432cbbd | |||
| d64fee1d15 | |||
| 3d01e15710 | |||
| c2d7a5aca8 | |||
| 2e8a040210 | |||
| 50886147ae | |||
| c0f2885de6 | |||
| e80574ecd7 | |||
| 7cdcf9e86f | |||
| 304720fc8e | |||
| 88adfb1446 | |||
| f8719d0912 | |||
| c6d36fccd9 | |||
| 13d061f7e9 | |||
| ba11c5beb2 | |||
| c1800d9a02 | |||
| 868859ac83 | |||
| d91114b340 | |||
| 0dc4d46fa3 | |||
| 0442aaaa7f | |||
| 2c8b120de1 | |||
| fc50ef68b4 | |||
| 92613a37cd | |||
| 77843fe697 | |||
| 9aea5ddc97 | |||
| 4d1333c302 | |||
| 9ca55f6f0e | |||
| 4698799b43 | |||
| 5d4e6ac89a | |||
| b1c09c62d9 | |||
| 2b7b74dbdd |
@@ -1,121 +1,17 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
# Using windows for builds
|
||||
win: circleci/windows@2.4.0
|
||||
# Upload artifacts to s3
|
||||
aws-s3: circleci/aws-s3@2.0.0
|
||||
|
||||
# Define the jobs we want to run for this project
|
||||
jobs:
|
||||
build-ui:
|
||||
build:
|
||||
docker:
|
||||
- image: "circleci/node:16"
|
||||
- image: cimg/base:2023.03
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: "npm install"
|
||||
working_directory: "ui"
|
||||
- run:
|
||||
command: "npm run build"
|
||||
working_directory: "ui"
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle_connector/html
|
||||
|
||||
build-connector: # Reusable job for basic connectors
|
||||
executor:
|
||||
name: win/default # comes with python 3.7.3
|
||||
shell: cmd.exe
|
||||
parameters:
|
||||
slug:
|
||||
type: string
|
||||
default: ""
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Patch
|
||||
shell: powershell.exe
|
||||
command:
|
||||
| # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
|
||||
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
|
||||
$semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
|
||||
$ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
|
||||
$channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
|
||||
$version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
|
||||
New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
|
||||
echo $version
|
||||
python patch_version.py $version
|
||||
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools/Installers
|
||||
|
||||
get-ci-tools: # Clones our ci tools and persists them to the workspace
|
||||
docker:
|
||||
- image: cimg/base:2021.01
|
||||
steps:
|
||||
- run:
|
||||
name: Clone
|
||||
command: git clone https://$GITHUB_TOKEN@github.com/specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools
|
||||
|
||||
deploy: # Uploads all installers found to S3
|
||||
docker:
|
||||
- image: cimg/base:2021.01
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: List contents
|
||||
command: ls -R speckle-sharp-ci-tools/Installers
|
||||
- aws-s3/copy:
|
||||
arguments: "--recursive --endpoint=https://$SPACES_REGION.digitaloceanspaces.com --acl public-read"
|
||||
aws-access-key-id: SPACES_KEY
|
||||
aws-region: SPACES_REGION
|
||||
aws-secret-access-key: SPACES_SECRET
|
||||
from: '"speckle-sharp-ci-tools/Installers/"'
|
||||
to: s3://speckle-releases/installers/
|
||||
- run: echo "so long and thanks for all the fish"
|
||||
|
||||
# Orchestrate our job run sequence
|
||||
workflows:
|
||||
build-and-deploy:
|
||||
build_and_test:
|
||||
when:
|
||||
false
|
||||
jobs:
|
||||
- get-ci-tools:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
- build-ui:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
- build-connector:
|
||||
slug: sketchup
|
||||
requires:
|
||||
- get-ci-tools
|
||||
- build-ui
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
- deploy:
|
||||
requires:
|
||||
- get-ci-tools
|
||||
- build-ui
|
||||
- build-connector
|
||||
filters:
|
||||
tags:
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/ # For testing only! /ci\/.*/
|
||||
- build
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_call:
|
||||
outputs:
|
||||
semver:
|
||||
description: "The full SemVer 2.0 version of this build, e.g. '3.0.0-alpha.1234' (note: no 'v'-prefix)"
|
||||
value: ${{ jobs.build.outputs.semver }}
|
||||
file_version:
|
||||
description: "The file info version, e.g. '3.0.0.1234'"
|
||||
value: ${{ jobs.build.outputs.file_version }}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
semver: ${{ steps.set-version.outputs.semver }}
|
||||
file_version: ${{ steps.set-version.outputs.file-version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- id: set-version
|
||||
name: Set version to output
|
||||
shell: bash
|
||||
run: |
|
||||
TAG=${{ github.ref_name }}
|
||||
if [[ "${{ github.ref }}" != refs/tags/* ]]; then
|
||||
TAG="v3.0.99.${{ github.run_number }}"
|
||||
fi
|
||||
SEMVER="${TAG#v}"
|
||||
FILE_VERSION=$(echo "$TAG" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||
FILE_VERSION="$FILE_VERSION.${{ github.run_number }}"
|
||||
|
||||
echo "semver=$SEMVER" >> "$GITHUB_OUTPUT"
|
||||
echo "file-version=$FILE_VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo $SEMVER
|
||||
echo $FILE_VERSION
|
||||
|
||||
- name: Set connector version
|
||||
run: |
|
||||
python patch_version.py ${{steps.set-version.outputs.semver}}
|
||||
|
||||
- uses: montudor/action-zip@v1
|
||||
with:
|
||||
args: zip -q -r sketchup.zip vendor speckle_connector_3/ speckle_connector_3.rb
|
||||
|
||||
- name: ⬆️ Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: output-${{steps.set-version.outputs.semver}}
|
||||
path: sketchup.zip
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
compression-level: 0 # no compression
|
||||
@@ -1,78 +0,0 @@
|
||||
name: Update issue Status
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
update_issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get project data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ORGANIZATION: specklesystems
|
||||
PROJECT_NUMBER: 9
|
||||
run: |
|
||||
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
query($org: String!, $number: Int!) {
|
||||
organization(login: $org){
|
||||
projectNext(number: $number) {
|
||||
id
|
||||
fields(first:20) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
settings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||
|
||||
echo "$PROJECT_ID"
|
||||
echo "$STATUS_FIELD_ID"
|
||||
|
||||
echo 'DONE_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .settings | fromjson | .options[] | select(.name== "Done") | .id' project_data.json) >> $GITHUB_ENV
|
||||
echo "$DONE_ID"
|
||||
|
||||
- name: Add Issue to project #it's already in the project, but we do this to get its node id!
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
mutation($project:ID!, $id:ID!) {
|
||||
addProjectNextItem(input: {projectId: $project, contentId: $id}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
||||
|
||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||
|
||||
- name: Update Status
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
mutation($project:ID!, $status:ID!, $id:ID!, $value:String!) {
|
||||
set_status: updateProjectNextItemField(
|
||||
input: {
|
||||
projectId: $project
|
||||
itemId: $id
|
||||
fieldId: $status
|
||||
value: $value
|
||||
}
|
||||
) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f status=$STATUS_FIELD_ID -f id=$ITEM_ID -f value=${{ env.DONE_ID }}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Build and deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "installer-test/**"]
|
||||
tags: ["v3.*.*"] # Manual delivery on every 3.x tag
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
|
||||
deploy-installers:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
|
||||
steps:
|
||||
- name: 🔫 Trigger Build Installer(s)
|
||||
uses: the-actions-org/workflow-dispatch@v4.0.0
|
||||
with:
|
||||
workflow: Build Installers
|
||||
repo: specklesystems/connector-installers
|
||||
token: ${{ secrets.CONNECTORS_GH_TOKEN }}
|
||||
inputs: '{
|
||||
"run_id": "${{ github.run_id }}",
|
||||
"semver": "${{ needs.build.outputs.semver }}",
|
||||
"file_version": "${{ needs.build.outputs.file_version }}",
|
||||
"repo": "${{ github.repository }}",
|
||||
"is_public_release": ${{ env.IS_PUBLIC_RELEASE }}
|
||||
}'
|
||||
ref: main
|
||||
wait-for-completion: true
|
||||
wait-for-completion-interval: 10s
|
||||
wait-for-completion-timeout: 10m
|
||||
display-workflow-run-url: true
|
||||
display-workflow-run-url-interval: 10s
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: output-*
|
||||
@@ -1,50 +0,0 @@
|
||||
name: Move new issues into Project
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
track_issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get project data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ORGANIZATION: specklesystems
|
||||
PROJECT_NUMBER: 9
|
||||
run: |
|
||||
gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
query($org: String!, $number: Int!) {
|
||||
organization(login: $org){
|
||||
projectNext(number: $number) {
|
||||
id
|
||||
fields(first:20) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
settings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||
|
||||
- name: Add Issue to project
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GHPROJECT_TOKEN}}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
item_id="$( gh api graphql --header 'GraphQL-Features: projects_next_graphql' -f query='
|
||||
mutation($project:ID!, $id:ID!) {
|
||||
addProjectNextItem(input: {projectId: $project, contentId: $id}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f id=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
||||
|
||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||
@@ -10,8 +10,23 @@
|
||||
settings.json
|
||||
|
||||
# vue app build dist folder
|
||||
speckle_connector/html
|
||||
|
||||
speckle_connector_3/vue_ui
|
||||
speckle_connector_3/html
|
||||
|
||||
# speckle-sharp-ci-tools
|
||||
/speckle-sharp-ci-tools
|
||||
|
||||
# _sqlite3
|
||||
/_sqlite3/.vs
|
||||
/_sqlite3/Release (2.7)
|
||||
/_sqlite3/Release (2.5)
|
||||
/_sqlite3/Release (2.2)
|
||||
/_sqlite3/Release (2.0)
|
||||
/_sqlite3/Debug (2.7)
|
||||
/_sqlite3/Debug (2.5)
|
||||
/_sqlite3/Debug (2.2)
|
||||
/_sqlite3/Debug (2.0)
|
||||
|
||||
*.gem
|
||||
*.rbc
|
||||
/.config
|
||||
@@ -24,6 +39,9 @@ speckle_connector/html
|
||||
/test/version_tmp/
|
||||
/tmp/
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
|
||||
# Used by dotenv library to load environment variables.
|
||||
.env
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "_sqlite3"]
|
||||
path = _sqlite3
|
||||
url = git@github.com:specklesystems/sketchup-sqlite3.git
|
||||
@@ -1,49 +1,132 @@
|
||||
require:
|
||||
- rubocop-sketchup
|
||||
- rubocop-minitest
|
||||
- rubocop-rake
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.7
|
||||
EnabledByDefault: true
|
||||
AutoCorrect: true
|
||||
TargetRubyVersion: 2.5
|
||||
DisabledByDefault: false
|
||||
NewCops: enable
|
||||
DisplayCopNames: true
|
||||
ExtraDetails: true
|
||||
SuggestExtensions: false
|
||||
Exclude:
|
||||
- '_tools/jf_RubyPanel.rb'
|
||||
- '_tools/jf_RubyPanel/**/*.rb'
|
||||
- '_tools/su_attributes.rb'
|
||||
- '_tools/su_attributes/**/*.rb'
|
||||
- '_tools/su_attributes/**/*.rb'
|
||||
- '_sqlite3/**/*.rb'
|
||||
- 'ui/**/*'
|
||||
- 'speckle_connector_3/src/ext/**/*.rb'
|
||||
- 'vendor/bundle/**/*'
|
||||
- 'tests/**/*.rb'
|
||||
SketchUp:
|
||||
SourcePath: .
|
||||
TargetSketchUpVersion: 2021
|
||||
Exclude: # Exclude common folders.
|
||||
- 'tests/**/*'
|
||||
- 'benchmarks/**/*'
|
||||
- '_tools/**/*'
|
||||
- 'Rakefile'
|
||||
|
||||
Style/StringLiterals:
|
||||
Layout:
|
||||
Enabled: true
|
||||
EnforcedStyle: double_quotes
|
||||
|
||||
Style/StringLiteralsInInterpolation:
|
||||
Layout/IndentationStyle:
|
||||
EnforcedStyle: spaces
|
||||
IndentationWidth: 2
|
||||
|
||||
# If DisabledByDefault is set to true then we need to enable the SketchUp
|
||||
# related departments:
|
||||
|
||||
SketchupDeprecations:
|
||||
Enabled: true
|
||||
EnforcedStyle: double_quotes
|
||||
|
||||
Layout/LineLength:
|
||||
Max: 120
|
||||
SketchupPerformance:
|
||||
Enabled: true
|
||||
|
||||
Lint/ConstantResolution:
|
||||
SketchupRequirements:
|
||||
Enabled: true
|
||||
|
||||
SketchupSuggestions:
|
||||
Enabled: true
|
||||
|
||||
SketchupBugs:
|
||||
Enabled: true
|
||||
|
||||
SketchupRequirements/FileStructure:
|
||||
Enabled: false
|
||||
|
||||
Style/Copyright:
|
||||
Enabled: false
|
||||
|
||||
Style/DocumentationMethod:
|
||||
SketchupSuggestions/ModelEntities:
|
||||
Enabled: false
|
||||
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
Max: 30
|
||||
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ModuleLength:
|
||||
Enabled: false
|
||||
# Exclude spec tests
|
||||
Exclude:
|
||||
- "**/*_spec.rb"
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 20
|
||||
|
||||
Metrics/ClassLength:
|
||||
Max: 200
|
||||
|
||||
Layout/EndOfLine:
|
||||
Enabled: false
|
||||
EnforcedStyle: lf
|
||||
|
||||
Minitest/MultipleAssertions:
|
||||
Max: 5
|
||||
|
||||
Naming/MethodParameterName:
|
||||
AllowedNames: [x, y, z, id]
|
||||
|
||||
Naming/VariableNumber:
|
||||
EnforcedStyle: snake_case
|
||||
|
||||
# SketchUp 2017 uses Ruby 2.2 where safe navigation is not available
|
||||
Style/SafeNavigation:
|
||||
Enabled: false
|
||||
|
||||
Metrics/ParameterLists:
|
||||
Style/AndOr:
|
||||
Enabled: false
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Style/Documentation:
|
||||
Exclude:
|
||||
- "*tests/**/*_spec.rb"
|
||||
- "*tests/**/*_test.rb"
|
||||
|
||||
Style/Not:
|
||||
Enabled: false
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Style/NumericLiterals:
|
||||
Enabled: false
|
||||
|
||||
Style/NumericPredicate:
|
||||
EnforcedStyle: comparison
|
||||
|
||||
Style/Proc:
|
||||
Enabled: false
|
||||
|
||||
Style/RedundantReturn:
|
||||
Enabled: false
|
||||
|
||||
# SketchUp 2017 uses Ruby 2.2 where safe navigation is not available
|
||||
Style/SlicingWithRange:
|
||||
Enabled: false
|
||||
|
||||
# SketchUp 2017 uses Ruby 2.2 where transform_values is not available
|
||||
Style/HashTransformValues:
|
||||
Enabled: false
|
||||
|
||||
# SketchUp 2017 uses Ruby 2.2 where transform_keys is not available
|
||||
Style/HashTransformKeys:
|
||||
Enabled: false
|
||||
|
||||
# SketchUp 2017 uses Ruby 2.2 where block needs to be wrapped in begin/end if ensure can be used
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
@@ -1,6 +1,6 @@
|
||||
require_paths:
|
||||
- "C:/Program Files/SketchUp/SketchUp 2021/Tools"
|
||||
- speckle_connector
|
||||
- speckle_connector_3
|
||||
|
||||
require:
|
||||
- sketchup-api-stubs
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
# gem "rake", "~> 13.0"
|
||||
|
||||
gem "rubocop", "~> 1.7"
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
group :development do
|
||||
gem "minitest"
|
||||
gem "sketchup-api-stubs"
|
||||
gem "solargraph"
|
||||
# mini tests for ruby classes
|
||||
gem 'minitest'
|
||||
# Git hooks manager
|
||||
gem 'overcommit', require: false
|
||||
# Pry is a runtime developer console and IRB alternative with powerful introspection capabilities.
|
||||
# Pry aims to be more than an IRB replacement. It is an attempt to bring REPL driven programming to the Ruby language.
|
||||
gem 'pry'
|
||||
# Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.
|
||||
gem 'rake'
|
||||
# RuboCop is a Ruby static code analyzer (a.k.a. linter) and code formatter.
|
||||
gem 'rubocop'
|
||||
# A RuboCop extension focused on enforcing Minitest best practices and coding conventions.
|
||||
gem 'rubocop-minitest'
|
||||
# A RuboCop plugin for Rake.
|
||||
gem 'rubocop-rake'
|
||||
# Code analysis for SketchUp extensions using the SketchUp Ruby API.
|
||||
gem 'rubocop-sketchup'
|
||||
# wraps around static analysis gems such as Reek, Flay and Flog to provide a quality report of your Ruby code.
|
||||
gem 'rubycritic', '~> 4.3', '>= 4.3.3', require: false
|
||||
# Auto completions for SketchUp API.
|
||||
gem 'sketchup-api-stubs'
|
||||
# Runtime dependency of skippy for Ruby 3.2. Have it!
|
||||
gem 'sorted_set', '~> 1.0'
|
||||
# Aid with common SketchUp extension tasks.
|
||||
gem 'skippy', '~> 0.5.2.a'
|
||||
end
|
||||
|
||||
gem "sqlite3", "~> 1.4"
|
||||
|
||||
@@ -1,74 +1,141 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.8.4)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
ast (2.4.2)
|
||||
backport (1.2.0)
|
||||
benchmark (0.1.1)
|
||||
diff-lcs (1.4.4)
|
||||
e2mmap (0.1.0)
|
||||
jaro_winkler (1.5.4)
|
||||
kramdown (2.3.1)
|
||||
rexml
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
minitest (5.14.4)
|
||||
nokogiri (1.12.5-x64-mingw32)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.12.5-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
parallel (1.20.1)
|
||||
parser (3.0.2.0)
|
||||
axiom-types (0.1.1)
|
||||
descendants_tracker (~> 0.0.4)
|
||||
ice_nine (~> 0.11.0)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
childprocess (4.1.0)
|
||||
coderay (1.1.3)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
docile (1.4.0)
|
||||
equalizer (0.0.11)
|
||||
erubi (1.11.0)
|
||||
flay (2.13.0)
|
||||
erubi (~> 1.10)
|
||||
path_expander (~> 1.0)
|
||||
ruby_parser (~> 3.0)
|
||||
sexp_processor (~> 4.0)
|
||||
flog (4.6.6)
|
||||
path_expander (~> 1.0)
|
||||
ruby_parser (~> 3.1, > 3.1.0)
|
||||
sexp_processor (~> 4.8)
|
||||
git (1.19.1)
|
||||
addressable (~> 2.8)
|
||||
rchardet (~> 1.8)
|
||||
ice_nine (0.11.2)
|
||||
iniparse (1.5.0)
|
||||
kwalify (0.7.2)
|
||||
launchy (2.5.0)
|
||||
addressable (~> 2.7)
|
||||
method_source (1.0.0)
|
||||
minitest (5.16.3)
|
||||
naturally (2.2.1)
|
||||
overcommit (0.59.1)
|
||||
childprocess (>= 0.6.3, < 5)
|
||||
iniparse (~> 1.4)
|
||||
rexml (~> 3.2)
|
||||
parallel (1.22.1)
|
||||
parser (3.1.2.1)
|
||||
ast (~> 2.4.1)
|
||||
racc (1.6.0)
|
||||
rainbow (3.0.0)
|
||||
regexp_parser (2.1.1)
|
||||
reverse_markdown (2.0.0)
|
||||
nokogiri
|
||||
path_expander (1.1.1)
|
||||
pry (0.14.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
public_suffix (5.0.1)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
rbtree (0.4.6)
|
||||
rchardet (1.8.0)
|
||||
reek (6.1.1)
|
||||
kwalify (~> 0.7.0)
|
||||
parser (~> 3.1.0)
|
||||
rainbow (>= 2.0, < 4.0)
|
||||
regexp_parser (2.6.0)
|
||||
rexml (3.2.5)
|
||||
rubocop (1.19.1)
|
||||
rubocop (1.7.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.0.0.0)
|
||||
parser (>= 2.7.1.5)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml
|
||||
rubocop-ast (>= 1.9.1, < 2.0)
|
||||
rubocop-ast (>= 1.2.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.11.0)
|
||||
parser (>= 3.0.1.1)
|
||||
unicode-display_width (>= 1.4.0, < 2.0)
|
||||
rubocop-ast (1.4.1)
|
||||
parser (>= 2.7.1.5)
|
||||
rubocop-minitest (0.23.0)
|
||||
rubocop (>= 0.90, < 2.0)
|
||||
rubocop-rake (0.6.0)
|
||||
rubocop (~> 1.0)
|
||||
rubocop-sketchup (1.3.0)
|
||||
rubocop (>= 0.82, < 2.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
sketchup-api-stubs (0.7.7)
|
||||
solargraph (0.43.0)
|
||||
backport (~> 1.2)
|
||||
benchmark
|
||||
bundler (>= 1.17.2)
|
||||
diff-lcs (~> 1.4)
|
||||
e2mmap
|
||||
jaro_winkler (~> 1.5)
|
||||
kramdown (~> 2.3)
|
||||
kramdown-parser-gfm (~> 1.1)
|
||||
parser (~> 3.0)
|
||||
reverse_markdown (>= 1.0.5, < 3)
|
||||
rubocop (>= 0.52)
|
||||
thor (~> 1.0)
|
||||
tilt (~> 2.0)
|
||||
yard (~> 0.9, >= 0.9.24)
|
||||
sqlite3 (1.4.2)
|
||||
thor (1.1.0)
|
||||
tilt (2.0.10)
|
||||
unicode-display_width (2.0.0)
|
||||
yard (0.9.26)
|
||||
ruby_parser (3.19.1)
|
||||
sexp_processor (~> 4.16)
|
||||
rubycritic (4.7.0)
|
||||
flay (~> 2.8)
|
||||
flog (~> 4.4)
|
||||
launchy (>= 2.0.0)
|
||||
parser (>= 2.6.0)
|
||||
rainbow (~> 3.0)
|
||||
reek (~> 6.0, < 7.0)
|
||||
ruby_parser (~> 3.8)
|
||||
simplecov (>= 0.17.0)
|
||||
tty-which (~> 0.4.0)
|
||||
virtus (~> 1.0)
|
||||
set (1.1.0)
|
||||
sexp_processor (4.16.1)
|
||||
simplecov (0.21.2)
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
sketchup-api-stubs (0.7.8)
|
||||
skippy (0.5.2.a)
|
||||
git (~> 1.3)
|
||||
naturally (~> 2.1)
|
||||
sorted_set (~> 1.0)
|
||||
thor (>= 0.19, < 2.0)
|
||||
sorted_set (1.0.3)
|
||||
rbtree
|
||||
set (~> 1.0)
|
||||
thor (1.3.1)
|
||||
thread_safe (0.3.6)
|
||||
tty-which (0.4.2)
|
||||
unicode-display_width (1.8.0)
|
||||
virtus (1.0.5)
|
||||
axiom-types (~> 0.1)
|
||||
coercible (~> 1.0)
|
||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||
equalizer (~> 0.0, >= 0.0.9)
|
||||
|
||||
PLATFORMS
|
||||
x64-mingw-ucrt
|
||||
x64-mingw32
|
||||
x64-unknown
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
minitest
|
||||
rubocop (~> 1.7)
|
||||
overcommit
|
||||
pry
|
||||
rake
|
||||
rubocop
|
||||
rubocop-minitest
|
||||
rubocop-rake
|
||||
rubocop-sketchup
|
||||
rubycritic (~> 4.3, >= 4.3.3)
|
||||
sketchup-api-stubs
|
||||
solargraph
|
||||
sqlite3 (~> 1.4)
|
||||
skippy (~> 0.5.2.a)
|
||||
sorted_set (~> 1.0)
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.26
|
||||
2.3.25
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
workflow: GitFlow/v1
|
||||
next-version: 3.0.0
|
||||
mode: ManualDeployment
|
||||
branches:
|
||||
main:
|
||||
label: rc
|
||||
develop:
|
||||
regex: ^dui3/alpha$
|
||||
label: beta
|
||||
unknown:
|
||||
increment: None
|
||||
@@ -41,27 +41,42 @@ Give Speckle a try in no time by:
|
||||
- [](https://speckle.guide/user/blender.html) reference on almost any end-user and developer functionality
|
||||
|
||||
|
||||
# Repo structure
|
||||
# Repo structure
|
||||
|
||||
This is the beginning of the Speckle SketchUp Connector. It is still in very early development and is not ready for general use.
|
||||
This repo is split into three parts:
|
||||
|
||||
This repo is split into two parts: `speckle_connector` which is the Ruby SketchUp plugin and `ui` which is the Vue frontend.
|
||||
### 1. **Speckle Connector extension**
|
||||
|
||||
Includes the `ruby` source files to run extension on SketchUp environment. SketchUp Extensions are composed of
|
||||
a **.rb** file as entry and **folder** that .rb file refers to. In our case entry file is `speckle_connector_3.rb`
|
||||
that responsible to register Speckle Connector extension to SketchUp and also it shows address to where extension
|
||||
will start to read extension. Source folder is `speckle_connector_3`.
|
||||
|
||||
## Usage
|
||||
### 2. **User Interface**
|
||||
|
||||
> NOTE: this connector is still in early development and isn't ready for general use.
|
||||
Includes the `Vue` frontend lives in the `ui` folder.
|
||||
|
||||
Copy the whole `speckle_connector` folder to you SketchUp Plugins folder. You will likely find this at:
|
||||
### 3. **SketchUp Sqlite3 extension** [submodule](https://github.com/specklesystems/sketchup-sqlite3)
|
||||
|
||||
C:\Users\{YOU}\AppData\Roaming\SketchUp\SketchUp 2021\SketchUp\Plugins
|
||||
Includes source codes of base `SQLite3` C/C++ library and `ruby` compiler files to be able to run SQLite3
|
||||
functionality on SketchUp in the same ruby module like `SpeckleConnector::Sqlite3::Database`. By this way
|
||||
we use extensions as native part of the source `ruby` code.
|
||||
|
||||
After building `sqlite3.sln` file, compiled `sqlite3.so` (for Windows) and `sqlite3.bundle` (for OSX) dynamic library files are created
|
||||
by solution to place them into source code into `speckle_connector_3/src/ext`. Building this project should be only
|
||||
happen when SketchUp starts to support newer Ruby versions (currently it is `2.7`).
|
||||
|
||||
You'll need to serve the ui before launching the connector:
|
||||
## Contribution Guide
|
||||
|
||||
cd ui
|
||||
npm install
|
||||
npm run serve
|
||||
Before start to contribute, it is better to understand how align with other contributors. It will make easier job
|
||||
of reviewer when you submit an issue or PR. If it is your first repo to contribute Speckle environment make sure that you read
|
||||
[Contribution Guideline](https://github.com/specklesystems/speckle-sharp/blob/main/.github/CONTRIBUTING.md).
|
||||
|
||||
Additionally as mentioned on [Repo Structure](#3-sketchup-sqlite3-extension-submodulehttpsgithubcomspecklesystemssketchup-sqlite3),
|
||||
this repo includes a submodule. Contributions on this source files should be done on the [sketchup-sqlite](https://github.com/specklesystems/sketchup-sqlite3)
|
||||
by creating issues and PRs on it. If it is your first time works with submodules, please read [git docs](https://git-scm.com/book/en/v2/Git-Tools-Submodules)
|
||||
briefly to get some insight about it.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -89,43 +104,77 @@ Clone this repo and run:
|
||||
|
||||
This will install all the necessary packages for the connector.
|
||||
|
||||
Next, install the Sketchup Ruby Debugger. You can find installation instructions [here](https://github.com/SketchUp/sketchup-ruby-debugger/blob/main/README.md). This will involve downloading the `dll` and copying it into the SketchUp installation directory:
|
||||
Next, install the Sketchup Ruby Debugger. You can find installation instructions
|
||||
[here](https://github.com/SketchUp/sketchup-ruby-debugger/blob/main/README.md).
|
||||
This will involve downloading the `dll` and copying it into the SketchUp installation
|
||||
directory:
|
||||
|
||||
C:\Program Files\SketchUp\SketchUp 2021\
|
||||
C:\Program Files\SketchUp\SketchUp 20XX\
|
||||
|
||||
You can now open up the repo in VS Code.
|
||||
You can now open up the repo in VS Code or you can use JetBrains' tools RubyMine and Webstorm.
|
||||
|
||||
Make sure you've installed the Ruby extension for VS Code.
|
||||
If you will use VS Code, make sure you've installed the Ruby extension for VS Code.
|
||||
|
||||
### Loading the Plugin
|
||||
#### RubyMine
|
||||
|
||||
To tell SketchUp to load the plugin from wherever you happen to be developing, you'll need to create a ruby file with the following contents:
|
||||
To debug:
|
||||
- Add configuration as **'Ruby remote debug'**
|
||||
- Remote host: localhost
|
||||
- Remote port: 7000
|
||||
- Remote root folder: <repo_path>
|
||||
- Local port: 26162
|
||||
- Local root folder: <repo_path>
|
||||
- Run below script
|
||||
|
||||
```ruby
|
||||
$LOAD_PATH << 'C:\YOUR\PATH\TO\THE\sketchup_connector'
|
||||
require 'speckle_connector.rb'
|
||||
```
|
||||
bundle exec skippy sketchup:debug 2024
|
||||
- When sketchup opened, click Debug button on RubyMine
|
||||
|
||||
Drop this Ruby file into your SketchUp Plugins directory. You will likely find this at:
|
||||
### Loading the Speckle Connector Plugin
|
||||
|
||||
C:\Users\{YOU}\AppData\Roaming\SketchUp\SketchUp 2021\SketchUp\Plugins
|
||||
1. Find already prepared `speckle_connector_3_loader.rb` file on the `_tools`
|
||||
folder.
|
||||
2. Copy this Ruby file into your SketchUp Plugins directory. You will likely find this at:
|
||||
`C:\Users\{YOU}\AppData\Roaming\SketchUp\SketchUp 20XX\SketchUp\Plugins`
|
||||
3. Update below line on the copied file with your local git file.
|
||||
```ruby
|
||||
speckle_path = File.join(home_folder, 'Git', 'Speckle', 'speckle-sketchup')
|
||||
```
|
||||
By this way SketchUp will directly read your local repository. Do not forget,
|
||||
this file also loads additional tools on the `_tools` folder.
|
||||
Those are will be only available on dev mode.
|
||||
|
||||
To reload the plugin while SketchUp is running, open up the Ruby console and run the following:
|
||||
Due to the fact that Ruby is interpreted language, so you can reload your file(s) when
|
||||
you changed them. There are different kinds of ways to reload them.
|
||||
|
||||
SpeckleSystems::SpeckleConnector.reload
|
||||
1. To reload the whole plugin files while SketchUp is running, open up the Ruby console
|
||||
and run the following:
|
||||
```ruby
|
||||
SpeckleConnector.reload
|
||||
```
|
||||
2. To reload only specific files, use `jf ruby toolbar` plugin that already available
|
||||
on SketchUp toolbar.
|
||||
|
||||
To run the `ui`, create a `.env` based on `.env-example` and paste in your Speckle token. Then:
|
||||
### User Interface
|
||||
|
||||
If it is your first time you cloned the project and willing to see Speckle UI, you
|
||||
should make sure that you compiled the `vue.js` project in the `ui` folder.
|
||||
|
||||
To run the `ui`, create a `.env` based on `.env-example` and paste in your
|
||||
Speckle token. Then:
|
||||
|
||||
cd ui
|
||||
npm run serve
|
||||
|
||||
### Debugging
|
||||
|
||||
To run SketchUp in debug mode, you will run the task specified in `tasks.json`. Before you do this, make sure your integrated shell for tasks is using powershell. You can specify this by adding the following option to your workspace's `settings.json`
|
||||
To run SketchUp in debug mode, you will run the task specified in `tasks.json`.
|
||||
Before you do this, make sure your integrated shell for tasks is using powershell.
|
||||
You can specify this by adding the following option to your workspace's `settings.json`
|
||||
|
||||
"terminal.integrated.automationShell.windows": "powershell.exe",
|
||||
|
||||
To start the task, use the keyboard shortcut `ctrl` + `shift` + `p` to open up the Command Palette. Search for `Tasks: Run Task` and select it:
|
||||
To start the task, use the keyboard shortcut `ctrl` + `shift` + `p` to open up
|
||||
the Command Palette. Search for `Tasks: Run Task` and select it:
|
||||
|
||||

|
||||
|
||||
@@ -133,9 +182,30 @@ Then choose the `Debug Sketchup 2021` task to run it:
|
||||
|
||||

|
||||
|
||||
Once Sketchup has launched, start the `Listen for rdebug-ide` debug configuration. Once the debugger has connected, you'll be able to debug the connector normally.
|
||||
Once Sketchup has launched, start the `Listen for rdebug-ide` debug configuration.
|
||||
Once the debugger has connected, you'll be able to debug the connector normally.
|
||||
|
||||
Make sure you run the `ui` before starting the SketchUp Connector
|
||||
|
||||
cd ui
|
||||
npm run serve
|
||||
npm run serve
|
||||
|
||||
### Code Quality
|
||||
|
||||
Tracking your code quality before merging any code to `main` branch might not seem at the
|
||||
first time crucial, but when repo became huge, you might have many spaghetti code and technical
|
||||
depth. It is always better to keep your work tough from the beginning. For this reason some
|
||||
workflows have already setup on CI, those workflows must be passed before considering to
|
||||
merge.
|
||||
|
||||
To track your code quality locally,
|
||||
|
||||
1. Make sure that you do not have any RuboCop issue, run below
|
||||
```ruby
|
||||
bundle exec rake
|
||||
```
|
||||
|
||||
2. To check overall state of repository by RubyCritic, run below
|
||||
```ruby
|
||||
rake rubycritic
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rake/testtask'
|
||||
require 'rubocop/rake_task'
|
||||
require 'rubycritic/rake_task'
|
||||
|
||||
module SpeckleConnector
|
||||
# Custom utility functions for rake tasks
|
||||
module RakeUtils
|
||||
module_function
|
||||
|
||||
# Find ruby files that were changed from `main` to the latest revision
|
||||
def changed_rb_files(previous_revision: 'main', latest_revision: '')
|
||||
range = latest_revision.empty? ? previous_revision : "#{latest_revision}..#{previous_revision}"
|
||||
command = "git diff #{range} --name-only"
|
||||
changed_files = `#{command}`.split("\n")
|
||||
# filter changed files with ruby files (.rb), Gemfile and Rakefile.
|
||||
filtered_files = changed_files.grep(/.*\.rb$|Gemfile|Rakefile/)
|
||||
filtered_files.select { |file| File.exist?(file) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add default rubocop task
|
||||
RuboCop::RakeTask.new(:default)
|
||||
|
||||
# Add task to only verify ruby files that are different than in the `main` branch
|
||||
desc('Run rubocop on changed files')
|
||||
RuboCop::RakeTask.new(:rubocop_changed) do |t|
|
||||
t.patterns = FileList.new(SpeckleConnector::RakeUtils.changed_rb_files)
|
||||
end
|
||||
|
||||
# Glob pattern to match source files. Defaults to FileList['.'].
|
||||
ruby_critic_paths = FileList[
|
||||
'speckle_connector_3/**/*.rb',
|
||||
'speckle_connector_3.rb',
|
||||
'tests/**/*.rb'] -
|
||||
FileList[
|
||||
'_tools/**/*.rb',
|
||||
'speckle_connector_3/src/ext/**/*.rb',
|
||||
]
|
||||
|
||||
# for local
|
||||
RubyCritic::RakeTask.new('rubycritic') do |task|
|
||||
task.paths = ruby_critic_paths
|
||||
end
|
||||
|
||||
# for CI
|
||||
RubyCritic::RakeTask.new('rubycritic-ci') do |task|
|
||||
task.options = '--mode-ci --format console --no-browser --branch main'
|
||||
task.paths = ruby_critic_paths
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
# Tools
|
||||
|
||||
This folder stores the external tools and helper scripts to make easier life of the developer,
|
||||
they are not the part of the main functionality of the Speckle.
|
||||
|
||||
Tools and scripts inside the folder will be loaded with `sketchup_connector_loader.rb` file.
|
||||
In order to load your own `.rb` files please add this file names into list in the loader.
|
||||
|
||||
````ruby
|
||||
...
|
||||
|
||||
files = %w[speckle_connector jf_RubyPanel su_attributes <put-your-file-here>]
|
||||
# This line placed before loading started.
|
||||
|
||||
files.each do |ruby_file|
|
||||
puts "Loading #{ruby_file}"
|
||||
begin
|
||||
require ruby_file
|
||||
rescue LoadError
|
||||
puts "Could not load #{ruby_file}"
|
||||
end
|
||||
end
|
||||
````
|
||||
|
||||
Track load status of your tools and scripts on the ruby console when SketchUp UI initializing.
|
||||
@@ -0,0 +1,15 @@
|
||||
# This is for automated pre-debugger configuration.
|
||||
# We run skippy first, then activate debugger.
|
||||
# The purpose of this file to wait till skp is live
|
||||
|
||||
# To establish a configuration
|
||||
# 1. Create 'Run External Tool' before lunch step
|
||||
# 2. Program -> C:\Ruby32-x64\bin\ruby.exe or whatever
|
||||
# 3. Arguments -> C:\Users\KORAL\Documents\Git\Speckle\speckle-sketchup\_tools\debugger\bundle_exec_2024.rb or whatever
|
||||
# 4. Working directory -> C:\Users\KORAL\Documents\Git\Speckle\speckle-sketchup or whatever
|
||||
|
||||
# Add a delay of 10 seconds, it is arbitrary, do not hesitate to change for what works best for you
|
||||
sleep(10)
|
||||
|
||||
# Execute the original command
|
||||
exec('bundle exec skippy sketchup:debug 2024')
|
||||
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# #-------------------------------------------------------------------------------------------------
|
||||
# *************************************************************************************************
|
||||
# RubyPanel Toolbar (C) 2007 jim.foltz@gmail.com
|
||||
#
|
||||
# With special thanks to Chris Phillips (Sketchy Physics)
|
||||
# for the Win32API code examples.
|
||||
#
|
||||
# 2011-01-05 <jim.foltz@gmail.com>
|
||||
# * Changed Toolbar name from "Ruby COnsole" to "Ruby Toolbar" (TT)
|
||||
# http://forums.sketchucation.com/viewtopic.php?f=323&t=1542&p=298587#p298587
|
||||
# * Wrapped in addition module RubyToolbar
|
||||
# * Use $suString.GetSting to get proper "Ruby Console" name string.
|
||||
# * Better check if TB was previously visible
|
||||
# * Use UI.start_timer to restore Toolbar
|
||||
# ICONS: located in the subfolder "rubytoolbar"
|
||||
# MODIFICATION: by Fredo6 for compliance with SU 2014 (and no dependency on Win32API) - 18 Sep 2013
|
||||
# *************************************************************************************************
|
||||
#-------------------------------------------------------------------------------------------------
|
||||
|
||||
require 'sketchup'
|
||||
require 'extensions'
|
||||
|
||||
ext = SketchupExtension.new('Ruby Toolbar', 'jf_RubyPanel/rubytoolbar.rb')
|
||||
ext.creator = 'Jim Foltz <jim.foltz@gmail.com>'
|
||||
ext.description = 'Toolbar for manipulating the Ruby Console. Compatible with SketchUp 2014'
|
||||
ext.version = '2014'
|
||||
Sketchup.register_extension(ext, true)
|
||||
|
After Width: | Height: | Size: 934 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 811 B |
|
After Width: | Height: | Size: 1006 B |
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,89 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
# RubyPanel Toolbar (C) 2007 jim.foltz@gmail.com
|
||||
|
||||
# Permission to use, copy, modify, and distribute this software for # any purpose and without fee is hereby granted,
|
||||
# provided that the above copyright notice appear in all copies.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION,
|
||||
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
# Description: Manage the loading of Ruby files and display of the Ruby console
|
||||
# CREDITS: Special thanks to Chris Phillips (Sketchy Physics) for the Win32API code examples
|
||||
# Revision: 3 Aug 2009, by Fredo6
|
||||
# ICONS: located in the subfolder "rubytoolbar"
|
||||
# MODIFICATION: by Fredo6 for compliance with SU 2014 (and no dependency on Win32API) - 18 Sep 2013
|
||||
#-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
require 'English'
|
||||
require 'sketchup'
|
||||
|
||||
module JF_RubyToolbar
|
||||
# Load the toolbar icons and commands, and do some initialization
|
||||
def self.load_toolbar
|
||||
@last_dir = "#{$LOAD_PATH[0]}/"
|
||||
@last_dir = @last_dir.gsub('/', '\\\\\\\\')
|
||||
@last_dir = File.join($JF_RUBYTOOLBAR, 'speckle_connector_3')
|
||||
curdir = File.dirname __FILE__
|
||||
|
||||
# create toolbar
|
||||
tb = UI::Toolbar.new 'Ruby Toolbar'
|
||||
|
||||
# Toggle console
|
||||
cmd = UI::Command.new('Show/Hide') { SKETCHUP_CONSOLE.visible? ? SKETCHUP_CONSOLE.hide : SKETCHUP_CONSOLE.show }
|
||||
cmd.large_icon = cmd.small_icon = File.join(curdir, 'rubypanel.png')
|
||||
cmd.status_bar_text = cmd.tooltip = 'Show/Hide Ruby Console'
|
||||
tb.add_item cmd
|
||||
|
||||
# Clear Console
|
||||
cmd = UI::Command.new('Clear') { SKETCHUP_CONSOLE.clear }
|
||||
cmd.status_bar_text = cmd.tooltip = 'Clear Console'
|
||||
cmd.large_icon = cmd.small_icon = File.join(curdir, 'Delete24.png')
|
||||
tb.add_item cmd
|
||||
|
||||
# Load a Ruby script
|
||||
cmd = UI::Command.new('LoadScript') { load_script }
|
||||
cmd.large_icon = cmd.small_icon = File.join(curdir, 'doc_ruby.png')
|
||||
cmd.tooltip = cmd.status_bar_text = 'Load Script'
|
||||
tb.add_item cmd
|
||||
|
||||
# Reload the last Ruby Script
|
||||
@cmd_reload = UI::Command.new('Reload') { load_script @last_file }
|
||||
@cmd_reload.large_icon = @cmd_reload.small_icon = File.join(curdir, 'reload.png')
|
||||
@cmd_reload.status_bar_text = @cmd_reload.tooltip = 'Reload Script'
|
||||
tb.add_item @cmd_reload
|
||||
|
||||
# Open the SU plugins directory panel
|
||||
cmd = UI::Command.new('PluginsDir') { UI.openURL @last_dir }
|
||||
cmd.tooltip = cmd.status_bar_text = 'Browse Plugins Folder'
|
||||
cmd.large_icon = cmd.small_icon = File.join(curdir, 'open_folder.png')
|
||||
tb.add_item cmd
|
||||
|
||||
# showing the toolbar
|
||||
tb.get_last_state == -1 ? tb.show : tb.restore
|
||||
end
|
||||
|
||||
# Load a script file - if <file> is nil, open the dialog panel to select the file
|
||||
def self.load_script(file = nil)
|
||||
file ||= UI.openpanel 'Load Script', @last_dir, '*.rb*'
|
||||
return unless file
|
||||
|
||||
begin
|
||||
load file
|
||||
Sketchup.set_status_text "#{File.basename(file)} loaded (#{Time.now.strftime('%H:%M:%S')})"
|
||||
@last_file = file
|
||||
@last_dir = "#{File.dirname(file)}/"
|
||||
@last_dir = @last_dir.gsub('/', '\\\\\\\\')
|
||||
@cmd_reload.status_bar_text = @cmd_reload.tooltip = "Reload Script: #{File.basename(file)}"
|
||||
rescue StandardError
|
||||
UI.messagebox("Couldn't load #{File.basename(file)}: #{$ERROR_INFO}")
|
||||
end
|
||||
end
|
||||
|
||||
# Loading the toolbar once
|
||||
unless file_loaded?('RubyToolbar.rb')
|
||||
load_toolbar
|
||||
file_loaded('RubyToolbar.rb')
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# The purpose of this file is customizing environment of the developer on SketchUp.
|
||||
# Each developer can customize it's own loader(this file), by this way developer can load their helper tools
|
||||
# and helper methods ONLY in dev mode.
|
||||
|
||||
# Change the base folder and copy this file to Sketchup Plugins directory
|
||||
# If you need to test in several versions of SketchUp, create symlinks to this file
|
||||
# ( AppData\Roaming\SketchUp\SketchUp <version>\SketchUp\Plugins )
|
||||
# Create a link to Plugins folder with this command
|
||||
|
||||
# rubocop:disable Layout/LineLength
|
||||
# New-Item -ItemType SymbolicLink -Path '~\AppData\Roaming\SketchUp\SketchUp 2022\SketchUp\Plugins\speckle_connector_3_loader.rb' -Target ~\Git\Speckle\speckle-sketchup\_tools\speckle_connector_3_loader.rb
|
||||
# rubocop:enable Layout/LineLength
|
||||
|
||||
SKETCHUP_CONSOLE.show # if you want to show Ruby console on startup
|
||||
# base location of your repos - will be merged with specific repos in next step
|
||||
home_folder = File.expand_path('~')
|
||||
# If you use some other location for your repository, you can change it here
|
||||
# but make sure it is not committed as it will change thi setting for all
|
||||
# users that use the default setup. Eg:
|
||||
|
||||
# Add Speckle folder - uncomment the one you need
|
||||
speckle_path = File.join(home_folder, 'Git', 'Speckle', 'speckle-sketchup')
|
||||
|
||||
$LOAD_PATH << speckle_path
|
||||
$LOAD_PATH << File.join(speckle_path, '_tools')
|
||||
|
||||
# Defining this path will help to tool to browse related source file directly when
|
||||
# developer attempted to reload/load file.
|
||||
# rubocop:disable Style/GlobalVars
|
||||
$JF_RUBYTOOLBAR = speckle_path
|
||||
# rubocop:enable Style/GlobalVars
|
||||
|
||||
files = %w[speckle_connector_3 jf_RubyPanel su_attributes]
|
||||
|
||||
files.each do |ruby_file|
|
||||
puts "Loading #{ruby_file}"
|
||||
begin
|
||||
require ruby_file
|
||||
rescue LoadError
|
||||
puts "Could not load #{ruby_file}"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright 2014-2021, Trimble Inc.
|
||||
#
|
||||
# License: The MIT License (MIT)
|
||||
#
|
||||
# A SketchUp Ruby Extension that surfaces attributes attached to components.
|
||||
# More info at https://github.com/SketchUp/sketchup-attribute-helper
|
||||
|
||||
|
||||
require 'sketchup.rb'
|
||||
require 'extensions.rb'
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
module Trimble
|
||||
module AttributeHelper
|
||||
|
||||
### CONSTANTS ### ------------------------------------------------------------
|
||||
|
||||
# Plugin information
|
||||
PLUGIN_ID = 'AttributeHelper'.freeze
|
||||
PLUGIN_NAME = 'SketchUp Attribute Helper'.freeze
|
||||
PLUGIN_VERSION = '1.0.3'.freeze
|
||||
|
||||
# Resource paths
|
||||
FILENAMESPACE = File.basename(__FILE__, '.*')
|
||||
PATH_ROOT = File.dirname(__FILE__).freeze
|
||||
PATH = File.join(PATH_ROOT, FILENAMESPACE).freeze
|
||||
|
||||
|
||||
### EXTENSION ### ------------------------------------------------------------
|
||||
|
||||
unless file_loaded?(__FILE__)
|
||||
loader = File.join( PATH, 'core.rb' )
|
||||
ex = SketchupExtension.new(PLUGIN_NAME, loader)
|
||||
ex.description = 'Visually inspect nested attributes in SketchUp.'
|
||||
ex.version = PLUGIN_VERSION
|
||||
ex.copyright = 'Trimble Inc © 2015-2021'
|
||||
ex.creator = 'SketchUp'
|
||||
Sketchup.register_extension(ex, true)
|
||||
end
|
||||
|
||||
end # module AttributeHelper
|
||||
end # module Trimble
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
file_loaded(__FILE__)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,285 @@
|
||||
# Copyright 2014-2021, Trimble Inc.
|
||||
#
|
||||
# License: The MIT License (MIT)
|
||||
|
||||
require "sketchup.rb"
|
||||
require "stringio"
|
||||
|
||||
module Trimble
|
||||
module AttributeHelper
|
||||
|
||||
PLUGIN = self
|
||||
|
||||
class << self
|
||||
attr_reader :app_observer
|
||||
attr_reader :model_observer
|
||||
attr_reader :selection_observer
|
||||
end
|
||||
|
||||
|
||||
def self.visualize_selected
|
||||
content = self.traverse_selected
|
||||
html = self.wrap_content(content)
|
||||
|
||||
options = {
|
||||
:dialog_title => "Attribute Visualizer",
|
||||
:preferences_key => 'AttributeVisualizer',
|
||||
:scrollable => true,
|
||||
:resizable => true,
|
||||
:height => 300,
|
||||
:width => 400,
|
||||
:left => 200,
|
||||
:top => 200
|
||||
}
|
||||
@window ||= UI::WebDialog.new(options)
|
||||
@window.set_html(html)
|
||||
@window.set_on_close {
|
||||
@window = nil
|
||||
self.detach_observers
|
||||
}
|
||||
unless @window.visible?
|
||||
@window.show
|
||||
self.attach_observers
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.attach_observers
|
||||
@app_observer ||= AppObserver.new
|
||||
@model_observer ||= ModelObserver.new
|
||||
@selection_observer ||= SelectionObserver.new
|
||||
model = Sketchup.active_model
|
||||
Sketchup.remove_observer(@app_observer)
|
||||
model.remove_observer(@model_observer)
|
||||
model.selection.remove_observer(@selection_observer)
|
||||
Sketchup.add_observer(@app_observer)
|
||||
model.add_observer(@model_observer)
|
||||
model.selection.add_observer(@selection_observer)
|
||||
end
|
||||
|
||||
|
||||
def self.detach_observers
|
||||
Sketchup.remove_observer(@app_observer)
|
||||
Sketchup.active_model.remove_observer(@model_observer)
|
||||
Sketchup.active_model.selection.remove_observer(@selection_observer)
|
||||
end
|
||||
|
||||
|
||||
def self.traverse_selected
|
||||
html = StringIO.new
|
||||
|
||||
model = Sketchup.active_model
|
||||
selection = model.selection
|
||||
|
||||
if selection.empty?
|
||||
if model.active_path.nil?
|
||||
entity = model
|
||||
else
|
||||
entity = model.active_path.last
|
||||
end
|
||||
else
|
||||
return "Invalid selection size" unless selection.size == 1
|
||||
entity = selection[0]
|
||||
end
|
||||
|
||||
html.puts "<h1>#{self.escape_html(entity)}</h1>"
|
||||
if entity.respond_to?(:name)
|
||||
html.puts "<h2>#{self.escape_html(entity.name)}</h2>"
|
||||
end
|
||||
if entity.attribute_dictionaries
|
||||
entity.attribute_dictionaries.each { |dictionary|
|
||||
html.puts self.format_dictionary(dictionary)
|
||||
}
|
||||
else
|
||||
html.puts "No dictionaries"
|
||||
end
|
||||
|
||||
if entity.is_a?(Sketchup::Group)
|
||||
definition = entity.entities.parent
|
||||
elsif entity.is_a?(Sketchup::ComponentInstance)
|
||||
definition = entity.definition
|
||||
else
|
||||
definition = nil
|
||||
end
|
||||
|
||||
if definition && definition.attribute_dictionaries
|
||||
html.puts "<h1>#{self.escape_html(definition)}</h1>"
|
||||
html.puts "<h2>#{self.escape_html(definition.name)}</h2>"
|
||||
definition.attribute_dictionaries.each { |dictionary|
|
||||
html.puts self.format_dictionary(dictionary)
|
||||
}
|
||||
end
|
||||
|
||||
html.string
|
||||
end
|
||||
|
||||
|
||||
def self.format_dictionary(dictionary, path = "")
|
||||
html_name = self.escape_html(dictionary.name)
|
||||
path = "#{path}:#{html_name}"
|
||||
html = StringIO.new
|
||||
html.puts "<table>"
|
||||
html.puts "<caption title='#{path}'>#{html_name}</caption>"
|
||||
html.puts "<tbody>"
|
||||
dictionary.each { |key, value|
|
||||
html_key = self.escape_html(key)
|
||||
html_value = self.escape_html(value)
|
||||
node_path = "#{path}:#{html_key}"
|
||||
html.puts "<tr title='#{node_path}'><td>#{html_key}</td><td>#{html_value}</td><td class='value_type'>#{value.class}</td></tr>"
|
||||
}
|
||||
if dictionary.attribute_dictionaries
|
||||
dictionary.attribute_dictionaries.each { |sub_dic|
|
||||
html.puts "<tr><td colspan='3' class='dictionary'>"
|
||||
html.puts self.format_dictionary(sub_dic, path)
|
||||
html.puts "</td></tr>"
|
||||
}
|
||||
end
|
||||
html.puts "</tbody>"
|
||||
html.puts "</table>"
|
||||
html.string
|
||||
end
|
||||
|
||||
|
||||
def self.escape_html(data)
|
||||
data.to_s.gsub("&", "&").gsub("<", "<").gsub(">", ">")
|
||||
end
|
||||
|
||||
|
||||
def self.wrap_content(content)
|
||||
html = <<-EOT
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
html {
|
||||
font-family: "Calibri", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
/*padding: 0.5em;*/
|
||||
border: 1px solid #666;
|
||||
}
|
||||
caption {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
/*border-bottom: 1px solid silver;*/
|
||||
padding: 0.2em;
|
||||
}
|
||||
td {
|
||||
background: #f3f3f3;
|
||||
padding: 0.2em;
|
||||
}
|
||||
td.dictionary {
|
||||
background: none;
|
||||
padding-left: 1em;
|
||||
}
|
||||
tr:hover td {
|
||||
background: rgba(255,210,180,0.2);
|
||||
}
|
||||
.value_type {
|
||||
text-align: right;
|
||||
width: 5%;
|
||||
}
|
||||
</style>
|
||||
<head>
|
||||
<body>
|
||||
#{content}
|
||||
</body>
|
||||
</html>
|
||||
EOT
|
||||
end
|
||||
|
||||
|
||||
class SelectionObserver < Sketchup::SelectionObserver
|
||||
def onSelectionAdded(selection, element)
|
||||
selection_changed()
|
||||
end
|
||||
def onSelectionBulkChange(selection)
|
||||
selection_changed()
|
||||
end
|
||||
def onSelectionCleared(selection)
|
||||
selection_changed()
|
||||
end
|
||||
def onSelectionRemoved(selection, element)
|
||||
selection_changed()
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def selection_changed
|
||||
PLUGIN.visualize_selected
|
||||
end
|
||||
end # class SelectionObserver
|
||||
|
||||
|
||||
class ModelObserver < Sketchup::ModelObserver
|
||||
def onActivePathChanged(model)
|
||||
PLUGIN.visualize_selected
|
||||
end
|
||||
|
||||
def onTransactionCommit(model)
|
||||
model_changed(model)
|
||||
end
|
||||
def onTransactionEmpty(model)
|
||||
model_changed(model)
|
||||
end
|
||||
def onTransactionRedo(model)
|
||||
model_changed(model)
|
||||
end
|
||||
def onTransactionUndo(model)
|
||||
model_changed(model)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model_changed(model)
|
||||
if @timer.nil?
|
||||
@timer = UI.start_timer(0.0, false) {
|
||||
@timer = nil
|
||||
PLUGIN.visualize_selected
|
||||
}
|
||||
end
|
||||
end
|
||||
end # class ModelObserver
|
||||
|
||||
|
||||
class AppObserver < Sketchup::AppObserver
|
||||
def onNewModel(model)
|
||||
observe_model(model)
|
||||
end
|
||||
def onOpenModel(model)
|
||||
observe_model(model)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def observe_model(model)
|
||||
model.add_observer(PLUGIN.model_observer)
|
||||
model.selection.add_observer(PLUGIN.selection_observer)
|
||||
PLUGIN.visualize_selected
|
||||
end
|
||||
end # class AppObserver
|
||||
|
||||
|
||||
unless file_loaded?(__FILE__)
|
||||
command = UI::Command.new("Attribute Helper") { self.visualize_selected }
|
||||
command.status_bar_text = "Inspect and edit the attributes of a selection."
|
||||
|
||||
menu_name = Sketchup.version.to_f < 21.1 ? 'Plugins' : 'Developer'
|
||||
menu = UI.menu(menu_name)
|
||||
menu.add_item(command)
|
||||
file_loaded(__FILE__)
|
||||
end
|
||||
|
||||
|
||||
end # module AttributeHelper
|
||||
end # module Sketchup
|
||||
@@ -0,0 +1,2 @@
|
||||
ID=f4d9d053-4479-4a9a-90da-b79fa16e28c4
|
||||
VERSION_ID=b787af5e-8e8e-4932-92ef-a3c99681795d
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
|
||||
def patch_connector(tag):
|
||||
"""Patches the connector version within the connector file"""
|
||||
rb_file = "speckle_connector.rb"
|
||||
rb_file = "speckle_connector_3.rb"
|
||||
|
||||
with open(rb_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
@@ -15,6 +15,12 @@ def patch_connector(tag):
|
||||
print(f"Patched connector version number in {rb_file}")
|
||||
break
|
||||
|
||||
for (index, line) in enumerate(lines):
|
||||
if 'DEV_MODE = ' in line:
|
||||
lines[index] = f' DEV_MODE = false\n'
|
||||
print(f"Patched dev mode to false in {rb_file}")
|
||||
break
|
||||
|
||||
with open(rb_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
@@ -25,7 +31,8 @@ def patch_installer(tag):
|
||||
|
||||
with open(iss_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
lines.insert(11, f'#define AppVersion "{tag}"\n')
|
||||
lines.insert(11, f'#define AppVersion "{tag.split("-")[0]}"\n')
|
||||
lines.insert(12, f'#define AppInfoVersion "{tag}"\n')
|
||||
|
||||
with open(iss_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
@@ -39,12 +46,12 @@ def main():
|
||||
return
|
||||
|
||||
tag = sys.argv[1]
|
||||
if not re.match(r"[0-9]+(\.[0-9]+)*$", tag):
|
||||
if not re.match(r"([0-9]+)\.([0-9]+)\.([0-9]+)", tag):
|
||||
raise ValueError(f"Invalid tag provided: {tag}")
|
||||
|
||||
print(f"Patching version: {tag}")
|
||||
patch_connector(tag)
|
||||
patch_installer(tag)
|
||||
# patch_installer(tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
require "sketchup"
|
||||
|
||||
require "extensions"
|
||||
|
||||
module SpeckleSystems
|
||||
module SpeckleConnector
|
||||
# Version - patched by CI
|
||||
CONNECTOR_VERSION = "0.0.0"
|
||||
|
||||
file = __FILE__.dup
|
||||
# Account for Ruby encoding bug under Windows.
|
||||
file.force_encoding("UTF-8") if file.respond_to?(:force_encoding)
|
||||
# Support folder should be named the same as the root .rb file.
|
||||
folder_name = File.basename(file, ".*")
|
||||
|
||||
# Path to the root .rb file (this file).
|
||||
PATH_ROOT = File.dirname(file).freeze
|
||||
|
||||
# Path to the support folder.
|
||||
PATH = File.join(PATH_ROOT, folder_name).freeze
|
||||
|
||||
# Run from localhost or from build files
|
||||
DEV_MODE = false
|
||||
puts("Loading Speckle Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
|
||||
|
||||
|
||||
unless file_loaded?(__FILE__)
|
||||
|
||||
ex = SketchupExtension.new("Speckle SketchUp", File.join(PATH, "main"))
|
||||
|
||||
ex.description = "Speckle Connector for SketchUp"
|
||||
|
||||
ex.version = CONNECTOR_VERSION
|
||||
|
||||
ex.copyright = "AEC Systems Ltd."
|
||||
|
||||
ex.creator = "Speckle Systems"
|
||||
|
||||
Sketchup.register_extension(ex, true)
|
||||
|
||||
file_loaded(__FILE__)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,57 +0,0 @@
|
||||
require "JSON"
|
||||
|
||||
begin
|
||||
require("sqlite3")
|
||||
rescue LoadError
|
||||
# ty msp-greg! https://github.com/MSP-Greg/SUMisc/releases/tag/sqlite3-mingw-1
|
||||
Gem.install(File.join(File.dirname(File.expand_path(__FILE__)), "utils/sqlite3-1.4.2.mspgreg-x64-mingw32.gem"))
|
||||
require("sqlite3")
|
||||
end
|
||||
|
||||
module SpeckleSystems::SpeckleConnector
|
||||
module Accounts
|
||||
def self.load_accounts
|
||||
dir = _get_speckle_dir
|
||||
db_path = File.join(dir, "Accounts.db")
|
||||
unless File.exist?(db_path)
|
||||
raise(IOError, "No Accounts db found. Please read the guide for different options for adding your account: \nhttps://speckle.guide/user/manager.html#adding-accounts")
|
||||
end
|
||||
|
||||
db = SQLite3::Database.new(db_path)
|
||||
rows = db.execute("SELECT * FROM objects")
|
||||
db.close
|
||||
rows.map { |row| JSON.parse(row[1]) }
|
||||
end
|
||||
|
||||
def self.default_account
|
||||
accts = load_accounts
|
||||
accts.select { |acc| acc["isDefault"] }[0] || accts[0]
|
||||
end
|
||||
|
||||
def self.get_suuid
|
||||
dir = _get_speckle_dir
|
||||
suuid_path = File.join(dir, "suuid")
|
||||
return unless File.exist?(suuid_path)
|
||||
|
||||
File.read(suuid_path)
|
||||
end
|
||||
|
||||
def self._get_speckle_dir
|
||||
speckle_dir =
|
||||
case Sketchup.platform
|
||||
# sometimes Dir.home on windows points somewhere else bc I guess it's picking up a higher level user?
|
||||
when :platform_win then File.join(Dir.pwd[%r{^((?:[^/]*/){3})}], "AppData/Roaming/Speckle")
|
||||
when :platform_osx then File.join(Dir.home, ".config", "Speckle")
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
return speckle_dir if Dir.exist?(speckle_dir)
|
||||
|
||||
raise(
|
||||
IOError,
|
||||
"No Speckle Directory exists. Please read the guide to get Speckle set up on your machine: \nhttps://speckle.guide/user/manager.html"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,32 +0,0 @@
|
||||
require "sketchup"
|
||||
require "speckle_connector/converter/to_speckle"
|
||||
require "speckle_connector/converter/to_native"
|
||||
|
||||
module SpeckleSystems::SpeckleConnector
|
||||
SKETCHUP_UNIT_STRINGS = { "m" => "m", "mm" => "mm", "ft" => "feet", "in" => "inch", "yd" => "yard", "cm" => "cm" }.freeze
|
||||
public_constant :SKETCHUP_UNIT_STRINGS
|
||||
class ConverterSketchup
|
||||
include ToNative
|
||||
include ToSpeckle
|
||||
|
||||
attr_accessor :units, :component_defs, :registry, :entity_observer
|
||||
|
||||
def initialize(units = "m")
|
||||
@units = units
|
||||
@component_defs = {}
|
||||
# @registry = Sketchup.active_model.attribute_dictionary("speckle_id_registry", true)
|
||||
# @entity_observer = SpeckleEntityObserver.new
|
||||
end
|
||||
|
||||
def convert_to_speckle(obj)
|
||||
case obj.typename
|
||||
when "Edge" then edge_to_speckle(obj)
|
||||
when "Face" then face_to_speckle(obj)
|
||||
when "Group" then component_instance_to_speckle(obj, is_group: true)
|
||||
when "ComponentDefinition" then component_definition_to_speckle(obj)
|
||||
when "ComponentInstance" then component_instance_to_speckle(obj)
|
||||
else nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
module SpeckleSystems::SpeckleConnector
|
||||
class SpeckleEntityObserver < Sketchup::EntityObserver
|
||||
attr_accessor :registry
|
||||
|
||||
def initialize
|
||||
super()
|
||||
@registry = Sketchup.active_model.attribute_dictionary("speckle_id_registry", true)
|
||||
end
|
||||
|
||||
def onEraseEntity(entity)
|
||||
app_id = entity.get_attribute("speckle", "applicationId")
|
||||
return if app_id.nil?
|
||||
|
||||
p(app_id)
|
||||
|
||||
@registry.delete_key(app_id)
|
||||
|
||||
p(@registry)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,243 +0,0 @@
|
||||
require "sketchup"
|
||||
|
||||
# To Native conversions for the ConverterSketchup
|
||||
module SpeckleSystems::SpeckleConnector::ToNative
|
||||
def traverse_commit_object(obj)
|
||||
if can_convert_to_native(obj)
|
||||
convert_to_native(obj, Sketchup.active_model.entities)
|
||||
elsif obj.is_a?(Hash) && obj.key?("speckle_type")
|
||||
return if is_ignored_speckle_type(obj)
|
||||
|
||||
puts(">>> Found #{obj["speckle_type"]}: #{obj["id"]}")
|
||||
props = obj.keys.filter_map { |key| key unless key.start_with?("_") }
|
||||
props.each { |prop| traverse_commit_object(obj[prop]) }
|
||||
elsif obj.is_a?(Hash)
|
||||
obj.each_value { |value| traverse_commit_object(value) }
|
||||
elsif obj.is_a?(Array)
|
||||
obj.each { |value| traverse_commit_object(value) }
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def can_convert_to_native(obj)
|
||||
return false unless obj.is_a?(Hash) && obj.key?("speckle_type")
|
||||
|
||||
[
|
||||
"Objects.Geometry.Line",
|
||||
"Objects.Geometry.Polyline",
|
||||
"Objects.Geometry.Mesh",
|
||||
"Objects.Geometry.Brep",
|
||||
"Objects.Other.BlockInstance",
|
||||
"Objects.Other.BlockDefinition",
|
||||
"Objects.Other.RenderMaterial"
|
||||
].include?(obj["speckle_type"])
|
||||
end
|
||||
|
||||
def is_ignored_speckle_type(obj)
|
||||
["Objects.BuiltElements.Revit.Parameter"].include?(obj["speckle_type"])
|
||||
end
|
||||
|
||||
def convert_to_native(obj, entities = Sketchup.active_model.entities)
|
||||
puts(">>> Converting #{obj["speckle_type"]}: #{obj["id"]}")
|
||||
|
||||
case obj["speckle_type"]
|
||||
when "Objects.Geometry.Line", "Objects.Geometry.Polyline" then if entities == Sketchup.active_model.entities
|
||||
edge_to_native_component(obj, entities)
|
||||
else
|
||||
edge_to_native(obj, entities)
|
||||
end
|
||||
when "Objects.Other.BlockInstance" then component_instance_to_native(obj, entities)
|
||||
when "Objects.Other.BlockDefinition" then component_definition_to_native(obj)
|
||||
when "Objects.Geometry.Mesh" then if entities == Sketchup.active_model.entities
|
||||
mesh_to_native_component(obj, entities)
|
||||
else
|
||||
mesh_to_native(obj, entities)
|
||||
end
|
||||
when "Objects.Geometry.Brep" then if entities == Sketchup.active_model.entities
|
||||
mesh_to_native_component(obj["displayMesh"], entities)
|
||||
else
|
||||
mesh_to_native(obj["displayMesh"], entities)
|
||||
end
|
||||
when obj.key?["displayValue"]
|
||||
parent_id = obj["applicationId"] || obj["id"]
|
||||
obj["displayValue"].each do |o|
|
||||
o["applicationId"] = "#{parent_id}::#{o["id"]}" if o["applicationId"].nil?
|
||||
convert_to_native(o, entities)
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
rescue StandardError => e
|
||||
puts("Failed to convert #{obj["speckle_type"]} (id: #{obj["id"]})")
|
||||
puts(e)
|
||||
nil
|
||||
end
|
||||
|
||||
# def register_receive(entity, id)
|
||||
# # entity.add_observer(@entity_observer)
|
||||
# # entity.set_attribute("speckle", "applicationId", id)
|
||||
# @registry[id] = entity.persistent_id
|
||||
# end
|
||||
|
||||
# def get_received_entity(app_id)
|
||||
# return if @registry[app_id].nil?
|
||||
# end
|
||||
|
||||
# def received?(id)
|
||||
# !@registry[id].nil?
|
||||
# end
|
||||
|
||||
def length_to_native(length, units = @units)
|
||||
length.__send__(SpeckleSystems::SpeckleConnector::SKETCHUP_UNIT_STRINGS[units])
|
||||
end
|
||||
|
||||
def edge_to_native(line, entities)
|
||||
if line.key?("value")
|
||||
values = line["value"]
|
||||
points = values.each_slice(3).to_a.map { |pt| point_to_native(pt[0], pt[1], pt[2], line["units"]) }
|
||||
points.push(points[0]) if line["closed"]
|
||||
entities.add_edges(*points)
|
||||
else
|
||||
start_pt = point_to_native(line["start"]["x"], line["start"]["y"], line["start"]["z"], line["units"])
|
||||
end_pt = point_to_native(line["end"]["x"], line["end"]["y"], line["end"]["z"], line["units"])
|
||||
entities.add_edges(start_pt, end_pt)
|
||||
end
|
||||
end
|
||||
|
||||
def edge_to_native_component(line, entities)
|
||||
line_id = line["applicationId"] || line["id"]
|
||||
definition = component_definition_to_native([line], "def::#{line_id}")
|
||||
find_and_erase_existing_instance(definition, line_id)
|
||||
instance = entities.add_instance(definition, Geom::Transformation.new)
|
||||
instance.name = line_id
|
||||
instance
|
||||
end
|
||||
|
||||
def face_to_native
|
||||
nil
|
||||
end
|
||||
|
||||
def point_to_native(x, y, z, units)
|
||||
Geom::Point3d.new(length_to_native(x, units), length_to_native(y, units), length_to_native(z, units))
|
||||
end
|
||||
|
||||
# converts a mesh to a native mesh and adds the faces to the given entities collection
|
||||
def mesh_to_native(mesh, entities)
|
||||
native_mesh = Geom::PolygonMesh.new(mesh["vertices"].count / 3)
|
||||
points = []
|
||||
mesh["vertices"].each_slice(3) do |pt|
|
||||
points.push(point_to_native(pt[0], pt[1], pt[2], mesh["units"]))
|
||||
end
|
||||
faces = mesh["faces"]
|
||||
while faces.count.positive?
|
||||
num_pts = faces.shift
|
||||
# 0 -> 3, 1 -> 4 to preserve backwards compatibility
|
||||
num_pts += 3 if num_pts < 3
|
||||
indices = faces.shift(num_pts)
|
||||
native_mesh.add_polygon(indices.map { |index| points[index] })
|
||||
end
|
||||
entities.add_faces_from_mesh(native_mesh, 4, material_to_native(mesh["renderMaterial"]))
|
||||
|
||||
native_mesh
|
||||
end
|
||||
|
||||
# creates a component definition and instance from a mesh
|
||||
def mesh_to_native_component(mesh, entities)
|
||||
mesh_id = mesh["applicationId"] || mesh["id"]
|
||||
definition = component_definition_to_native([mesh], "def::#{mesh_id}")
|
||||
find_and_erase_existing_instance(definition, mesh_id)
|
||||
instance = entities.add_instance(definition, Geom::Transformation.new)
|
||||
instance.name = mesh_id
|
||||
instance.material = material_to_native(mesh["renderMaterial"])
|
||||
instance
|
||||
end
|
||||
|
||||
# finds or creates a component definition from the geometry and the given name
|
||||
def component_definition_to_native(geometry, name, application_id = "")
|
||||
definition = Sketchup.active_model.definitions[name]
|
||||
return definition if definition && (definition.name == name || definition.guid == application_id)
|
||||
|
||||
definition&.entities&.clear!
|
||||
definition ||= Sketchup.active_model.definitions.add(name)
|
||||
geometry.each { |obj| convert_to_native(obj, definition.entities) }
|
||||
puts("definition finished: #{name} (#{application_id})")
|
||||
puts(" entity count: #{definition.entities.count}")
|
||||
definition
|
||||
end
|
||||
|
||||
# takes a component definition and finds and erases the first instance with the matching name (and optionally the applicationId)
|
||||
def find_and_erase_existing_instance(definition, name, app_id = "")
|
||||
definition.instances.find { |ins| ins.name == name || ins.guid == app_id }&.erase!
|
||||
end
|
||||
|
||||
# creates a component instance from a block
|
||||
def component_instance_to_native(block, entities)
|
||||
# is_group = block.key?("is_sketchup_group") && block["is_sketchup_group"]
|
||||
# something about this conversion is freaking out if nested block geo is a group
|
||||
# so this is set to false always until I can figure this out
|
||||
is_group = false
|
||||
|
||||
definition = component_definition_to_native(
|
||||
block["blockDefinition"]["geometry"],
|
||||
block["blockDefinition"]["name"],
|
||||
block["blockDefinition"]["applicationId"]
|
||||
)
|
||||
name = block["name"].nil? || block["name"].empty? ? block["id"] : block["name"]
|
||||
find_and_erase_existing_instance(definition, name, block["applicationId"])
|
||||
transform = transform_to_native(
|
||||
block["transform"].is_a?(Hash) ? block["transform"]["value"] : block["transform"],
|
||||
block["units"]
|
||||
)
|
||||
instance =
|
||||
if is_group
|
||||
entities.add_group(definition.entities.to_a)
|
||||
else
|
||||
entities.add_instance(definition, transform)
|
||||
end
|
||||
puts("Failed to create instance for speckle block instance #{block["id"]}") if instance.nil?
|
||||
instance.transformation = transform if is_group
|
||||
instance.material = material_to_native(block["renderMaterial"])
|
||||
instance.name = name
|
||||
instance
|
||||
end
|
||||
|
||||
def transform_to_native(t_arr, units = @units)
|
||||
Geom::Transformation.new(
|
||||
[
|
||||
t_arr[0],
|
||||
t_arr[4],
|
||||
t_arr[8],
|
||||
t_arr[12],
|
||||
t_arr[1],
|
||||
t_arr[5],
|
||||
t_arr[9],
|
||||
t_arr[13],
|
||||
t_arr[2],
|
||||
t_arr[6],
|
||||
t_arr[10],
|
||||
t_arr[14],
|
||||
length_to_native(t_arr[3], units),
|
||||
length_to_native(t_arr[7], units),
|
||||
length_to_native(t_arr[11], units),
|
||||
t_arr[15]
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def material_to_native(render_mat)
|
||||
return if render_mat.nil?
|
||||
|
||||
# return material with same name if it exists
|
||||
name = render_mat["name"] || render_mat["id"]
|
||||
material = Sketchup.active_model.materials[name]
|
||||
return material if material
|
||||
|
||||
# create a new sketchup material
|
||||
material = Sketchup.active_model.materials.add(name)
|
||||
material.alpha = render_mat["opacity"]
|
||||
argb = render_mat["diffuse"]
|
||||
material.color = Sketchup::Color.new((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >> 24) & 255)
|
||||
material
|
||||
end
|
||||
end
|
||||
@@ -1,270 +0,0 @@
|
||||
require "sketchup"
|
||||
|
||||
# To Speckle conversions for the ConverterSketchup
|
||||
module SpeckleSystems::SpeckleConnector::ToSpeckle
|
||||
def length_to_speckle(length)
|
||||
length.__send__("to_#{SpeckleSystems::SpeckleConnector::SKETCHUP_UNIT_STRINGS[@units]}")
|
||||
end
|
||||
|
||||
# convert an edge to a speckle line
|
||||
def edge_to_speckle(edge)
|
||||
{
|
||||
speckle_type: "Objects.Geometry.Line",
|
||||
applicationId: edge.persistent_id.to_s,
|
||||
units: @units,
|
||||
start: vertex_to_speckle(edge.start),
|
||||
end: vertex_to_speckle(edge.end),
|
||||
domain: speckle_interval(0, Float(edge.length)),
|
||||
bbox: bounds_to_speckle(edge.bounds)
|
||||
}
|
||||
end
|
||||
|
||||
# covnert a component definition to a speckle block definition
|
||||
def component_definition_to_speckle(definition)
|
||||
guid = definition.guid
|
||||
return @component_defs[guid] if @component_defs.key?(guid)
|
||||
|
||||
speckle_def = {
|
||||
speckle_type: "Objects.Other.BlockDefinition",
|
||||
applicationId: guid,
|
||||
units: @units,
|
||||
name: definition.name,
|
||||
# i think the base point is always the origin?
|
||||
basePoint: speckle_point,
|
||||
"@geometry" => if %w[Edge Face].include?(definition.entities[0].typename)
|
||||
group_mesh_to_speckle(definition)
|
||||
else
|
||||
definition.entities.map { |entity| convert_to_speckle(entity) }
|
||||
end
|
||||
}
|
||||
@component_defs[guid] = speckle_def
|
||||
end
|
||||
|
||||
# convert a component instane to a speckle block instance
|
||||
def component_instance_to_speckle(instance, is_group: false)
|
||||
transform = instance.transformation
|
||||
{
|
||||
speckle_type: "Objects.Other.BlockInstance",
|
||||
applicationId: instance.guid,
|
||||
is_sketchup_group: is_group,
|
||||
units: @units,
|
||||
bbox: bounds_to_speckle(instance.bounds),
|
||||
name: instance.name == "" ? nil : instance.name,
|
||||
renderMaterial: instance.material.nil? ? nil : material_to_speckle(instance.material),
|
||||
transform: transform_to_speckle(transform),
|
||||
"@blockDefinition" => component_definition_to_speckle(instance.definition)
|
||||
}
|
||||
end
|
||||
|
||||
def group_mesh_to_speckle(component_def)
|
||||
mat_groups = {}
|
||||
nested_blocks = []
|
||||
lines = []
|
||||
|
||||
component_def.entities.each do |entity|
|
||||
nested_blocks.push(component_instance_to_speckle(entity)) if entity.typename == "ComponentInstance"
|
||||
next unless %w[Edge Face].include?(entity.typename)
|
||||
|
||||
if entity.typename == "Edge"
|
||||
lines.push(edge_to_speckle(entity))
|
||||
else
|
||||
face = entity
|
||||
# convert material
|
||||
mat_id = face.material.nil? ? "none" : face.material.entityID
|
||||
mat_groups[mat_id] = initialise_group_mesh(face, component_def.bounds) unless mat_groups.key?(mat_id)
|
||||
|
||||
if face.loops.size > 1
|
||||
mesh = face.mesh
|
||||
mat_groups[mat_id]["@(31250)vertices"].push(*mesh_points_to_array(mesh))
|
||||
mat_groups[mat_id]["@(62500)faces"].push(*mesh_faces_to_array(mesh, mat_groups[mat_id][:pt_count] - 1))
|
||||
else
|
||||
mat_groups[mat_id]["@(31250)vertices"].push(*face_vertices_to_array(face))
|
||||
mat_groups[mat_id]["@(62500)faces"].push(*face_indices_to_array(face, mat_groups[mat_id][:pt_count]))
|
||||
end
|
||||
mat_groups[mat_id][:pt_count] += face.vertices.count
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
mat_groups.values.map { |group| group.delete(:pt_count) }
|
||||
mat_groups.values + lines + nested_blocks
|
||||
end
|
||||
|
||||
def transform_to_speckle(transform)
|
||||
t_arr = transform.to_a
|
||||
{
|
||||
speckle_type: "Objects.Other.Transform",
|
||||
units: @units,
|
||||
value: [
|
||||
t_arr[0],
|
||||
t_arr[4],
|
||||
t_arr[8],
|
||||
length_to_speckle(t_arr[12]),
|
||||
t_arr[1],
|
||||
t_arr[5],
|
||||
t_arr[9],
|
||||
length_to_speckle(t_arr[13]),
|
||||
t_arr[2],
|
||||
t_arr[6],
|
||||
t_arr[10],
|
||||
length_to_speckle(t_arr[14]),
|
||||
t_arr[3],
|
||||
t_arr[7],
|
||||
t_arr[11],
|
||||
t_arr[15]
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def initialise_group_mesh(face, bounds)
|
||||
{
|
||||
speckle_type: "Objects.Geometry.Mesh",
|
||||
units: @units,
|
||||
bbox: bounds_to_speckle(bounds),
|
||||
"@(31250)vertices" => [],
|
||||
"@(62500)faces" => [],
|
||||
"@(31250)textureCoordinates" => [],
|
||||
pt_count: 0,
|
||||
renderMaterial: face.material.nil? ? nil : material_to_speckle(face.material)
|
||||
}
|
||||
end
|
||||
|
||||
# get an array of face indices from a sketchup polygon mesh
|
||||
def mesh_faces_to_array(mesh, offset)
|
||||
faces = []
|
||||
puts(faces)
|
||||
mesh.polygons.each do |poly|
|
||||
faces.push(
|
||||
poly.count, *poly.map { |index| index.abs + offset }
|
||||
)
|
||||
end
|
||||
faces
|
||||
end
|
||||
|
||||
# get a flat array of vertices from a sketchup polygon mesh
|
||||
def mesh_points_to_array(mesh)
|
||||
pts_array = []
|
||||
mesh.points.each do |pt|
|
||||
pts_array.push(
|
||||
length_to_speckle(pt[0]),
|
||||
length_to_speckle(pt[1]),
|
||||
length_to_speckle(pt[2])
|
||||
)
|
||||
end
|
||||
pts_array
|
||||
end
|
||||
|
||||
# get a flat array of face indices from a sketchup face
|
||||
def face_indices_to_array(face, offset)
|
||||
face_array = [face.vertices.count]
|
||||
face_array.push(*face.vertices.count.times.map { |index| index + offset })
|
||||
face_array
|
||||
end
|
||||
|
||||
# get a flat array of vertices from a list of sketchup vertices
|
||||
def face_vertices_to_array(face)
|
||||
pts_array = []
|
||||
face.vertices.each do |v|
|
||||
pt = v.position
|
||||
pts_array.push(length_to_speckle(pt[0]), length_to_speckle(pt[1]), length_to_speckle(pt[2]))
|
||||
end
|
||||
pts_array
|
||||
end
|
||||
|
||||
|
||||
|
||||
def uvs_to_array(mesh)
|
||||
uvs_array = []
|
||||
mesh.uvs(true).each do |pt|
|
||||
uvs_array.push(
|
||||
length_to_speckle(pt[0] / pt[2]),
|
||||
length_to_speckle(pt[1] / pt[2])
|
||||
)
|
||||
end
|
||||
uvs_array
|
||||
end
|
||||
|
||||
def face_to_speckle(face)
|
||||
mesh = face.loops.count > 1 ? face.mesh : nil
|
||||
{
|
||||
speckle_type: "Objects.Geometry.Mesh",
|
||||
units: @units,
|
||||
renderMaterial: face.material.nil? ? nil : material_to_speckle(face.material),
|
||||
bbox: bounds_to_speckle(face.bounds),
|
||||
"@(31250)vertices" => mesh.nil? ? face_vertices_to_array(face) : mesh_points_to_array(mesh),
|
||||
"@(62500)faces" => mesh.nil? ? face_indices_to_array(face, 0) : mesh_faces_to_array(mesh, -1)
|
||||
}
|
||||
end
|
||||
|
||||
def vertex_to_speckle(vertex)
|
||||
point = vertex.position
|
||||
{
|
||||
speckle_type: "Objects.Geometry.Point",
|
||||
units: @units,
|
||||
x: length_to_speckle(point[0]),
|
||||
y: length_to_speckle(point[1]),
|
||||
z: length_to_speckle(point[2])
|
||||
}
|
||||
end
|
||||
|
||||
def material_to_speckle(material)
|
||||
rgba = material.color.to_a
|
||||
{
|
||||
speckle_type: "Objects.Other.RenderMaterial",
|
||||
name: material.name,
|
||||
diffuse: [rgba[3] << 24 | rgba[0] << 16 | rgba[1] << 8 | rgba[2]].pack("l").unpack1("l"),
|
||||
opacity: material.alpha,
|
||||
emissive: -16_777_216,
|
||||
metalness: 0,
|
||||
roughness: 1
|
||||
}
|
||||
end
|
||||
|
||||
def bounds_to_speckle(bounds)
|
||||
min_pt = bounds.min
|
||||
{
|
||||
speckle_type: "Objects.Geometry.Box",
|
||||
units: @units,
|
||||
area: 0,
|
||||
volume: 0,
|
||||
xSize: speckle_interval(min_pt[0], bounds.width),
|
||||
ySize: speckle_interval(min_pt[1], bounds.height),
|
||||
zSize: speckle_interval(min_pt[2], bounds.depth),
|
||||
basePlane: speckle_plane
|
||||
}
|
||||
end
|
||||
|
||||
def speckle_interval(start_val, end_val)
|
||||
{
|
||||
speckle_type: "Objects.Primitive.Interval",
|
||||
units: @units,
|
||||
start: start_val.is_a?(Length) ? length_to_speckle(start_val) : start_val,
|
||||
end: end_val.is_a?(Length) ? length_to_speckle(end_val) : end_val
|
||||
}
|
||||
end
|
||||
|
||||
def speckle_point(x = 0.0, y = 0.0, z = 0.0, vector: false)
|
||||
{
|
||||
speckle_type: vector ? "Objects.Geometry.Vector" : "Objects.Geometry.Point",
|
||||
units: @units,
|
||||
x: x.is_a?(Length) ? length_to_speckle(x) : x,
|
||||
y: y.is_a?(Length) ? length_to_speckle(y) : y,
|
||||
z: z.is_a?(Length) ? length_to_speckle(z) : z
|
||||
}
|
||||
end
|
||||
|
||||
def speckle_vector(x = 0.0, y = 0.0, z = 0.0)
|
||||
speckle_point(x, y, z, vector: true)
|
||||
end
|
||||
|
||||
def speckle_plane(xdir: [1, 0, 0], ydir: [0, 1, 0], normal: [0, 0, 1], origin: [0, 0, 0])
|
||||
{
|
||||
speckle_type: "Objects.Geometry.Plane",
|
||||
units: @units,
|
||||
xdir: speckle_vector(*xdir),
|
||||
ydir: speckle_vector(*ydir),
|
||||
normal: speckle_vector(*normal),
|
||||
origin: speckle_point(*origin)
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -1,17 +0,0 @@
|
||||
module SpeckleSystems::SpeckleConnector
|
||||
# from thomthom
|
||||
# https://github.com/thomthom/true-bend/blob/master/src/tt_truebend/debug.rb
|
||||
|
||||
# @note Debug method to reload the plugin.
|
||||
#
|
||||
# @example
|
||||
# SpeckleSystems::SpeckleConnector.reload
|
||||
#
|
||||
# @return [Integer] Number of files reloaded.
|
||||
def self.reload
|
||||
load(__FILE__)
|
||||
pattern = File.join(__dir__, "**/*.rb")
|
||||
Dir.glob(pattern).each { |file| load(file) }
|
||||
.size
|
||||
end
|
||||
end
|
||||
@@ -1,204 +0,0 @@
|
||||
require "JSON"
|
||||
require "json"
|
||||
require "sketchup"
|
||||
require "speckle_connector/converter/converter_sketchup"
|
||||
require "speckle_connector/accounts"
|
||||
|
||||
module SpeckleSystems::SpeckleConnector
|
||||
UNITS = { 0 => "in", 1 => "ft", 2 => "mm", 3 => "cm", 4 => "m", 5 => "yd" }.freeze
|
||||
public_constant :UNITS
|
||||
@to_send = {}
|
||||
@connected = false
|
||||
|
||||
def self.queue_send(stream_id, converted)
|
||||
@to_send = { stream_id: stream_id, converted: converted }
|
||||
send_from_queue(stream_id) if @connected
|
||||
end
|
||||
|
||||
def self.send_from_queue(stream_id)
|
||||
return unless @to_send[:stream_id] == stream_id
|
||||
|
||||
@dialog.execute_script("convertedFromSketchup('#{@to_send[:stream_id]}',#{@to_send[:converted].to_json})")
|
||||
@dialog.execute_script("oneClickSend('#{@to_send[:stream_id]}')")
|
||||
@to_send = {}
|
||||
end
|
||||
|
||||
def self.init_dialog
|
||||
options = {
|
||||
dialog_title: "SpeckleSketchUp",
|
||||
preferences_key: "example.htmldialog.materialinspector",
|
||||
style: UI::HtmlDialog::STYLE_DIALOG,
|
||||
min_width: 250,
|
||||
min_height: 50
|
||||
}
|
||||
dialog = UI::HtmlDialog.new(options)
|
||||
dialog.center
|
||||
dialog
|
||||
end
|
||||
|
||||
def self.create_dialog(show: true)
|
||||
if @dialog&.visible?
|
||||
@dialog.bring_to_front
|
||||
else
|
||||
@dialog ||= init_dialog
|
||||
@dialog.add_action_callback("send_selection") do |_action_context, stream_id|
|
||||
send_selection(stream_id)
|
||||
nil
|
||||
end
|
||||
@dialog.add_action_callback("receive_objects") do |_action_context, base, stream_id|
|
||||
receive_objects(base, stream_id)
|
||||
nil
|
||||
end
|
||||
@dialog.add_action_callback("reload_accounts") do |_action_context|
|
||||
reload_accounts
|
||||
end
|
||||
@dialog.add_action_callback("init_local_accounts") do |_action_context|
|
||||
init_local_accounts
|
||||
end
|
||||
@dialog.add_action_callback("load_saved_streams") do |_action_context|
|
||||
load_saved_streams
|
||||
end
|
||||
@dialog.add_action_callback("save_stream") do |_action_context, stream_id|
|
||||
save_stream(stream_id)
|
||||
end
|
||||
@dialog.add_action_callback("remove_stream") do |_action_context, stream_id|
|
||||
remove_stream(stream_id)
|
||||
end
|
||||
@dialog.add_action_callback("notify_connected") do |_action_context, stream_id|
|
||||
notify_connected(stream_id)
|
||||
end
|
||||
|
||||
# set connected to false when dialog is closed
|
||||
@dialog.set_can_close do
|
||||
@connected = false
|
||||
!@connected
|
||||
end
|
||||
|
||||
if DEV_MODE
|
||||
puts("Launching Speckle Connector from http://localhost:8081")
|
||||
@dialog.set_url("http://localhost:8081")
|
||||
else
|
||||
html_file = File.join(File.dirname(File.expand_path(__FILE__)), "html", "index.html")
|
||||
puts("Launching Speckle Connector from #{html_file}")
|
||||
@dialog.set_file(html_file)
|
||||
end
|
||||
|
||||
@dialog.show if show
|
||||
end
|
||||
@dialog
|
||||
end
|
||||
|
||||
def self.notify_connected(stream_id)
|
||||
@connected = true
|
||||
send_from_queue(stream_id)
|
||||
end
|
||||
|
||||
def self.convert_to_speckle(to_convert)
|
||||
converter = ConverterSketchup.new(UNITS[Sketchup.active_model.options["UnitsOptions"]["LengthUnit"]])
|
||||
to_convert.map { |entity| converter.convert_to_speckle(entity) }
|
||||
end
|
||||
|
||||
def self.send_selection(stream_id)
|
||||
converted = convert_to_speckle(Sketchup.active_model.selection)
|
||||
puts("converted #{converted.count} objects for stream #{stream_id}")
|
||||
# puts(converted.to_json)
|
||||
@dialog.execute_script("convertedFromSketchup('#{stream_id}',#{converted.to_json})")
|
||||
rescue StandardError => e
|
||||
puts(e)
|
||||
@dialog.execute_script("sketchupOperationFailed('#{stream_id}')")
|
||||
end
|
||||
|
||||
def self.receive_objects(base, stream_id)
|
||||
puts("received objects from stream #{stream_id}")
|
||||
model = Sketchup.active_model
|
||||
converter = ConverterSketchup.new(UNITS[model.options["UnitsOptions"]["LengthUnit"]])
|
||||
converter.traverse_commit_object(base)
|
||||
@dialog.execute_script("finishedReceiveInSketchup('#{stream_id}')")
|
||||
rescue StandardError => e
|
||||
puts(e)
|
||||
@dialog.execute_script("sketchupOperationFailed('#{stream_id}')")
|
||||
end
|
||||
|
||||
def self.one_click_send
|
||||
acct = Accounts.default_account
|
||||
return puts("No local account found. Please refer to speckle.guide for more information.") if acct.nil?
|
||||
|
||||
create_dialog
|
||||
to_convert = Sketchup.active_model.selection.count > 0 ? Sketchup.active_model.selection : Sketchup.active_model.entities
|
||||
if first_saved_stream.nil?
|
||||
create_stream(to_convert)
|
||||
else
|
||||
queue_send(first_saved_stream, convert_to_speckle(to_convert))
|
||||
end
|
||||
rescue StandardError => e
|
||||
puts(e)
|
||||
@dialog.execute_script("sketchupOperationFailed('#{@to_send[:stream_id]}')")
|
||||
end
|
||||
|
||||
def self.first_saved_stream
|
||||
saved_streams = Sketchup.active_model.attribute_dictionary("speckle", true)["streams"] or []
|
||||
saved_streams.nil? || saved_streams.empty? ? nil : saved_streams[0]
|
||||
end
|
||||
|
||||
def self.load_saved_streams
|
||||
saved_streams = Sketchup.active_model.attribute_dictionary("speckle", true)["streams"] or []
|
||||
@dialog.execute_script("setSavedStreams(#{saved_streams})")
|
||||
end
|
||||
|
||||
def self.init_local_accounts
|
||||
puts("Initialisation of Speckle accounts requested by plugin")
|
||||
@dialog.execute_script("loadAccounts(#{Accounts.load_accounts.to_json}, #{Accounts.get_suuid.to_json})")
|
||||
end
|
||||
|
||||
def self.reload_accounts
|
||||
puts("Reload of Speckle accounts requested by plugin")
|
||||
@dialog.execute_script("loadAccounts(#{Accounts.load_accounts.to_json})")
|
||||
load_saved_streams
|
||||
end
|
||||
|
||||
def self.save_stream(stream_id)
|
||||
speckle_dict = Sketchup.active_model.attribute_dictionary("speckle", true)
|
||||
saved = speckle_dict["streams"] || []
|
||||
saved = saved.empty? ? [stream_id] : saved.unshift(stream_id)
|
||||
speckle_dict["streams"] = saved
|
||||
|
||||
load_saved_streams
|
||||
end
|
||||
|
||||
def self.remove_stream(stream_id)
|
||||
speckle_dict = Sketchup.active_model.attribute_dictionary("speckle", true)
|
||||
saved = speckle_dict["streams"] || []
|
||||
saved -= [stream_id]
|
||||
speckle_dict["streams"] = saved
|
||||
|
||||
load_saved_streams
|
||||
end
|
||||
|
||||
def self.create_stream(to_convert)
|
||||
acct = Accounts.default_account
|
||||
return if acct.nil?
|
||||
|
||||
path = Sketchup.active_model.path
|
||||
stream_name = path ? File.basename(path, ".*") : "Untitled SketchUp Model"
|
||||
query = "mutation streamCreate($stream: StreamCreateInput!) {streamCreate(stream: $stream)}"
|
||||
vars = { stream: { name: stream_name, description: "Stream created from SketchUp" } }
|
||||
|
||||
request = Sketchup::Http::Request.new("#{acct["serverInfo"]["url"]}/graphql", Sketchup::Http::POST)
|
||||
request.headers = { "Authorization" => "Bearer #{acct["token"]}", "Content-Type" => "application/json" }
|
||||
request.body = { query: query, variables: vars }.to_json
|
||||
|
||||
request.start do |_req, res|
|
||||
res_data = JSON.parse(res.body)["data"]
|
||||
raise(StandardError) unless res_data
|
||||
|
||||
stream_id = res_data["streamCreate"]
|
||||
save_stream(stream_id)
|
||||
queue_send(stream_id, convert_to_speckle(to_convert))
|
||||
# send_selection(stream_id)
|
||||
end
|
||||
load_saved_streams
|
||||
rescue StandardError => e
|
||||
puts(e)
|
||||
puts("Could not create a new stream")
|
||||
end
|
||||
end
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -1,37 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "sketchup"
|
||||
require "speckle_connector/dialog"
|
||||
require "speckle_connector/debug"
|
||||
|
||||
module SpeckleSystems
|
||||
module SpeckleConnector
|
||||
unless file_loaded?(__FILE__)
|
||||
cmd_cube = UI::Command.new("Dialog") { create_dialog }
|
||||
cmd_cube.tooltip = "Launch Connector"
|
||||
cmd_cube.status_bar_text = "Opens the Speckle Connector window"
|
||||
cmd_cube.small_icon = "icons/s2logo.png"
|
||||
cmd_cube.large_icon = "icons/s2logo.png"
|
||||
|
||||
menu = UI.menu("Tools")
|
||||
menu.add_item(cmd_cube)
|
||||
|
||||
cmd_send = UI::Command.new("Send") { one_click_send }
|
||||
cmd_send.tooltip = "Send to Speckle"
|
||||
cmd_send.status_bar_text = "Send to Speckle"
|
||||
cmd_send.small_icon = "icons/Sender.png"
|
||||
cmd_send.large_icon = "icons/Sender.png"
|
||||
|
||||
menu = UI.menu("Tools")
|
||||
menu.add_item(cmd_send)
|
||||
|
||||
toolbar = UI::Toolbar.new("Speckle")
|
||||
toolbar.add_item(cmd_cube)
|
||||
toolbar.add_item(cmd_send)
|
||||
toolbar.restore
|
||||
|
||||
file_loaded(__FILE__)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sketchup'
|
||||
require 'extensions'
|
||||
|
||||
# Speckle connector module to enable multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
# Version - patched by CI
|
||||
CONNECTOR_VERSION = '0.0.0'
|
||||
|
||||
file = __FILE__.dup
|
||||
|
||||
# Account for Ruby encoding bug under Windows.
|
||||
file.force_encoding('UTF-8') if file.respond_to?(:force_encoding)
|
||||
|
||||
# Support folder should be named the same as the root .rb file.
|
||||
folder_name = File.basename(file, '.*')
|
||||
|
||||
# Path to the root .rb file (this file).
|
||||
PATH_ROOT = File.dirname(file).freeze
|
||||
|
||||
# Path to the support folder.
|
||||
PATH = File.join(PATH_ROOT, folder_name).freeze
|
||||
|
||||
# Run from localhost or from build files
|
||||
DEV_MODE = true
|
||||
puts("Loading Speckle Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
|
||||
|
||||
unless file_loaded?(__FILE__)
|
||||
ex = SketchupExtension.new('Speckle SketchUp v3', File.join(PATH, 'bootstrap'))
|
||||
ex.description = 'Speckle Connector for SketchUp'
|
||||
ex.version = CONNECTOR_VERSION
|
||||
ex.copyright = 'AEC Systems Ltd.'
|
||||
ex.creator = 'Speckle Systems'
|
||||
Sketchup.register_extension(ex, true)
|
||||
|
||||
file_loaded(__FILE__)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'sketchup'
|
||||
require 'pathname'
|
||||
require 'speckle_connector_3/debug'
|
||||
require_relative 'src/log/log'
|
||||
require_relative 'src/ui/sketchup_ui'
|
||||
require_relative 'src/ui/ui_controller'
|
||||
require_relative 'src/commands/menu_command_handler'
|
||||
require_relative 'src/app/speckle_connector_app'
|
||||
require_relative 'src/states/user_state'
|
||||
require_relative 'src/states/initial_state'
|
||||
require_relative 'src/commands/speckle_menu_commands'
|
||||
|
||||
# Speckle Connector on SketchUp to enable Multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
SKETCHUP_VERSION = Sketchup.version.to_i
|
||||
|
||||
dir = __dir__.dup
|
||||
dir.force_encoding('UTF-8') if dir.respond_to?(:force_encoding)
|
||||
SPECKLE_CONNECTOR_SRC_PATH = Pathname.new(File.expand_path(dir)).cleanpath.to_s
|
||||
|
||||
def self.initialize_app
|
||||
sketchup_ui = Ui::SketchupUi.new
|
||||
ui_controller = Ui::UiController.new(sketchup_ui)
|
||||
menu_commands = Commands::MenuCommandHandler.new
|
||||
user_state = SpeckleConnector3::States::UserState.new({})
|
||||
initial_state = SpeckleConnector3::States::InitialState.new(user_state)
|
||||
app = SpeckleConnector3::App::SpeckleConnectorApp.new(menu_commands, initial_state, ui_controller)
|
||||
# Add menu commands to SketchUp and Speckle application
|
||||
Commands::SpeckleMenuCommands.add_initial_commands!(app)
|
||||
app
|
||||
end
|
||||
|
||||
app = initialize_app
|
||||
SPECKLE_APP = app
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Speckle connector module to enable multiplayer mode ON!
|
||||
module SpeckleConnector3
|
||||
# from thomthom
|
||||
# https://github.com/thomthom/true-bend/blob/master/src/tt_truebend/debug.rb
|
||||
|
||||
# @note Debug method to reload the plugin.
|
||||
#
|
||||
# @example
|
||||
# SpeckleConnector.reload
|
||||
#
|
||||
# @return [Integer] Number of files reloaded.
|
||||
# rubocop:disable SketchupSuggestions/FileEncoding
|
||||
def self.reload
|
||||
load(__FILE__)
|
||||
pattern = File.join(__dir__, '**/*.rb')
|
||||
# TODO: Here is a opportunity to improve reloading process.
|
||||
# We can cache last edited time of the each file later to check which file need to be reloaded.
|
||||
Dir.glob(pattern).each { |file| load(file) unless file.include?('bootstrap') }
|
||||
.size
|
||||
end
|
||||
# rubocop:enable SketchupSuggestions/FileEncoding
|
||||
end
|
||||
|
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 798 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 639 B After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,103 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'JSON'
|
||||
require 'fileutils'
|
||||
require_relative '../ext/sqlite3'
|
||||
require_relative '../constants/path_constants'
|
||||
require_relative '../preferences/preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
# Accounts to communicate with models on user's account.
|
||||
module Accounts
|
||||
# Load accounts from user's app data.
|
||||
def self.load_accounts
|
||||
db_path = SPECKLE_ACCOUNTS_DB_PATH
|
||||
unless File.exist?(db_path)
|
||||
# Ensure parent directory exists before creating the database file
|
||||
FileUtils.mkdir_p(File.dirname(db_path))
|
||||
File.new(SPECKLE_ACCOUNTS_DB_PATH, "w")
|
||||
db = Sqlite3::Database.new(SPECKLE_ACCOUNTS_DB_PATH)
|
||||
Preferences.create_objects_table(db)
|
||||
db.close
|
||||
return []
|
||||
end
|
||||
|
||||
db = Sqlite3::Database.new(db_path)
|
||||
rows = db.exec('SELECT * FROM objects')
|
||||
db.close
|
||||
rows.map { |row| JSON.parse(row[1]) }
|
||||
end
|
||||
|
||||
def self.add_account(account_id, account)
|
||||
unless File.exist?(SPECKLE_ACCOUNTS_DB_PATH)
|
||||
# Ensure parent directory exists before creating the database file
|
||||
FileUtils.mkdir_p(File.dirname(SPECKLE_ACCOUNTS_DB_PATH))
|
||||
File.new(SPECKLE_ACCOUNTS_DB_PATH, "w")
|
||||
db = Sqlite3::Database.new(SPECKLE_ACCOUNTS_DB_PATH)
|
||||
create_objects_table(db)
|
||||
db.close
|
||||
end
|
||||
db = Sqlite3::Database.new(SPECKLE_ACCOUNTS_DB_PATH)
|
||||
account_json = JSON.generate(account)
|
||||
sql_query = "INSERT OR REPLACE INTO objects (hash, content) VALUES ('#{account_id}', '#{account_json}')"
|
||||
|
||||
begin
|
||||
db.exec(sql_query)
|
||||
puts "Account with hash #{account_id} has been added/updated."
|
||||
rescue StandardError => e
|
||||
puts "An error occurred while adding the account: #{e}"
|
||||
ensure
|
||||
db.close
|
||||
end
|
||||
end
|
||||
|
||||
# Creates the 'objects' table in the database if it doesn't already exist.
|
||||
# @param db [Sqlite3::Database] the SQLite3 database instance.
|
||||
def self.create_objects_table(db)
|
||||
db.exec <<-SQL
|
||||
CREATE TABLE IF NOT EXISTS objects (
|
||||
hash TEXT PRIMARY KEY,
|
||||
content TEXT
|
||||
);
|
||||
SQL
|
||||
end
|
||||
|
||||
def self.remove_account(account_id)
|
||||
db_path = SPECKLE_ACCOUNTS_DB_PATH
|
||||
unless File.exist?(db_path)
|
||||
raise(
|
||||
IOError,
|
||||
"No Accounts db found. Please read the guide for different options for adding your account:\n
|
||||
https://speckle.guide/user/manager.html#adding-accounts"
|
||||
)
|
||||
end
|
||||
db = Sqlite3::Database.new(db_path)
|
||||
|
||||
begin
|
||||
db.exec("DELETE FROM objects WHERE hash = '#{account_id}'")
|
||||
puts "Account with hash #{account_id} has been removed."
|
||||
rescue StandardError => e
|
||||
puts "An error occurred: #{e}"
|
||||
ensure
|
||||
db.close
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_account_by_id(id)
|
||||
accounts = load_accounts
|
||||
accounts.select { |acc| acc['id'] == id }[0]
|
||||
end
|
||||
|
||||
# Default account on the user computer.
|
||||
def self.default_account
|
||||
accounts = load_accounts
|
||||
accounts.select { |acc| acc['isDefault'] }[0] || accounts[0]
|
||||
end
|
||||
|
||||
# Try to get local server account for debug/test purposes.
|
||||
def self.try_get_local_server_account
|
||||
accounts = load_accounts
|
||||
accounts.select { |acc| acc['serverInfo']['url'].include?('localhost') }[0] || nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../accounts/accounts'
|
||||
require_relative '../load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to add account to local Account db.
|
||||
class AddAccount < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, account_id, account)
|
||||
SpeckleConnector3::Accounts.add_account(account_id, account)
|
||||
js_script = "accountsBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('addAccount', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../accounts/accounts'
|
||||
require_relative '../load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to initialize local accounts from database.
|
||||
class GetAccounts < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
puts 'Initialisation of Speckle accounts requested by plugin'
|
||||
accounts_data = SpeckleConnector3::Accounts.load_accounts
|
||||
js_script = "accountsBinding.receiveResponse('#{resolve_id}', #{accounts_data.to_json})"
|
||||
state.with_add_queue_js_command('getAccounts', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../accounts/accounts'
|
||||
require_relative '../load_saved_streams'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to remove account from database.
|
||||
class RemoveAccount < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, account_id)
|
||||
SpeckleConnector3::Accounts.remove_account(account_id)
|
||||
js_script = "accountsBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('removeAccount', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# State changer object.
|
||||
class Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @param parameters [Array] parameters that the action takes
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(_state, *_parameters)
|
||||
raise NotImplementedError, 'Implement in subclass.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative 'deactivate_diffing'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Deactivate diffing for stream.
|
||||
class ActivateDiffing < Action
|
||||
def initialize(stream_id)
|
||||
super()
|
||||
@stream_id = stream_id
|
||||
end
|
||||
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def update_state(state)
|
||||
state = DeactivateDiffing.update_state(state, nil, {})
|
||||
puts "Diffing activated for #{@stream_id}"
|
||||
speckle_entities = state.speckle_state.speckle_entities
|
||||
invalid_speckle_entities = speckle_entities.select do |_id, entity|
|
||||
entity.invalid_stream_ids.include?(@stream_id) && entity.sketchup_entity.is_a?(Sketchup::Face)
|
||||
end
|
||||
invalid_speckle_entities.each do |id, entity|
|
||||
new_entity = entity.activate_diffing(@stream_id, state.sketchup_state.materials.by_id(MAT_EDIT))
|
||||
speckle_entities = speckle_entities.put(id, new_entity)
|
||||
end
|
||||
new_speckle_state = state.speckle_state.with_speckle_entities(speckle_entities)
|
||||
state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Adds material to speckle state and Sketchup.
|
||||
class AddMaterial < Action
|
||||
def self.update_state(state, material_name:, color:, material_id:, alpha: nil)
|
||||
materials = state.sketchup_state.materials
|
||||
existing_material = materials.by_id(material_id)
|
||||
return state if existing_material&.valid?
|
||||
|
||||
new_material = create_or_get_material(state.sketchup_state.sketchup_model,
|
||||
material_name,
|
||||
color,
|
||||
material_id,
|
||||
alpha: alpha)
|
||||
new_materials = materials.add_material(material_id, new_material)
|
||||
new_sketchup_state = state.sketchup_state.with(:@materials => new_materials)
|
||||
state.with(:@sketchup_state => new_sketchup_state)
|
||||
end
|
||||
|
||||
def self.create_or_get_material(model, material_name, color, material_id, alpha: nil)
|
||||
materials = model.materials
|
||||
existing_material = materials.find { |mat| mat.name == material_name }
|
||||
return existing_material if existing_material&.valid?
|
||||
|
||||
existing_material = materials.add material_name
|
||||
existing_material.set_attribute(MAT_DICTIONARY, MAT_ID, material_id.to_s)
|
||||
set_hex_color(existing_material, color)
|
||||
existing_material.alpha = alpha if alpha
|
||||
existing_material
|
||||
end
|
||||
|
||||
def self.set_hex_color(skp_material, hex_value)
|
||||
hex_value = hex_value.to_s
|
||||
col_blue, col_green, col_red = parse_hex_color(hex_value)
|
||||
skp_material.color = col_red, col_green, col_blue
|
||||
end
|
||||
|
||||
def self.parse_hex_color(hex_value)
|
||||
split_values = hex_value.match(/^#([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})$/) ||
|
||||
hex_value.match(/^#([a-fA-F\d])([a-fA-F\d])([a-fA-F\d])$/)
|
||||
col_red = split_values[1].hex
|
||||
col_green = split_values[2].hex
|
||||
col_blue = split_values[3].hex
|
||||
return col_blue, col_green, col_red
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative 'mapped_entities_updated'
|
||||
require_relative 'events/selection_event_action'
|
||||
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Apply mappings for selected entities.
|
||||
class ApplyMappings < Action
|
||||
def initialize(entities_to_map, method, category, family,
|
||||
family_type, level, name, is_definition)
|
||||
super()
|
||||
@entities_to_map = entities_to_map
|
||||
@method = method
|
||||
@category = category
|
||||
@name = name
|
||||
@family = family
|
||||
@family_type = family_type
|
||||
@level = level
|
||||
@is_definition = is_definition
|
||||
end
|
||||
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def update_state(state)
|
||||
sketchup_model = state.sketchup_state.sketchup_model
|
||||
entities = if sketchup_model.active_path.nil?
|
||||
sketchup_model.entities
|
||||
else
|
||||
sketchup_model.active_path.last.definition.entities
|
||||
end
|
||||
|
||||
# Collect entities from entity ids that comes from UI as list
|
||||
entities_to_map = entities.select { |e| @entities_to_map.include?(e.persistent_id.to_s) }
|
||||
|
||||
# Switch to definitions if all entities are component instance and UI flag shows that
|
||||
if entities_to_map.all? { |e| e.is_a?(Sketchup::ComponentInstance) } && @is_definition
|
||||
entities_to_map = entities_to_map.collect(&:definition).uniq
|
||||
end
|
||||
|
||||
# Store speckle state to update with mapped entities.
|
||||
speckle_state = state.speckle_state
|
||||
entities_to_map.each do |entity|
|
||||
name = if @name == '<Mixed>'
|
||||
entity.respond_to?(:name) ? entity.name : ''
|
||||
else
|
||||
@name
|
||||
end
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :category, @category)
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :name, name)
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :method, @method)
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :family, @family)
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :family_type, @family_type)
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :level, @level)
|
||||
speckle_state = speckle_state.with_mapped_entity(entity)
|
||||
end
|
||||
|
||||
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
|
||||
Events::SelectionEventAction.update_state(new_state, { apply: true })
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'add_send_model_card'
|
||||
require_relative 'add_receive_model_card'
|
||||
require_relative '../action'
|
||||
require_relative '../../cards/send_card'
|
||||
require_relative '../../cards/receive_card'
|
||||
require_relative '../../filters/send/everything_filter'
|
||||
require_relative '../../filters/send/selection_filter'
|
||||
require_relative '../../filters/send_filters'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to add send card.
|
||||
class AddModel < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, data)
|
||||
if data['typeDiscriminator'] == 'SenderModelCard'
|
||||
Actions::AddSendModelCard.update_state(state, resolve_id, data)
|
||||
else
|
||||
Actions::AddReceiveModelCard.update_state(state, resolve_id, data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../cards/send_card'
|
||||
require_relative '../../cards/receive_card'
|
||||
require_relative '../../filters/send/everything_filter'
|
||||
require_relative '../../filters/send/selection_filter'
|
||||
require_relative '../../filters/send_filters'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to add receive model card.
|
||||
class AddReceiveModelCard < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, data)
|
||||
model_card_id = data['modelCardId']
|
||||
account_id = data['accountId']
|
||||
server_url = data['serverUrl']
|
||||
workspace_id = data['workspaceId']
|
||||
workspace_slug = data['workspaceSlug']
|
||||
project_id = data['projectId']
|
||||
model_id = data['modelId']
|
||||
project_name = data['projectName']
|
||||
model_name = data['modelName']
|
||||
expired = data['expired']
|
||||
selected_version_id = data['selectedVersionId']
|
||||
selected_version_source_app = data['selectedVersionSourceApp']
|
||||
selected_version_user_id = data['selectedVersionUserId']
|
||||
latest_version_id = data['latestVersionId']
|
||||
latest_version_source_app = data['latestVersionSourceApp']
|
||||
latest_version_user_id = data['latestVersionUserId']
|
||||
has_dismissed_update_warning = data['hasDismissedUpdateWarning']
|
||||
baked_object_ids = data['bakedObjectIds'].nil? ? nil : data['bakedObjectIds'].values
|
||||
|
||||
receive_card = Cards::ReceiveCard.new(model_card_id, account_id, server_url, workspace_id, workspace_slug,
|
||||
project_id, model_id,
|
||||
project_name, model_name,
|
||||
selected_version_id, selected_version_source_app, selected_version_user_id,
|
||||
latest_version_id, latest_version_source_app, latest_version_user_id,
|
||||
has_dismissed_update_warning, expired, baked_object_ids)
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler
|
||||
.save_receive_card_to_model(receive_card, state.sketchup_state.sketchup_model)
|
||||
new_speckle_state = state.speckle_state.with_receive_card(receive_card)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
return state.with_add_queue_js_command('addReceiveCard', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../cards/send_card'
|
||||
require_relative '../../cards/receive_card'
|
||||
require_relative '../../filters/send/everything_filter'
|
||||
require_relative '../../filters/send/selection_filter'
|
||||
require_relative '../../filters/send_filters'
|
||||
require_relative '../../settings/card_settings'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to add send model card.
|
||||
class AddSendModelCard < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, data)
|
||||
send_filter = Filters::SendFilters.get_filter_from_ui_data(data['sendFilter'])
|
||||
# settings = Settings::CardSetting.get_setting_from_ui_data(data['settings'])
|
||||
# Init card and add to the state
|
||||
send_card = Cards::SendCard.new(
|
||||
data['modelCardId'],
|
||||
data['accountId'],
|
||||
data['serverUrl'],
|
||||
data['workspaceId'],
|
||||
data['workspaceSlug'],
|
||||
data['projectId'],
|
||||
data['projectName'],
|
||||
data['modelId'],
|
||||
data['modelName'],
|
||||
data['latestCreatedVersionId'],
|
||||
send_filter,
|
||||
data['settings']
|
||||
)
|
||||
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler
|
||||
.save_send_card_to_model(send_card, state.sketchup_state.sketchup_model)
|
||||
|
||||
new_speckle_state = state.speckle_state.with_send_card(send_card)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
# Resolve promise
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('addSendCard', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Get connector version.
|
||||
class GetConnectorVersion < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
js_command = "baseBinding.receiveResponse('#{resolve_id}', '#{CONNECTOR_VERSION}')"
|
||||
state.with_add_queue_js_command('getConnectorVersion', js_command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../filters/send_filters'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Gets document state.
|
||||
class GetDocumentState < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
send_cards_hash = SketchupModel::Dictionary::ModelCardDictionaryHandler
|
||||
.get_send_cards_from_dict(state.sketchup_state.sketchup_model)
|
||||
|
||||
# TODO: CONVERTER_V2: Extract into new actions
|
||||
send_cards = send_cards_hash.collect do |id, card|
|
||||
filter = Filters::SendFilters.get_filter_from_document(card['sendFilter'])
|
||||
settings = Settings::CardSetting.get_filter_from_document(card['sendSettings'])
|
||||
send_card = Cards::SendCard.new(
|
||||
id,
|
||||
card['account_id'],
|
||||
card['server_url'],
|
||||
card['workspace_id'],
|
||||
card['workspace_slug'],
|
||||
card['project_id'],
|
||||
card['project_name'],
|
||||
card['model_id'],
|
||||
card['model_name'],
|
||||
card['latest_created_version_id'],
|
||||
filter,
|
||||
settings
|
||||
)
|
||||
|
||||
new_speckle_state = state.speckle_state.with_send_card(send_card)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
{
|
||||
modelCardId: send_card.model_card_id,
|
||||
accountId: send_card.account_id,
|
||||
serverUrl: send_card.server_url,
|
||||
workspaceId: send_card.workspace_id,
|
||||
workspaceSlug: send_card.workspace_slug,
|
||||
projectId: send_card.project_id,
|
||||
modelId: send_card.model_id,
|
||||
sendFilter: send_card.send_filter,
|
||||
settings: send_card.send_settings,
|
||||
latestCreatedVersionId: send_card.latest_created_version_id,
|
||||
typeDiscriminator: send_card.type_discriminator
|
||||
}
|
||||
end
|
||||
|
||||
receive_cards_hash = SketchupModel::Dictionary::ModelCardDictionaryHandler
|
||||
.get_receive_cards_from_dict(state.sketchup_state.sketchup_model)
|
||||
|
||||
# TODO: CONVERTER_V2: Extract into new actions
|
||||
receive_cards = receive_cards_hash.collect do |id, card|
|
||||
receive_card = Cards::ReceiveCard.new(id, card['account_id'], card['server_url'], card['workspace_id'], card['workspace_slug'], card['project_id'], card['model_id'],
|
||||
card['project_name'], card['model_name'], card['selected_version_id'],
|
||||
card['selected_version_source_app'], card['selected_version_user_id'],
|
||||
card['latest_version_id'], card['latest_version_source_app'],
|
||||
card['latest_version_user_id'], card['has_dismissed_update_warning'],
|
||||
card['expired'], card['baked_object_ids'])
|
||||
|
||||
new_speckle_state = state.speckle_state.with_receive_card(receive_card)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
{
|
||||
modelCardId: receive_card.model_card_id,
|
||||
accountId: receive_card.account_id,
|
||||
serverUrl: receive_card.server_url,
|
||||
workspaceId: receive_card.workspace_id,
|
||||
workspaceSlug: receive_card.workspace_slug,
|
||||
projectId: receive_card.project_id,
|
||||
modelId: receive_card.model_id,
|
||||
projectName: receive_card.project_name,
|
||||
modelName: receive_card.model_name,
|
||||
selectedVersionId: receive_card.selected_version_id,
|
||||
selectedVersionSourceApp: receive_card.selected_version_source_app,
|
||||
selectedVersionUserId: receive_card.selected_version_user_id,
|
||||
latestVersionId: receive_card.latest_version_id,
|
||||
latestVersionSourceApp: receive_card.latest_version_source_app,
|
||||
latestVersionUserId: receive_card.latest_version_user_id,
|
||||
hasDismissedUpdateWarning: receive_card.has_dismissed_update_warning,
|
||||
expired: receive_card.expired,
|
||||
bakedObjectIds: receive_card.baked_object_ids,
|
||||
typeDiscriminator: receive_card.type_discriminator
|
||||
}
|
||||
end
|
||||
|
||||
model_state = { models: send_cards + receive_cards }
|
||||
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}', #{model_state.to_json})"
|
||||
state.with_add_queue_js_command('getDocumentState', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../filters/send_filters'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to get send filter.
|
||||
class GetSendFilters < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
default_filters = Filters::SendFilters.get_default(state.sketchup_state.sketchup_model)
|
||||
js_script = "sendBinding.receiveResponse('#{resolve_id}', #{default_filters.to_json})"
|
||||
state.with_add_queue_js_command('getSendFilter', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../settings/card_settings'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to get send settings.
|
||||
class GetSendSettings < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
# NOTE: below code is tested and works!
|
||||
# default_settings = [
|
||||
# Settings::CardSetting.new(id: "includeAttributes", title: "Include Attributes", type: "boolean", value: true),
|
||||
# Settings::CardSetting.new(id: "test", title: "Test", type: "string", value: "a", enum: %w[a b c])
|
||||
# ]
|
||||
default_settings = []
|
||||
js_script = "sendBinding.receiveResponse('#{resolve_id}', #{default_settings.to_json})"
|
||||
state.with_add_queue_js_command('getSendSettings', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Get source app name.
|
||||
class GetSourceAppName < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
js_command = "baseBinding.receiveResponse('#{resolve_id}', 'sketchup')"
|
||||
state.with_add_queue_js_command('getSourceAppName', js_command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Get source app version.
|
||||
class GetSourceAppVersion < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
js_command = "baseBinding.receiveResponse('#{resolve_id}', #{SKETCHUP_VERSION})"
|
||||
state.with_add_queue_js_command('getSourceAppVersion', js_command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../sketchup_model/query/entity'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to add send card.
|
||||
class HighlightModel < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, model_card_id)
|
||||
receiver_card = state.speckle_state.receive_cards[model_card_id]
|
||||
sender_card = state.speckle_state.send_cards[model_card_id]
|
||||
card = receiver_card || sender_card
|
||||
|
||||
objects_to_highlight = if card.type_discriminator == 'ReceiverModelCard'
|
||||
state.speckle_state.receive_cards[model_card_id].baked_object_ids
|
||||
else
|
||||
state.speckle_state.send_cards[model_card_id].send_filter.selected_object_ids
|
||||
end
|
||||
|
||||
SketchupModel::Utils::ViewUtils.highlight_entities(state.sketchup_state.sketchup_model, objects_to_highlight)
|
||||
|
||||
# Resolve promise
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('highlightModel', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../sketchup_model/query/entity'
|
||||
require_relative '../../sketchup_model/utils/view_utils'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to add send card.
|
||||
class HighlightObjects < Action
|
||||
def self.update_state(state, resolve_id, object_ids)
|
||||
SketchupModel::Utils::ViewUtils.highlight_entities(state.sketchup_state.sketchup_model, object_ids)
|
||||
|
||||
# Resolve promise
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('highlightObjects', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../cards/send_card'
|
||||
require_relative '../../cards/receive_card'
|
||||
require_relative '../../filters/send/everything_filter'
|
||||
require_relative '../../filters/send/selection_filter'
|
||||
require_relative '../../filters/send_filters'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to remove send card.
|
||||
class RemoveModel < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, data)
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler.remove_card_dict(state.sketchup_state.sketchup_model, data)
|
||||
new_speckle_state = if data['typeDiscriminator'] == 'ReceiverModelCard'
|
||||
state.speckle_state.without_receive_card(data['id'])
|
||||
else
|
||||
state.speckle_state.without_send_card(data['id'])
|
||||
end
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
# Resolve promise
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('removeModel', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../cards/send_card'
|
||||
require_relative '../../cards/receive_card'
|
||||
require_relative '../../filters/send/everything_filter'
|
||||
require_relative '../../filters/send/selection_filter'
|
||||
require_relative '../../filters/send_filters'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to remove cards.
|
||||
class RemoveModels < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, data)
|
||||
data.each do |model_card|
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler.remove_card_dict(state.sketchup_state.sketchup_model, model_card)
|
||||
new_speckle_state = if model_card['typeDiscriminator'] == 'ReceiverModelCard'
|
||||
state.speckle_state.without_receive_card(model_card['id'])
|
||||
else
|
||||
state.speckle_state.without_send_card(model_card['id'])
|
||||
end
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
|
||||
# Resolve promise
|
||||
js_script = "baseBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('removeModels', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../sketchup_model/dictionary/model_card_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to update send filter.
|
||||
class UpdateSendFilter < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, data, value)
|
||||
SketchupModel::Dictionary::ModelCardDictionaryHandler.update_filter(state.sketchup_state.sketchup_model, data, value)
|
||||
|
||||
js_script = "sendBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('updateSendFilter', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Clear mapper source.
|
||||
class ClearMapperSource < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, _resolve_id, _data)
|
||||
new_speckle_state = state.speckle_state.with_removed_mapper_source
|
||||
erase_levels(state)
|
||||
state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
def self.erase_levels(state)
|
||||
levels = state.sketchup_state.sketchup_model.definitions.select do |definition|
|
||||
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.get_attribute(definition, :speckle_type) ==
|
||||
OBJECTS_BUILTELEMENTS_REVIT_LEVEL
|
||||
end
|
||||
levels.each do |level|
|
||||
level.entities.clear!
|
||||
level.instances.each(&:erase!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative 'mapped_entities_updated'
|
||||
require_relative 'events/selection_event_action'
|
||||
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Clear mappings for selected entities.
|
||||
class ClearMappings < Action
|
||||
def initialize(entities_to_map, is_definition)
|
||||
super()
|
||||
@entities_to_map = entities_to_map
|
||||
@is_definition = is_definition
|
||||
end
|
||||
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
def update_state(state)
|
||||
sketchup_model = state.sketchup_state.sketchup_model
|
||||
entities = if sketchup_model.active_path.nil?
|
||||
sketchup_model.entities
|
||||
else
|
||||
sketchup_model.active_path.last.definition.entities
|
||||
end
|
||||
|
||||
# Collect entities from entity ids that comes from UI as list
|
||||
entities_to_map = entities.select { |e| @entities_to_map.include?(e.persistent_id.to_s) }
|
||||
|
||||
# Switch to definitions if all entities are component instance and UI flag shows that
|
||||
if entities_to_map.all? { |e| e.is_a?(Sketchup::ComponentInstance) } && @is_definition
|
||||
entities_to_map = entities_to_map.collect(&:definition).uniq
|
||||
end
|
||||
|
||||
# Store speckle state to update with mapped entities.
|
||||
speckle_state = state.speckle_state
|
||||
entities_to_map.each do |entity|
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.remove_dictionary(entity)
|
||||
speckle_state = speckle_state.with_removed_mapped_entity(entity)
|
||||
end
|
||||
|
||||
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
|
||||
Events::SelectionEventAction.update_state(new_state, { clear: true })
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative 'mapped_entities_updated'
|
||||
require_relative 'events/selection_event_action'
|
||||
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Clear mappings for selected entities from mapped elements table.
|
||||
class ClearMappingsFromTable < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, _resolve_id, data)
|
||||
# Flat entities to clear mappings
|
||||
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
|
||||
|
||||
# Collect entity ids to clear mappings
|
||||
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
|
||||
# Store speckle state to update with mapped entities.
|
||||
speckle_state = state.speckle_state
|
||||
flat_entities.each do |entity|
|
||||
next unless entity_ids.include?(entity.persistent_id.to_s)
|
||||
|
||||
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.remove_dictionary(entity)
|
||||
speckle_state = speckle_state.with_removed_mapped_entity(entity)
|
||||
end
|
||||
|
||||
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
|
||||
Events::SelectionEventAction.update_state(new_state, { clear: true })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Clear queue from state.
|
||||
class ClearQueue < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state)
|
||||
new_speckle_state = state.speckle_state.with(:@message_queue => {})
|
||||
state.with(:@speckle_state => new_speckle_state)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative '../ext/sqlite3'
|
||||
require_relative '../constants/path_constants'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to collect preferences from database to UI.
|
||||
class CollectPreferences < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, _resolve_id, _data)
|
||||
state.with_add_queue('collectPreferences', state.user_state.preferences.to_json, [])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to collect versions from sketchup and connector to track user's version by mixpanel.
|
||||
class CollectVersions < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, _resolve_id, _data)
|
||||
versions = {
|
||||
sketchup: Sketchup.version.to_i,
|
||||
speckle: SpeckleConnector3::CONNECTOR_VERSION
|
||||
}
|
||||
state.with_add_queue('collectVersions', versions.to_json, [])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to get config.
|
||||
class GetConfig < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
# Previously it was stored in user state
|
||||
# config = state.user_state.preferences.to_json
|
||||
config = {
|
||||
darkTheme: state.user_state.user_preferences[:dark_theme]
|
||||
}
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}', #{config.to_json})"
|
||||
state.with_add_queue_js_command('getConfig', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to get is dev mode.
|
||||
class GetIsDevMode < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}', #{DEV_MODE})"
|
||||
state.with_add_queue_js_command('getIsDevMode', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../preferences/preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
class GetUserSelectedAccountId < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id)
|
||||
user_selected_account_id = Preferences.get_user_selected_account_id
|
||||
accountsConfig = {
|
||||
userSelectedAccountId: user_selected_account_id
|
||||
}
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}', #{accountsConfig.to_json})"
|
||||
state.with_add_queue_js_command('getUserSelectedAccountId', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../../preferences/preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
class SetUserSelectedAccountId < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, account_id)
|
||||
Preferences.set_user_selected_account_id(account_id)
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('setUserSelectedAccountId', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../action'
|
||||
require_relative '../user_preferences_updated'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to update config.
|
||||
class UpdateConfig < Action
|
||||
KEY_VALUES = {
|
||||
'darkTheme' => 'dark_theme'
|
||||
}.freeze
|
||||
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, resolve_id, config)
|
||||
config.each do |key, value|
|
||||
state = Actions::UserPreferencesUpdated.new('Sketchup', KEY_VALUES[key], value).update_state(state)
|
||||
end
|
||||
|
||||
js_script = "configBinding.receiveResponse('#{resolve_id}')"
|
||||
state.with_add_queue_js_command('updateConfig', js_script)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Action to update connected state of application.
|
||||
class Connected < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state)
|
||||
puts 'Speckle connected!'
|
||||
state.with(:@connected => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
require_relative '../accounts/accounts'
|
||||
require_relative '../actions/save_stream'
|
||||
require_relative '../actions/queue_send'
|
||||
require_relative '../convertors/converter'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Create stream.
|
||||
class CreateStream < Action
|
||||
def initialize(stream_name: nil)
|
||||
super()
|
||||
@stream_name = stream_name
|
||||
end
|
||||
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def update_state(state)
|
||||
puts 'send to speckle'
|
||||
acct = Accounts.default_account
|
||||
if acct.nil?
|
||||
puts 'No local account found. Please refer to speckle.guide for more information.'
|
||||
return state
|
||||
end
|
||||
sketchup_model = state.sketchup_state.sketchup_model
|
||||
path = sketchup_model.path
|
||||
if @stream_name.nil?
|
||||
@stream_name = path ? File.basename(path, '.*') : 'Untitled SketchUp Model'
|
||||
end
|
||||
query = 'mutation streamCreate($stream: StreamCreateInput!) {streamCreate(stream: $stream)}'
|
||||
vars = { stream: { name: @stream_name, description: 'Stream created from SketchUp' } }
|
||||
request = Sketchup::Http::Request.new("#{acct['serverInfo']['url']}/graphql", Sketchup::Http::POST)
|
||||
request.headers = { 'Authorization' => "Bearer #{acct['token']}", 'Content-Type' => 'application/json' }
|
||||
request.body = { query: query, variables: vars }.to_json
|
||||
to_convert = if sketchup_model.selection.count > 0
|
||||
sketchup_model.selection
|
||||
else
|
||||
sketchup_model.entities
|
||||
end
|
||||
state = evaluate_request(sketchup_model, request, state, to_convert)
|
||||
Actions::LoadSavedStreams.update_state(state, {})
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
private
|
||||
|
||||
def evaluate_request(sketchup_model, request, state, to_convert)
|
||||
converter = Converters::Converter.new(sketchup_model)
|
||||
|
||||
request.start do |_req, res|
|
||||
res_data = JSON.parse(res.body)['data']
|
||||
raise(StandardError) unless res_data
|
||||
|
||||
stream_id = res_data['streamCreate']
|
||||
state = Actions::SaveStream.new(stream_id).update_state(state)
|
||||
converted = to_convert.map { |entity| converter.convert_to_speckle(entity) }
|
||||
state = Actions::QueueSend.new(stream_id, converted).update_state(state)
|
||||
end
|
||||
state
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'action'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Deactivate diffing.
|
||||
class DeactivateDiffing < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, _resolve_id, _data)
|
||||
puts 'Diffing deactivated!'
|
||||
speckle_entities = state.speckle_state.speckle_entities
|
||||
diffing_activated_speckle_entities = speckle_entities.reject do |_id, entity|
|
||||
entity.active_diffing_stream_id.nil?
|
||||
end
|
||||
diffing_activated_speckle_entities.each do |id, entity|
|
||||
new_entity = entity.deactivate_diffing
|
||||
speckle_entities = speckle_entities.put(id, new_entity)
|
||||
end
|
||||
new_speckle_state = state.speckle_state.with_speckle_entities(speckle_entities)
|
||||
state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative 'on_document_changed'
|
||||
require_relative '../load_sketchup_model'
|
||||
require_relative '../collect_preferences'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
module Events
|
||||
# Handle events that are triggered by the {AppObserver}.
|
||||
class AppEventAction < EventAction
|
||||
# Handle loading new or existing model
|
||||
class OnNewOrChangedModel
|
||||
# Handle events when the new or existing model is loaded in Sketchup
|
||||
# @param state [States::State] the current state of speckle application
|
||||
# @param event_data [Array<(Sketchup::Model)>] the event data for the given event. It consists of
|
||||
# a double array with a single element that is the {Sketchup::Model} object of the loaded model.
|
||||
def self.update_state(state, event_data)
|
||||
return state unless event_data&.any?
|
||||
|
||||
model = event_data.flatten.first
|
||||
# LoadSketchupModel action should be responsible to update all model specific data for state and then
|
||||
# should notify the UI to update it's components.
|
||||
new_state = Actions::LoadSketchupModel.update_state(state, model)
|
||||
# Action to let UI to render itself with new preferences state
|
||||
# TODO: Later UI should be updated if any stream is invalid after
|
||||
# we collected speckle_entities appropriately
|
||||
new_state = CollectPreferences.update_state(new_state, nil, {})
|
||||
OnDocumentChanged.update_state(new_state)
|
||||
end
|
||||
end
|
||||
|
||||
# Run actions that are needed before the sketchup quits
|
||||
class OnQuit
|
||||
# Handle when Sketchup application closes
|
||||
# @param state [States::State] the current state of speckle application
|
||||
# @param _event_data [Array] the event data
|
||||
# @return [States::State] the transformed state object
|
||||
def self.update_state(state, _event_data)
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
# Handlers that are used to handle specific events
|
||||
ACTIONS = {
|
||||
onNewModel: OnNewOrChangedModel,
|
||||
onOpenModel: OnNewOrChangedModel,
|
||||
onQuit: OnQuit
|
||||
}.freeze
|
||||
|
||||
def self.actions
|
||||
ACTIONS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,161 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative '../../actions/send_actions/send_card_expiration_check'
|
||||
require_relative '../../sketchup_model/utils/face_utils'
|
||||
require_relative '../../constants/dict_constants'
|
||||
require_relative '../../sketchup_model/query/path'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
module Events
|
||||
# Event actions related to entities.
|
||||
class EntitiesEventAction < EventAction
|
||||
PATH = SketchupModel::Query::Path
|
||||
# Event action when element added.
|
||||
class OnElementAdded
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, event_data)
|
||||
modified_entities = event_data.to_a.collect { |e| e[1] }
|
||||
# do not copy speckle base object specific attributes, because they are entity specific
|
||||
modified_entities.each { |entity| entity.delete_attribute(SPECKLE_BASE_OBJECT) }
|
||||
|
||||
# All parent ids on current active path
|
||||
parent_ids = PATH.parents_with_definitions(state.sketchup_state.sketchup_model).collect(&:persistent_id).collect(&:to_s)
|
||||
|
||||
# All instances that changed potentially because of potential definition update
|
||||
path_instance_ids = PATH.instances(state.sketchup_state.sketchup_model).collect(&:persistent_id).collect(&:to_s)
|
||||
wrapped_entity_ids = wrapped_entity_ids(modified_entities)
|
||||
ids_to_check = parent_ids + wrapped_entity_ids + path_instance_ids +
|
||||
modified_entities.collect(&:persistent_id).collect(&:to_s) # NOTE: It fixes the same weird face problems. Ideally the push-pull result should hit the OnElementModified but for some cases we found the edge here......
|
||||
state = EntitiesEventAction.run_expiration_checks(state, ids_to_check) if ids_to_check.any?
|
||||
|
||||
attach_edge_entity_observer(modified_entities.grep(Sketchup::Edge), state.speckle_state.observers[ENTITY_OBSERVER])
|
||||
state
|
||||
end
|
||||
|
||||
def self.wrapped_entity_ids(modified_entities)
|
||||
wrapped_entity_ids = []
|
||||
modified_entities.select { |e| e.respond_to?(:definition) }.each do |c|
|
||||
wrapped_entity_ids += c.definition.entities.collect(&:persistent_id).collect(&:to_s)
|
||||
end
|
||||
wrapped_entity_ids
|
||||
end
|
||||
|
||||
# It is needed for attaching EntityObserver to newly added edges to track them with a hacky way.
|
||||
# This hacky way is because of limitation on Sketchup API that observer cannot catch changes on Edges
|
||||
# with EntitiesObserver.
|
||||
def self.attach_edge_entity_observer(edges, observer)
|
||||
edges.each do |edge|
|
||||
edge.add_observer(observer)
|
||||
edge.start.add_observer(observer)
|
||||
edge.end.add_observer(observer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Event action when element modified.
|
||||
class OnElementModified
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, event_data)
|
||||
modified_entities = event_data.collect { |data| data[1] }.to_a
|
||||
definition_faces = get_definition_faces(modified_entities)
|
||||
near_faces = get_near_faces(modified_entities)
|
||||
modified_persistent_ids = modified_entities.collect(&:persistent_id).collect(&:to_s) +
|
||||
definition_faces.collect(&:persistent_id).collect(&:to_s) +
|
||||
near_faces.collect(&:persistent_id).collect(&:to_s)
|
||||
|
||||
parent_ids = PATH.parents_with_definitions(state.sketchup_state.sketchup_model).collect(&:persistent_id).collect(&:to_s)
|
||||
|
||||
path_instance_ids = PATH.instances(state.sketchup_state.sketchup_model).collect(&:persistent_id).collect(&:to_s)
|
||||
modified_persistent_ids += parent_ids + path_instance_ids
|
||||
state = EntitiesEventAction.run_expiration_checks(state, modified_persistent_ids)
|
||||
# if modified_entity.is_a?(Sketchup::Face)
|
||||
# path = state.sketchup_state.sketchup_model.active_path
|
||||
# modified_faces = SketchupModel::Utils::FaceUtils.near_faces(modified_entity.edges)
|
||||
# path_objects = path.nil? ? [] : path + path.collect(&:definition)
|
||||
# parent_ids = path_objects.collect(&:persistent_id).collect(&:to_s)
|
||||
# ids_to_invalidate = modified_faces.collect(&:persistent_id).collect(&:to_s) + parent_ids
|
||||
# entities_to_invalidate = speckle_entities_to_invalidate(speckle_state, ids_to_invalidate)
|
||||
# new_speckle_state = invalidate_speckle_entities(speckle_state, entities_to_invalidate)
|
||||
# # This is the place we can send information to UI for diffing check
|
||||
# diffing = state.user_state.preferences[:user][:diffing]
|
||||
# new_speckle_state = new_speckle_state.with_invalid_streams_queue if diffing
|
||||
# return state.with_speckle_state(new_speckle_state)
|
||||
# end
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def self.get_near_faces(modified_entities)
|
||||
near_faces = []
|
||||
modified_entities.each do |modified_entity|
|
||||
if modified_entity.is_a?(Sketchup::Face)
|
||||
near_faces += SketchupModel::Utils::FaceUtils.near_faces(modified_entity.edges)
|
||||
elsif modified_entity.is_a?(Sketchup::Edge)
|
||||
near_faces += modified_entity.faces
|
||||
end
|
||||
end
|
||||
near_faces
|
||||
end
|
||||
|
||||
def self.get_definition_faces(modified_entities)
|
||||
definition_faces = []
|
||||
modified_entities.each do |modified_entity|
|
||||
next unless modified_entity.is_a?(Sketchup::Face)
|
||||
next unless modified_entity.parent.is_a?(Sketchup::ComponentDefinition)
|
||||
|
||||
definition_faces += modified_entity.parent.entities.grep(Sketchup::Face)
|
||||
end
|
||||
definition_faces
|
||||
end
|
||||
|
||||
# @param speckle_state [States::SpeckleState] the current state of the Speckle
|
||||
def self.speckle_entities_to_invalidate(speckle_state, ids)
|
||||
speckle_state.speckle_entities.to_h.select { |id, _| ids.include?(id) }
|
||||
end
|
||||
|
||||
# @param speckle_state [States::SpeckleState] the current state of the Speckle
|
||||
def self.invalidate_speckle_entities(speckle_state, entities_to_invalidate)
|
||||
speckle_entities = speckle_state.speckle_entities
|
||||
entities_to_invalidate.each do |id, speckle_entity|
|
||||
edited_speckle_entity = speckle_entity.with_invalid
|
||||
speckle_entities = speckle_entities.put(id, edited_speckle_entity)
|
||||
end
|
||||
speckle_state.with_speckle_entities(speckle_entities)
|
||||
end
|
||||
end
|
||||
|
||||
# Event action when element removed.
|
||||
class OnElementRemoved
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, event_data)
|
||||
modified_entity_ids = event_data.collect { |data| data[1] }.to_a.collect(&:to_s)
|
||||
new_speckle_state = state.speckle_state.with_changed_entity_ids(modified_entity_ids)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
Actions::SendCardExpirationCheck.update_state(state)
|
||||
end
|
||||
end
|
||||
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
# @param changed_persistent_ids [Array<String>] list of changed persistent ids
|
||||
def self.run_expiration_checks(state, changed_persistent_ids)
|
||||
new_speckle_state = state.speckle_state.with_changed_entity_persistent_ids(changed_persistent_ids)
|
||||
state = state.with_speckle_state(new_speckle_state)
|
||||
Actions::SendCardExpirationCheck.update_state(state)
|
||||
end
|
||||
|
||||
# Handlers that are used to handle specific events
|
||||
ACTIONS = {
|
||||
onElementRemoved: OnElementRemoved,
|
||||
onElementAdded: OnElementAdded,
|
||||
onElementModified: OnElementModified
|
||||
}.freeze
|
||||
|
||||
def self.actions
|
||||
ACTIONS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative '../../constants/dict_constants'
|
||||
require_relative '../../sketchup_model/query/path'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
module Events
|
||||
PATH = SketchupModel::Query::Path
|
||||
# Event actions related to entities.
|
||||
class EntityEventAction < EventAction
|
||||
# Event action when entity modified/changed.
|
||||
# PS: this handler action only triggers for edges and it's vertices since we attach EntityObserver to
|
||||
# only edge and vertex entities. This is a limitation of the Sketchup API that can't handles edges with
|
||||
# EntitiesObserver.
|
||||
class OnChangeEntity
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
def self.update_state(state, event_data)
|
||||
edges = []
|
||||
event_data.each do |event_d|
|
||||
event_d.each do |d|
|
||||
next if d.deleted?
|
||||
|
||||
edges.append(d) if d.is_a?(Sketchup::Edge)
|
||||
edges += d.edges if d.is_a?(Sketchup::Vertex) && d.edges
|
||||
end
|
||||
end
|
||||
|
||||
parent_ids = PATH.parents_with_definitions(state.sketchup_state.sketchup_model).collect(&:persistent_id).collect(&:to_s)
|
||||
path_instance_ids = PATH.instances(state.sketchup_state.sketchup_model).collect(&:persistent_id).collect(&:to_s)
|
||||
|
||||
edges.uniq!
|
||||
edge_ids = edges.collect(&:persistent_id).collect(&:to_s)
|
||||
new_speckle_state = state.speckle_state.with_changed_entity_persistent_ids(edge_ids + parent_ids + path_instance_ids)
|
||||
state.with_speckle_state(new_speckle_state)
|
||||
end
|
||||
end
|
||||
|
||||
# Handlers that are used to handle specific events
|
||||
ACTIONS = {
|
||||
onChangeEntity: OnChangeEntity
|
||||
}.freeze
|
||||
|
||||
def self.actions
|
||||
ACTIONS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# This module contains actions that are performed to handle events triggered by observers in Sketchup.
|
||||
module Events
|
||||
# Base action for Handling events
|
||||
class EventAction
|
||||
def self.actions
|
||||
raise NoMethodError, 'Implement in a subclass'
|
||||
end
|
||||
|
||||
# Handle the events that were collected by the observer. In case of the selection observer,
|
||||
# we only need to handle the events once if any of the events actually happened.
|
||||
# @param state [States::State] the current state of the SpeckleConnector Application
|
||||
# @param events [Hash{Symbol=>Array}] the event data grouped by the event name
|
||||
# @return [States::State] the transformed state
|
||||
def self.update_state(state, events)
|
||||
# Don't do anything if there are no events for this action
|
||||
return state unless events
|
||||
|
||||
actions = self.actions
|
||||
actions.each do |event_name, action|
|
||||
next unless events.key?(event_name)
|
||||
|
||||
event_data = events[event_name]
|
||||
state = action.update_state(state, event_data)
|
||||
end
|
||||
state
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative '../load_sketchup_model'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
module Events
|
||||
# Handle events that are triggered by the {ModelObserver}.
|
||||
class ModelEventAction < EventAction
|
||||
# Handle loading new or existing model
|
||||
class OnActivePathChanged
|
||||
# Handle events when the new or existing model is loaded in Sketchup
|
||||
# @param state [States::State] the current state of speckle application
|
||||
# @param event_data [Array<(Sketchup::Model)>] the event data for the given event. It consists of
|
||||
# a double array with a single element that is the {Sketchup::Model} object of the loaded model.
|
||||
def self.update_state(state, _event_data)
|
||||
sketchup_state = state.sketchup_state
|
||||
active_path = sketchup_state.sketchup_model.active_path
|
||||
observers = state.speckle_state.observers
|
||||
update_entity_observers(active_path, observers)
|
||||
return state
|
||||
end
|
||||
|
||||
def self.update_entity_observers(path, observers)
|
||||
unless path.nil?
|
||||
new_path_entities = path[-1].definition.entities
|
||||
new_path_entities.add_observer(observers[ENTITIES_OBSERVER])
|
||||
# attach observers to only orphan edges since face edges can be detected via face changes.
|
||||
edges = new_path_entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
|
||||
edges.each do |edge|
|
||||
edge.add_observer(observers[ENTITY_OBSERVER])
|
||||
edge.start.add_observer(observers[ENTITY_OBSERVER])
|
||||
edge.end.add_observer(observers[ENTITY_OBSERVER])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Handlers that are used to handle specific events
|
||||
ACTIONS = {
|
||||
onActivePathChanged: OnActivePathChanged
|
||||
}.freeze
|
||||
|
||||
def self.actions
|
||||
ACTIONS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
# Triggers whenever document has changed.
|
||||
class OnDocumentChanged < Action
|
||||
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state)
|
||||
js_command = "baseBinding.emit('documentChanged')"
|
||||
state.with_add_queue_js_command('documentChanged', js_command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'event_action'
|
||||
require_relative '../mapper_selection_changed'
|
||||
require_relative '../selection_actions/get_selection'
|
||||
require_relative '../../mapper/category/revit_category'
|
||||
require_relative '../../sketchup_model/reader/speckle_entities_reader'
|
||||
require_relative '../../sketchup_model/reader/mapper_reader'
|
||||
require_relative '../../sketchup_model/query/entity'
|
||||
|
||||
module SpeckleConnector3
|
||||
module Actions
|
||||
module Events
|
||||
# Update selected speckle objects when the selection changes for mapper tool.
|
||||
class SelectionEventAction < EventAction
|
||||
# @param state [States::State] the current state of Speckle application.
|
||||
# @return [States::State] the new updated state object
|
||||
def self.update_state(state, event_data)
|
||||
return state unless event_data&.any?
|
||||
|
||||
# POC: Not happy with it. We log also entity.entityID property since
|
||||
# onElementRemoved observer only return them! :/ Reconsider this in BETA!
|
||||
selected_object_ids = state.sketchup_state.sketchup_model.selection.collect(&:persistent_id).collect(&:to_s) +
|
||||
state.sketchup_state.sketchup_model.selection.collect(&:entityID).collect(&:to_s)
|
||||
summary = "Selected #{selected_object_ids.length / 2} objects." # POC: OFFF. I'll fix it
|
||||
selection_info = UiData::Sketchup::SelectionInfo.new(selected_object_ids, summary)
|
||||
js_script = "selectionBinding.emit('setSelection', #{selection_info.to_json})"
|
||||
state.with_add_queue_js_command('setSelection', js_script)
|
||||
|
||||
# Collect and return mapper selection info.
|
||||
# Later we can add more selection info for different scopes.
|
||||
# MapperSelectionChanged.new(sketchup_selection).update_state(state)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||