mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
1156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85a2a5ad12 | |||
| 49c3d28a0b | |||
| a8f20b2c6b | |||
| 66fa1cedd9 | |||
| eb971d3d73 | |||
| a30a796c07 | |||
| 9e46bacc12 | |||
| b33378e135 | |||
| d42e19f491 | |||
| 2597827ab3 | |||
| 3f71efea6d | |||
| 2d279b7a7f | |||
| e5fe039fd0 | |||
| 9e14829cc1 | |||
| e867602d9d | |||
| aecc3d56b8 | |||
| e3f5d3d2b7 | |||
| 65f8b1a532 | |||
| 96b4df10ba | |||
| 98b56aea42 | |||
| ba6ba29da8 | |||
| 1112b102f7 | |||
| 04b8172ea9 | |||
| f5f3edbc79 | |||
| 30029e3dc7 | |||
| ed836f5b4a | |||
| a88b708442 | |||
| 764c3bbb85 | |||
| 382d3f1f9f | |||
| c92f607a15 | |||
| b57b7080fd | |||
| 860ff4e513 | |||
| 3546f13593 | |||
| 663966e08d | |||
| f077ebdc81 | |||
| eb888fd116 | |||
| 40b6b3bf17 | |||
| 2ef2f923d0 | |||
| 75fab58831 | |||
| ce26c6e19b | |||
| 8728fa38b9 | |||
| 3037795199 | |||
| 39ee9cd8b1 | |||
| b375bbf558 | |||
| f2cae79dea | |||
| aadc77f6b6 | |||
| 35223d968c | |||
| e106dc4502 | |||
| 2995b73c2d | |||
| 770bc6ea61 | |||
| f5736a7144 | |||
| b4120b1cab | |||
| f44ca67ab9 | |||
| 77eae1c8e8 | |||
| 02d79f0687 | |||
| 83d364574a | |||
| 688d2b7bd1 | |||
| bf35239979 | |||
| ca8e014647 | |||
| b5f63ec4f3 | |||
| aec2141128 | |||
| 778344c804 | |||
| 3ede9b254f | |||
| 8df40dc4a6 | |||
| 75dd4f3132 | |||
| be11df3dc1 | |||
| 09ac212aa9 | |||
| e159151b44 | |||
| 27f6ac0575 | |||
| f840da3ffd | |||
| 034df852b2 | |||
| f242071ebb | |||
| b6663e9189 | |||
| 39d143668c | |||
| 6ea1e378c5 | |||
| a33f30e8ce | |||
| e79fb2ff0b | |||
| 054b4d0ee2 | |||
| 43bebf4eee | |||
| 532869a48a | |||
| 6569d0031f | |||
| bfaa61e592 | |||
| 1a09ce40e9 | |||
| 6f1b40fc86 | |||
| 734eec7715 | |||
| bfabc0db0f | |||
| e71aac84dc | |||
| c156100be2 | |||
| 48c2b43a58 | |||
| 2f8cfb5a95 | |||
| 1c4cc34c1b | |||
| b81e03d9c2 | |||
| eb96858460 | |||
| 5c7e70e987 | |||
| 9093c9d419 | |||
| 02a5b8d997 | |||
| f1b5f7d558 | |||
| 742a9ecd81 | |||
| 77a497cdf7 | |||
| 00576ef574 | |||
| c26caaa58d | |||
| b260bd3877 | |||
| cd6246c382 | |||
| a96d6dacc7 | |||
| 64c711a02a | |||
| 68a2480a66 | |||
| d5e2fd5d3c | |||
| bb445663f8 | |||
| 1f7db75572 | |||
| f478ec8d6b | |||
| cf3ba334b7 | |||
| c36af110c4 | |||
| 91eac7085b | |||
| 239d1d6fd2 | |||
| 831709d514 | |||
| 5925476ed9 | |||
| 3edb3c8df3 | |||
| 72badd8427 | |||
| ff251b7a6a | |||
| dbce57ffc5 | |||
| 8ff9bcef32 | |||
| 82b2006686 | |||
| ca4181c714 | |||
| 3928dd7c9c | |||
| 925964ee2a | |||
| 7201df8d22 | |||
| 7b65108a76 | |||
| 0fbe4d80c7 | |||
| 54c689d615 | |||
| ab828b1a8b | |||
| 8ddd349f8b | |||
| 3221219f4a | |||
| 36387f348e | |||
| c39c6d81e3 | |||
| e8042de1ed | |||
| 78fd999c53 | |||
| 91d1cb79c2 | |||
| 832618b84a | |||
| e4ce53d548 | |||
| 7b92b5c7e2 | |||
| c8c08439b2 | |||
| 3476ee8fd5 | |||
| d2f331c813 | |||
| 9e9b294f44 | |||
| f824ee8ca4 | |||
| 919f673108 | |||
| b034b40b09 | |||
| 34f038530f | |||
| 1c70990b06 | |||
| 07eecad433 | |||
| ef87f0fe74 | |||
| 23c2d73e8e | |||
| 0b47fb5987 | |||
| 8a1dee532b | |||
| f0c6c4ca59 | |||
| 92cf0d9265 | |||
| 7c438b6b9b | |||
| 46454c5c94 | |||
| 73aeafb063 | |||
| e06297940f | |||
| 1652629332 | |||
| 3d8de40407 | |||
| 308457300e | |||
| 6ef96841ac | |||
| 289b2d1e9d | |||
| 96db699ad2 | |||
| 6b7eec0db6 | |||
| 80f35d0bda | |||
| df390a655d | |||
| 593fca7c3d | |||
| 9335c8a0c8 | |||
| eccc21277d | |||
| 2ce7e16537 | |||
| 49835ee303 | |||
| f5500da0af | |||
| c9552fac29 | |||
| b11b3b7fa8 | |||
| 8aecee5836 | |||
| 2f3b3011ec | |||
| 491c9203b0 | |||
| 7f2b996d23 | |||
| d2ea6f6656 | |||
| 25128e8aa8 | |||
| 30fa917e69 | |||
| 40e494996d | |||
| cfdc54064e | |||
| 6daead6b61 | |||
| 241d52f0ad | |||
| 68dd93da1d | |||
| 82703670f0 | |||
| cb8462a0d0 | |||
| 000245c26c | |||
| d97ee8eed4 | |||
| f8af28f12a | |||
| f6fab30d0a | |||
| f1a4ed702b | |||
| c4080626df | |||
| a8078e38d2 | |||
| c52fe8abf9 | |||
| b310b5aaba | |||
| 7246e75085 | |||
| 48c0378bff | |||
| 91c90e7b86 | |||
| 72abdb0569 | |||
| 7947ba4aaf | |||
| 2520a93055 | |||
| fa480d99a2 | |||
| 72c057e7a0 | |||
| 61cfb361fa | |||
| 3786cfa666 | |||
| cabc06821f | |||
| f9fef4f5f6 | |||
| 7c89d072e2 | |||
| dbb9e8c907 | |||
| 2df5b4c70e | |||
| a5aa608240 | |||
| f7454366cc | |||
| f773480917 | |||
| 89f9cd0403 | |||
| b21159c6b3 | |||
| 227f556669 | |||
| 42bd69929c | |||
| a3532b24b3 | |||
| 5486c4c8e9 | |||
| 0259d4c0cd | |||
| 66c4c61dc2 | |||
| 0f32ffe232 | |||
| 72630de77a | |||
| 9395bed72b | |||
| 507a5cab83 | |||
| a46a60996d | |||
| 3106168889 | |||
| 7e0c0c5c83 | |||
| 19205c8774 | |||
| 62b5f1d728 | |||
| 299df62758 | |||
| e58bfde500 | |||
| b60306cdbd | |||
| 61e0549e65 | |||
| 52ae759218 | |||
| 9b41614eff | |||
| 58e01751c1 | |||
| 624134d033 | |||
| 22f83b68e4 | |||
| 1b35ea122e | |||
| f1deefbcb3 | |||
| d6c020b5ee | |||
| ff4eddf38a | |||
| 2b34dc4ce7 | |||
| 530260ebe7 | |||
| 594268c860 | |||
| e5fa8a01b4 | |||
| ad934cd9a6 | |||
| 92fe325816 | |||
| 818bb9c823 | |||
| d71fad988d | |||
| ff0d6c810f | |||
| 8b72929d22 | |||
| 6452872e83 | |||
| 88d2c61453 | |||
| f777be6403 | |||
| 9aad249586 | |||
| 7168f8eeff | |||
| da306c6141 | |||
| e53c0b74b1 | |||
| cb2f58ba0f | |||
| c0c7727f36 | |||
| 60526e62b4 | |||
| 496e58418a | |||
| b976ad3880 | |||
| 31a763fe12 | |||
| 93ef2d0349 | |||
| 7828508179 | |||
| 7aa6be8e8a | |||
| 0679956add | |||
| b86f924543 | |||
| 962b8fac67 | |||
| fdcb705551 | |||
| b80ca3c167 | |||
| e8bd272189 | |||
| 25bd94e84a | |||
| 5face7170f | |||
| 434a11a502 | |||
| eb91f5179b | |||
| c6643da4ba | |||
| 18c93a13ec | |||
| 7cb582de5d | |||
| a5bb458d3d | |||
| c7f6a5e621 | |||
| 141e0240d4 | |||
| 15cd2b6075 | |||
| fab8b5b702 | |||
| 844f4c4944 | |||
| 03809b3d5e | |||
| 3e268be5c7 | |||
| 6e932ec2fb | |||
| 58414777be | |||
| 14f1acadb7 | |||
| f215670381 | |||
| 11df5065e4 | |||
| ed47b6b227 | |||
| 6216ee9a98 | |||
| 848d6b294e | |||
| 795a372ac9 | |||
| 03a6ee9121 | |||
| 5299a9d8a2 | |||
| a5d5d72064 | |||
| b6f2a76385 | |||
| ea6c8fe2f8 | |||
| c7268b6f42 | |||
| 5937ed4b63 | |||
| 8f650c56e5 | |||
| 1d972a6a6f | |||
| 70a1d7d345 | |||
| 519d59effe | |||
| 7f5f67b131 | |||
| 8c64d9ddcc | |||
| 397f333db0 | |||
| b01bd8afb3 | |||
| 1dd8624825 | |||
| bc0c2e641b | |||
| f5f68ea150 | |||
| 76ce117b46 | |||
| 52cad9d11c | |||
| 58af81e256 | |||
| fc2da4ee8f | |||
| c04fa12051 | |||
| 623d2b5be1 | |||
| 46319634fb | |||
| 91d2e5ccd1 | |||
| d23e32c64a | |||
| 764efa1fed | |||
| b051adcc6c | |||
| b815719477 | |||
| 0612d2b615 | |||
| 0da210b732 | |||
| 9429e478fe | |||
| 2f6c50d9c2 | |||
| 640341715c | |||
| 42878e325e | |||
| ac5cf95f9e | |||
| c9189055c7 | |||
| 6a08993f5e | |||
| df2a50b335 | |||
| c7543ff48d | |||
| 1f75fa09ae | |||
| 94ce8e2c9c | |||
| 85e94d2cfc | |||
| c2b332da48 | |||
| 6ee4edd0a0 | |||
| 0d757027e3 | |||
| 9ffe56e64d | |||
| 37a9a1b5d9 | |||
| c30c9d50c9 | |||
| 8e64b55290 | |||
| b45001a95a | |||
| fd65009f5a | |||
| 52036e9bd4 | |||
| 5399767898 | |||
| f96d477d2b | |||
| d5f3991d06 | |||
| 2f2e81f585 | |||
| 0b7442b288 | |||
| 229c7285c2 | |||
| 4fb99d2eed | |||
| 123557939f | |||
| 3c396d3180 | |||
| 5d39603308 | |||
| b792673f88 | |||
| cf65612a96 | |||
| 50bbe849ff | |||
| dc438b2560 | |||
| 6750dd927c | |||
| 9fb1986cad | |||
| 60f8d30fc1 | |||
| 67be2e8ce8 | |||
| 2557580500 | |||
| c0d2ac6ff9 | |||
| 52c168051e | |||
| e072da8d65 | |||
| 8a63275db0 | |||
| 69be3a005c | |||
| ad9ad3f060 | |||
| f0e5304b8e | |||
| c6454803ee | |||
| e8aa8c44e5 | |||
| 48b237ab54 | |||
| 608b3e5401 | |||
| c5d38227fc | |||
| e98ff136c2 | |||
| 02ebc80525 | |||
| 66431158b5 | |||
| dd044c8516 | |||
| 55763ea7df | |||
| 8fe92e810f | |||
| c26988b292 | |||
| 46e70c92c5 | |||
| 325c3665d9 | |||
| 9a80dc14e9 | |||
| 6bda066e1b | |||
| 912b01587b | |||
| 5d4d953320 | |||
| 11ed8cc787 | |||
| d1a7b99eda | |||
| 85bdeaeef4 | |||
| a5f0de4d19 | |||
| cb3392ec70 | |||
| 1dbe42061d | |||
| a024d5c07f | |||
| ea5e3c30b6 | |||
| ebfc8ef619 | |||
| c0d8467154 | |||
| d0adbab9c2 | |||
| 8e80f456cb | |||
| 61bd98e7db | |||
| 4bf0d9ccff | |||
| 1e36ac8740 | |||
| af864e8556 | |||
| 8cd9fb9015 | |||
| d9fdc0908a | |||
| a218ce5160 | |||
| b3a6b8e07f | |||
| 52700452d9 | |||
| aab011f7bf | |||
| 5f129e2357 | |||
| 4e76f36af1 | |||
| 6f3ff51d91 | |||
| 0de0422c2e | |||
| 98de86e0ba | |||
| 142e41b3ad | |||
| 1e0dc7749d | |||
| 0f29de0391 | |||
| 9933cc1b20 | |||
| fa6192e9bc | |||
| bfd4559da8 | |||
| 01f52e069e | |||
| 2fbc9e4424 | |||
| ba97da20eb | |||
| 44f5be142f | |||
| 2f951d352b | |||
| 8b23c615c3 | |||
| 7c694e5b3e | |||
| 8eeb212003 | |||
| 7bf8d58cde | |||
| b6e24559e5 | |||
| 0d87d5927e | |||
| dfd5cc4728 | |||
| 20b7ee009d | |||
| 19a3f56941 | |||
| 3b298d44c1 | |||
| 038d3bc41c | |||
| 235e89ae14 | |||
| e2de9efc33 | |||
| 8126085037 | |||
| bbe14834e4 | |||
| 45f2d301b3 | |||
| 2ecd11d71f | |||
| b42f072bd5 | |||
| 3b3668eedf | |||
| 7f7f353131 | |||
| 53594de1ff | |||
| 4fbbfb5191 | |||
| 4e1daea838 | |||
| d8ff83da73 | |||
| a7712a923f | |||
| 01b5f8f237 | |||
| b9ad15c50f | |||
| db06cff997 | |||
| f0028c0094 | |||
| 1c954801b0 | |||
| 973e4f0064 | |||
| be5d291bd9 | |||
| cbec56c2f0 | |||
| 880d0ba753 | |||
| db85689364 | |||
| 48a0d8c508 | |||
| df4cd493ee | |||
| 74132dcd49 | |||
| ef72cd3250 | |||
| 5cf1dd4249 | |||
| 29bd30d3bf | |||
| 3f8a0a91b3 | |||
| 416a572664 | |||
| 388c72360d | |||
| b841afc2e4 | |||
| f10d340254 | |||
| 0d0b91ee9b | |||
| 4f1e59a613 | |||
| 5960e4153c | |||
| 8032333a17 | |||
| 712ec6def3 | |||
| 0d43ef0736 | |||
| b318509cba | |||
| f3b541f00a | |||
| 4f64dcdbd4 | |||
| 68acf84e26 | |||
| aaceb923ad | |||
| 074de2741a | |||
| 0e4bc320cb | |||
| 9e9412bec0 | |||
| 7e92471ac9 | |||
| 078a7eb290 | |||
| df3876b438 | |||
| d414de450b | |||
| 9e89bb5044 | |||
| aa0c42e1ec | |||
| a4daa0e3d9 | |||
| 162ac39570 | |||
| 8c73fa69a1 | |||
| 159eeeb3a4 | |||
| 66f735fde9 | |||
| 1a3aa7f031 | |||
| ac41a9e63a | |||
| eb0873996f | |||
| c85826ef2e | |||
| 627cf63a4c | |||
| 744c08e13e | |||
| dd7a6a80eb | |||
| ab807e3b9b | |||
| c35be50c83 | |||
| 37ab9c80e3 | |||
| 7ff98d2918 | |||
| 35d66f5d8c | |||
| 3f018a4195 | |||
| 578e15adb2 | |||
| c4474455e9 | |||
| 8f43854cbf | |||
| 787149da13 | |||
| 3b31f6932a | |||
| 6c9e130ca8 | |||
| 86a234d17e | |||
| f68baee5c3 | |||
| 0028fd3e9c | |||
| f5f9862b78 | |||
| f38952dc51 | |||
| 24eb149221 | |||
| ce8598a0b4 | |||
| fb9cf75412 | |||
| 68f5df73b9 | |||
| e068c523ec | |||
| aa182bf047 | |||
| 6e9639446d | |||
| c48c1c3b32 | |||
| 14224ac538 | |||
| 58be92d3fc | |||
| 7107a91f6f | |||
| 438ed22f8f | |||
| a9dfc9a9fd | |||
| f8b09b3525 | |||
| f1ab02b311 | |||
| 4e833e26ca | |||
| 25df2f6414 | |||
| 42719d3fe3 | |||
| 1f6d23c19c | |||
| 94db9ec522 | |||
| a47c57c22c | |||
| 8f4bc0ea00 | |||
| dd70b09fb1 | |||
| c930050da6 | |||
| 55a7a72e61 | |||
| ca2ae36d41 | |||
| 81b4e42ca2 | |||
| 55e61b55d8 | |||
| a99529cb1e | |||
| bf43efe509 | |||
| da7f5b015b | |||
| 3de9915389 | |||
| 418444a196 | |||
| 171251b27d | |||
| 9fef920a6f | |||
| 5bff837a61 | |||
| 05f6f4efab | |||
| e4f5b0e3d8 | |||
| 49b733f660 | |||
| 36ed618adf | |||
| 48042011e2 | |||
| 0e1939a871 | |||
| bea1e61739 | |||
| 72cd82f450 | |||
| befa059591 | |||
| 7229f7271c | |||
| 9c5970e757 | |||
| 2fe3f0a9df | |||
| 3929f02e94 | |||
| 1abf80c310 | |||
| 2c18c89807 | |||
| 1b81fa5aa1 | |||
| baa08b3221 | |||
| b9a9abd30d | |||
| 33536957d8 | |||
| d092fd45ba | |||
| 78c748df5d | |||
| 3d76de3041 | |||
| ed3e9f3347 | |||
| eb836436b9 | |||
| c34a9c52d0 | |||
| 88d96f7d57 | |||
| 69e447be1c | |||
| df09acfa31 | |||
| 6343f77c0d | |||
| 285fc2269e | |||
| 68a1da9190 | |||
| ceae7d4fdb | |||
| 371daa6c80 | |||
| 948993e066 | |||
| ec4f0e5ad6 | |||
| ef56c6ce7e | |||
| 3c02da416e | |||
| 6660bf7618 | |||
| 000e5ec29a | |||
| ef8f4df4ab | |||
| dff70f2608 | |||
| 0d7967ff62 | |||
| 4fe459dab6 | |||
| 0e5e1f7873 | |||
| 82d4829c83 | |||
| a24fab6d92 | |||
| 748055dcf1 | |||
| 3904162360 | |||
| 7ca24ede4c | |||
| 1e59b10479 | |||
| cda61bff5b | |||
| 09a9753ca7 | |||
| 1621449143 | |||
| d0ef7f69b1 | |||
| 3df95b8970 | |||
| ddd4b0e262 | |||
| 6b05b6e7fb | |||
| 9dd728ce7e | |||
| 90b40ff0dc | |||
| 3c27c52c9f | |||
| 05d4dc5666 | |||
| edaabf5bd2 | |||
| ad79c63a37 | |||
| 9461bf2129 | |||
| f2097c0c5f | |||
| d838e1808d | |||
| 5ed54d9e7c | |||
| 78aba1c7e4 | |||
| 3faaee80ec | |||
| a847b8f4e4 | |||
| 24170a8a16 | |||
| 5438331fe1 | |||
| 3c957e0cbe | |||
| 10672f3e1a | |||
| fa90e420eb | |||
| 347bb2df4b | |||
| 986ffe66f8 | |||
| c1bedec354 | |||
| 182b6c7b07 | |||
| 22edbddd84 | |||
| 50f6f44d26 | |||
| e93a697179 | |||
| f1a5805b1c | |||
| 126f530af1 | |||
| 5dfbbb8d96 | |||
| 6962e505f9 | |||
| 5c2523e752 | |||
| 635dff73b4 | |||
| c85e4e7ceb | |||
| 7ed4301c6c | |||
| 1193e7025a | |||
| cc1ee19115 | |||
| e975eac4a7 | |||
| 779fc35228 | |||
| 8e59282fe0 | |||
| 11c8d47909 | |||
| 2661999f3e | |||
| e4bfb05875 | |||
| fe14988d50 | |||
| d45bef5e70 | |||
| 82d03b98c6 | |||
| 9c1ecf3f2c | |||
| 8f6be60c6e | |||
| 81a1bd9206 | |||
| 9f7dffb29d | |||
| eee799bfc4 | |||
| 9665adb61d | |||
| e40019f3cb | |||
| b2f95bfa60 | |||
| be2c7a62fd | |||
| 38f5468f19 | |||
| 31dcbc0159 | |||
| bd81320c08 | |||
| 0707f37247 | |||
| 0993736cea | |||
| cc3e2204e2 | |||
| 8ac5f0318b | |||
| 270a61e73a | |||
| 1c057b1ce1 | |||
| e74e824417 | |||
| 73d1de5dcf | |||
| 4549be0d05 | |||
| e3f2f2175d | |||
| a604cf2b2a | |||
| d6b4c1f454 | |||
| d3258693b2 | |||
| 9eba3b6eaa | |||
| 7b75364feb | |||
| 4a4a0f09ef | |||
| 6d14c2d448 | |||
| 57edd47ed7 | |||
| aefcedf754 | |||
| 2c685c846f | |||
| 6536f7b56b | |||
| 9d3881b00e | |||
| baffbd6a32 | |||
| 9e4702c102 | |||
| b63f3f335c | |||
| 29b0bcb6b9 | |||
| 62fa0bb218 | |||
| 9e67565c71 | |||
| dee4cf591e | |||
| bc5d6ee113 | |||
| 5af3b537da | |||
| 4b159d400c | |||
| f3c012660a | |||
| b5b8b5df26 | |||
| 6d38d9b538 | |||
| ef11ae6540 | |||
| a3802c25c0 | |||
| 377bcfe6da | |||
| 63a5c0986f | |||
| 8320623210 | |||
| 90c4a2fd41 | |||
| 4ee66caaf9 | |||
| 23251de6f1 | |||
| 37287bac7e | |||
| 43a7340f5d | |||
| 2363b5e167 | |||
| 62bf8c2287 | |||
| a5a958d6e2 | |||
| 812d8508f4 | |||
| c40cc3f740 | |||
| 56040951f1 | |||
| 39b1a65dc5 | |||
| 438f35fe88 | |||
| 9240518066 | |||
| 27b71a9d07 | |||
| 0f1d675bef | |||
| 19345ff538 | |||
| 7b28bc9c68 | |||
| d67eae55df | |||
| 8e375dcf73 | |||
| a34d0cabfc | |||
| a668743d10 | |||
| b13cbbb244 | |||
| b6ae5fd344 | |||
| 1c85034e19 | |||
| b0d1823968 | |||
| f0e03c79df | |||
| 6109cdf41b | |||
| ec0873d527 | |||
| 63223172b4 | |||
| 9778253d21 | |||
| 0140b4ded1 | |||
| dca60b83a9 | |||
| 42e683375c | |||
| 66e07c2f69 | |||
| 2b067b9dec | |||
| c22d7d2f41 | |||
| ea3d489c24 | |||
| 97683a14ef | |||
| d79b912e0e | |||
| 7ec8f8b03e | |||
| 58199ef7c9 | |||
| d5c46514d3 | |||
| f400973557 | |||
| 148d6e7b1b | |||
| 74dec650a7 | |||
| 5f6e003e4e | |||
| d3df22b44b | |||
| 257208d55b | |||
| d8b9b09d14 | |||
| 6f9e14ceb0 | |||
| d854984dda | |||
| 2951721886 | |||
| 02801b32a6 | |||
| b243e04fd2 | |||
| 02902d9a41 | |||
| 5c33034455 | |||
| 7541293a3c | |||
| da7d4dac7a | |||
| 507ab8ec11 | |||
| e93ff7a6b1 | |||
| 098ae2b69f | |||
| a58a10e93d | |||
| 9be7d951c3 | |||
| 6cf482dc63 | |||
| aaa4683462 | |||
| 0b5535b6d4 | |||
| 45feaeaec4 | |||
| 90f92cd139 | |||
| 0285d97e3f | |||
| bf80e4b5e1 | |||
| d4e4717857 | |||
| fca90e9b0f | |||
| ffbb91216f | |||
| 09620e060f | |||
| 0ea38384cc | |||
| aae659c5fd | |||
| b7a481ec79 | |||
| 763eaf83da | |||
| 2843d1e13e | |||
| e897797955 | |||
| cc8f103465 | |||
| f1c3a90bc5 | |||
| c90c438324 | |||
| ae1ff3f6f7 | |||
| e2224386bf | |||
| 6ad51ebd18 | |||
| 0fb9f584cb | |||
| 9a34b94568 | |||
| 7bca3bcac3 | |||
| de27554043 | |||
| 306ee3dfa4 | |||
| 3593b6b5c2 | |||
| e06acab39b | |||
| 6dd4e0f53a | |||
| 2c2a37cacb | |||
| 7935bd22c2 | |||
| 6887210a1a | |||
| 33dc3a4b06 | |||
| ba4610155f | |||
| 28eeba49ba | |||
| 0a00e529c3 | |||
| c5024cf6be | |||
| 0f3b416c3f | |||
| addacd9ce5 | |||
| 5582bb2d4c | |||
| 8b27c0e755 | |||
| ca4efa6e50 | |||
| cfac835607 | |||
| 3aa72ae74a | |||
| 07579ee9a1 | |||
| fe4823681f | |||
| 6816ad5c0f | |||
| aace987149 | |||
| 339ab9eeab | |||
| eee844e204 | |||
| 46cfe70181 | |||
| 294935ceed | |||
| f2097b9036 | |||
| e48a3a06c0 | |||
| c07371cb76 | |||
| 8d450aa500 | |||
| 726c792fbe | |||
| 83dda3e73f | |||
| 5c78e29849 | |||
| e6d48cd37f | |||
| a3ecde3ad9 | |||
| 2bb64a6cae | |||
| 764c818d10 | |||
| 2d18185928 | |||
| 43468166d2 | |||
| fbfd6bc485 | |||
| 1578efe366 | |||
| 76948c5156 | |||
| 562ed5908d | |||
| 9caac14014 | |||
| 695f75f3a7 | |||
| 7d7c04ddc0 | |||
| 863bf8ebbe | |||
| 0fcc28f50c | |||
| 04e9a08f3d | |||
| 837c401116 | |||
| b087d5125f | |||
| a6bff09c78 | |||
| 4716c0b7b7 | |||
| 1d040a170e | |||
| 951026a95c | |||
| c6b82bfa56 | |||
| 01b6d90432 | |||
| 504a116ebf | |||
| be785a878c | |||
| 5c2b7404d2 | |||
| 84e9e2396a | |||
| 7687d5e26e | |||
| 25ce3b039e | |||
| 9c4c42e27e | |||
| 26dd850ac1 | |||
| 0380c286a5 | |||
| 10aca77de4 | |||
| b6f2fdef77 | |||
| 9cc1f5f0e3 | |||
| adcf22f281 | |||
| ad0c49ac37 | |||
| 73d77b8382 | |||
| 3e45f39ea2 | |||
| a2bb015c79 | |||
| b231f43fc7 | |||
| 383038fdb4 | |||
| fbd8ea1edb | |||
| 26e292dd34 | |||
| c520e16031 | |||
| 101972fbad | |||
| f23ebc4031 | |||
| 44431db092 | |||
| d1b6c1ffe4 | |||
| 4d98151b62 | |||
| 5e8e18e3b5 | |||
| 808ff548ec | |||
| bc9b482373 | |||
| 4506eb0df9 | |||
| 921d412fc7 | |||
| 6089d3af78 | |||
| b8168ca606 | |||
| 61a7325bc0 | |||
| c483116b32 | |||
| 55a623dc97 | |||
| ad420108b0 | |||
| 9944c7d2de | |||
| 1a8f2be4fd | |||
| 13f64fdfe2 | |||
| 38a9e29102 | |||
| 3c23a57138 | |||
| 7baed872c6 | |||
| 9a7a284561 | |||
| e9c24a2656 | |||
| 5c73ef0a6d | |||
| 6c29c53a72 | |||
| dadcbe61ea | |||
| 93502a05ea | |||
| 5ebde4c79a | |||
| cf21ae22f1 | |||
| cc500ae833 | |||
| 256c0baa14 | |||
| 66decc604e | |||
| 356bc18078 | |||
| 1344cbc192 | |||
| e00ffb07ad | |||
| dba43590a5 | |||
| a0613cfee6 | |||
| c3de72b71a | |||
| 39fc60a482 | |||
| 0d7e71a269 | |||
| 78a2f65ec3 | |||
| 802bd74c52 | |||
| 33cf40debb | |||
| ff63fd9f6f | |||
| 5b207e6f54 | |||
| 695dc6ec5a | |||
| d3bcb8c6b9 | |||
| 6226aea3c3 | |||
| a30ad9b886 | |||
| 2b1e17681f | |||
| 8f25a31f19 | |||
| 1c7e6cf1b9 | |||
| fc7459c07f | |||
| 8f6932de64 | |||
| b708100c03 | |||
| d4e2d3d2dd | |||
| 13309cb8c4 | |||
| 5a4bc0909b | |||
| 39540e5794 | |||
| 0a0f977b5c | |||
| 13a75e7ad7 | |||
| 99574d6255 | |||
| 968cceb725 | |||
| 002985a446 | |||
| 5adc29b0b3 | |||
| 52ea4ff44d | |||
| 4e31a87c20 | |||
| 5d85390715 | |||
| 3e13eb8942 | |||
| 262c6d1fcb | |||
| 12b5a88861 | |||
| 2ccc9d886e | |||
| a78769c8ff | |||
| 1a142ad5eb | |||
| 2bc22e150c | |||
| aad5d280fd | |||
| 7ab07ed149 | |||
| 3690998dbc | |||
| 1b83fcc1c7 | |||
| 5cf25c45f9 | |||
| efa734c06e | |||
| e242244ec0 | |||
| 6f9da163fb | |||
| a3fd169bb6 | |||
| 899056f929 | |||
| 132fef1b76 | |||
| 422f772c40 | |||
| 66f5081ada | |||
| 839e35f1da | |||
| 40d9713326 | |||
| 17b04ad697 | |||
| 2f99dd8e98 | |||
| 49f825ba82 | |||
| 3f18ad0bc5 | |||
| 991c42e816 | |||
| 20d6eae50a | |||
| 9740582a84 | |||
| a8046a6d69 | |||
| d6655a5c14 | |||
| 0a3a2db4ae | |||
| 499eb5ea14 | |||
| b8ca287883 | |||
| 52f3392ece | |||
| d53641ce5e | |||
| 7a3ad6c3a1 | |||
| 0b2007011e | |||
| eae6983ca5 | |||
| 0ec337748f | |||
| 2699278ba3 | |||
| 0406166d8a | |||
| 2128eda87b | |||
| 59e11aa804 | |||
| 21f4fb169f | |||
| 30fc52ebd9 | |||
| 64bc7af4ef | |||
| b3f1fc667a | |||
| fcbb28f9d8 | |||
| 240a31db3a | |||
| c1bde09585 | |||
| 5dd2b5e555 | |||
| 37dcb92220 | |||
| 6c43c60d04 | |||
| b8d191ac1c | |||
| 57173c7740 | |||
| 2d3295ec54 | |||
| 941ede117c | |||
| 4210b8333f | |||
| 1723d1e5a9 | |||
| 3ee06ee7a0 | |||
| d3e8da58dc | |||
| 29e000b939 | |||
| 6686a72d1f | |||
| 7dd20f69d7 | |||
| 0044ef5588 | |||
| ffa5686b22 | |||
| ab8d6bea99 | |||
| e7bb89f582 | |||
| c80b0464e5 | |||
| 7e3547b309 | |||
| 45c7432ff7 | |||
| c98c20d403 | |||
| d26a2580ea | |||
| f84c4ab2e5 | |||
| de6676a516 | |||
| bfbb7bec94 | |||
| ac00aaae4d | |||
| 422222a7cb | |||
| e1c7b37d62 | |||
| dec978743b | |||
| 10fae033c8 | |||
| b23180d287 | |||
| 99ccd81b3f | |||
| e2d3f3a351 | |||
| 5e6a444aa2 | |||
| 72cf5cfc59 | |||
| 75cb68d2f1 | |||
| 7f40a9d723 | |||
| dcc9d0e9cd | |||
| 7228329f5e | |||
| d8786717b6 | |||
| b73bd3506f | |||
| bdb2791b35 | |||
| 549ea11945 | |||
| aed01b5703 | |||
| 13eb8562e0 | |||
| c022a12835 | |||
| 04eb47ff65 | |||
| 10ecd16f0c | |||
| 75852bfa75 | |||
| 750c720c9b | |||
| 4697681fcb | |||
| 9398363cbf | |||
| ae45e6b960 | |||
| 88c002703e | |||
| 51c78a89bd | |||
| e36a958c0d | |||
| a8126a6f90 | |||
| 9b857cdefa | |||
| 90e6400115 | |||
| 2dcfcf9708 | |||
| f4971da42e | |||
| 008f953b6c | |||
| 5481237480 | |||
| 4f7564e5ae | |||
| d214cbaece | |||
| d0c5f1e69f | |||
| 4ba3240fb4 | |||
| 6d8e5e4c83 | |||
| cd1402570b | |||
| 6c0e528dd2 | |||
| 297a513d21 | |||
| 9f0a4ba064 | |||
| 8813ed66e0 | |||
| fafcd5dfca | |||
| 155a8b79da | |||
| f523870b57 | |||
| 2ed24434c8 | |||
| 6878b2c072 | |||
| 0ed8dc970d | |||
| bc85f09109 | |||
| 793b28e43b | |||
| ed4abacd1e | |||
| 8bf55ab46c | |||
| 7edabce657 | |||
| 56187e4da3 | |||
| 31a12faef1 | |||
| eb94c16465 | |||
| e8437a9c54 | |||
| 840039ef1e | |||
| 84a749605e | |||
| c1283d2ab0 | |||
| 07b5c4918b | |||
| 703f0a6b70 | |||
| 5e98dee46f | |||
| b2eddf55d2 | |||
| db7be640d4 | |||
| 04fa597844 | |||
| 694dbeb96c | |||
| 7afd358c3c | |||
| b0a5a570a9 | |||
| c45b84c850 | |||
| d575a43aee | |||
| 0b151ea559 | |||
| 926970b256 | |||
| d58af0bfa5 | |||
| 429da7c6f3 | |||
| 60a7433573 | |||
| 08a5d444d4 | |||
| 1982069b69 | |||
| 7c06c65df3 | |||
| 6263766202 | |||
| 66fbbeff54 | |||
| 3c5ad1328f | |||
| 3345d7e088 | |||
| 2bb25cb874 | |||
| 444a728e7a | |||
| 70bea0c865 | |||
| 6eea1e9035 | |||
| 4deb1836ad | |||
| 4c8007dcfe | |||
| 000116bb3d | |||
| a2b6f21c7a | |||
| a374ee39d3 | |||
| 0f5c82dffd | |||
| 268fed761e | |||
| fe01d252db | |||
| dc36d74337 | |||
| 7a0da7bd8e | |||
| 530d5a4e63 | |||
| 2d847d1704 | |||
| 631d8fcfb1 | |||
| a8888efb88 | |||
| a51ecea9b1 | |||
| 4d49517111 | |||
| d5d1b3567b | |||
| 7827ce3448 | |||
| a05059db01 | |||
| b78490ca56 | |||
| b46ccc9b78 | |||
| 4f1e21dfb0 |
@@ -0,0 +1,57 @@
|
||||
name: ci-bcf-pypi
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
# │ │ ┌───────────── day of the month (1 - 31)
|
||||
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||
# * * * * *
|
||||
- cron: "0 0 18 * *"
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/ci-bcf-pypi.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
major: 0
|
||||
minor: 0
|
||||
name: ifcopenshell
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2 # https://github.com/actions/checkout
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
python-version: '3.10' # Version range or exact version of a Python version to use, using SemVer's version range syntax
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%y%m%d')"
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/bcf &&
|
||||
make dist
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: ortega2247/pypi-upload-action@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/bcf/dist
|
||||
@@ -1,37 +0,0 @@
|
||||
name: ci-bcf
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell' &&
|
||||
contains(github.event.head_commit.message, '[bcf release]')
|
||||
steps:
|
||||
- run: echo ok go
|
||||
upload:
|
||||
needs: activate
|
||||
name: Upload BCF package to Pypi
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Build package
|
||||
run: |
|
||||
cd src/bcf
|
||||
pip install build
|
||||
python -m build
|
||||
- name: Test
|
||||
run: |
|
||||
cd src/bcf
|
||||
make test
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_TOKEN }}
|
||||
packages_dir: src/bcf/dist
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py39, py310]
|
||||
pyver: [py39, py310, py311]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
python-version: '3.10'
|
||||
python-version: '3.11'
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current date
|
||||
id: date
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
name: ci-ifcopenshell-conda-daily
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/ci-ifcopenshell-conda-daily.yml
|
||||
- conda/**
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
@@ -15,17 +12,8 @@ on:
|
||||
- cron: "49 23 * * *" # 11min before utc midnight every day
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
test:
|
||||
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}
|
||||
needs: activate
|
||||
name: ${{ matrix.platform.distver }}-${{ matrix.pyver.name }}-${{ matrix.variant }}
|
||||
runs-on: ${{ matrix.platform.distver }}
|
||||
defaults:
|
||||
run:
|
||||
@@ -34,18 +22,23 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [
|
||||
{ name: py310, distver: '3.10'},
|
||||
{ name: py311, distver: '3.11'}
|
||||
{ name: py311, distver: '3.11'},
|
||||
{ name: py312, distver: '3.12'}
|
||||
]
|
||||
platform: [
|
||||
{ name: Windows, distver: windows-2022, upload: 'true' },
|
||||
{ name: Linux, distver: ubuntu-22.04, upload: 'true' },
|
||||
{ name: macOS, distver: macos-12, upload: 'true' }
|
||||
]
|
||||
variant: [
|
||||
'novtk',
|
||||
# 'all'
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Download MacOSX SDK
|
||||
if: ${{ matrix.platform.name == 'macOS' }}
|
||||
run: |
|
||||
@@ -53,21 +46,22 @@ jobs:
|
||||
tar xf MacOSX10.13.sdk.tar.xz && \
|
||||
sudo mv -v MacOSX10.13.sdk /opt/ && \
|
||||
ls /opt/
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- uses: mamba-org/provision-with-micromamba@main # https://github.com/mamba-org/provision-with-micromamba
|
||||
|
||||
- uses: mamba-org/setup-micromamba@v1 # https://github.com/mamba-org/setup-micromamba
|
||||
with:
|
||||
channels: conda-forge
|
||||
channel-priority: strict
|
||||
environment-file: conda/environment.yml
|
||||
extra-specs: |
|
||||
python=${{ matrix.pyver.distver }}
|
||||
environment-name: build-env
|
||||
cache-environment: true
|
||||
condarc: |
|
||||
channels:
|
||||
- conda-forge
|
||||
channel_priority: strict
|
||||
create-args: >-
|
||||
python=3.11
|
||||
anaconda-client
|
||||
boa
|
||||
|
||||
- name: build, test and upload ifcopenshell
|
||||
if: ${{ matrix.platform.upload == 'true' }}
|
||||
run: |
|
||||
conda-build . --python ${{ matrix.pyver.distver }} -c conda-forge --token ${{ secrets.ANACONDA_TOKEN }} --user ifcopenshell
|
||||
working-directory: ./conda
|
||||
- name: build & test ifcopenshell
|
||||
if: ${{ matrix.platform.upload == 'false' }}
|
||||
run: |
|
||||
conda-build . --python ${{ matrix.pyver.distver }} -c conda-forge
|
||||
conda mambabuild . --python ${{ matrix.pyver.distver }} -c conda-forge --variants "{variant: ${{ matrix.variant }}}" --token ${{ secrets.ANACONDA_TOKEN }} --user ifcopenshell
|
||||
working-directory: ./conda
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
name: ci_py_only
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'test/**'
|
||||
- 'conda/**'
|
||||
- 'cmake/**'
|
||||
- '.github/workflows/ci-py-only.yml'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell' &&
|
||||
!contains(github.event.head_commit.message, 'skip ci')
|
||||
steps:
|
||||
- run: echo ok go
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: activate
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install C++ dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt-get install --no-install-recommends \
|
||||
git cmake gcc g++ \
|
||||
libboost-date-time-dev \
|
||||
libboost-filesystem-dev \
|
||||
libboost-iostreams-dev \
|
||||
libboost-program-options-dev \
|
||||
libboost-regex-dev \
|
||||
libboost-system-dev \
|
||||
libboost-thread-dev \
|
||||
python3-all-dev python3-pip \
|
||||
swig libpcre3-dev libxml2-dev \
|
||||
libtbb-dev nlohmann-json3-dev \
|
||||
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
|
||||
libhdf5-dev libcgal-dev
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${GITHUB_WORKFLOW}
|
||||
|
||||
- name: Build ifcopenshell
|
||||
run: |
|
||||
sudo /usr/sbin/update-ccache-symlinks
|
||||
mkdir build && cd build
|
||||
PATH="/usr/lib/ccache:$PATH" cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH=/usr \
|
||||
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
|
||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||
-DCOLLADA_SUPPORT=Off \
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
|
||||
-DBUILD_CONVERT=Off \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include \
|
||||
-DCGAL_INCLUDE_DIR=/usr/include \
|
||||
-DGMP_INCLUDE_DIR=/usr/include \
|
||||
-DMPFR_INCLUDE_DIR=/usr/include \
|
||||
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DHDF5_INCLUDE_DIR=/usr/include/hdf5/serial \
|
||||
../cmake
|
||||
sudo make -j $(nproc)
|
||||
sudo make install
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
sudo /usr/bin/python -m pip install -U pip
|
||||
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
|
||||
sudo /usr/bin/python -m pip install src/bcf --no-deps
|
||||
sudo /usr/bin/python -m pip install pytest
|
||||
sudo /usr/bin/python -m pip install isodate
|
||||
sudo /usr/bin/python -m pip install lark
|
||||
sudo /usr/bin/python -m pip install networkx
|
||||
sudo /usr/bin/python -m pip install tabulate
|
||||
sudo /usr/bin/python -m pip install python-dateutil
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd test
|
||||
sudo /usr/bin/python tests.py
|
||||
cd ../src/ifcopenshell-python
|
||||
mv ifcopenshell ifcopenshell-local # Force testing on installed module
|
||||
make test
|
||||
+24
-21
@@ -31,12 +31,25 @@ jobs:
|
||||
- run: echo ok go
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: activate
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil
|
||||
pip install src/bcf --no-deps
|
||||
pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
|
||||
- name: Install C++ dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
@@ -49,7 +62,6 @@ jobs:
|
||||
libboost-regex-dev \
|
||||
libboost-system-dev \
|
||||
libboost-thread-dev \
|
||||
python3-all-dev python3-pip \
|
||||
swig libpcre3-dev libxml2-dev \
|
||||
libtbb-dev nlohmann-json3-dev \
|
||||
libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev libocct-data-exchange-dev \
|
||||
@@ -62,6 +74,9 @@ jobs:
|
||||
|
||||
- name: Build ifcopenshell
|
||||
run: |
|
||||
echo $Python3_ROOT_DIR
|
||||
echo ${{ env.pythonLocation }}
|
||||
|
||||
mkdir build && cd build
|
||||
cmake \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
@@ -71,9 +86,9 @@ jobs:
|
||||
-DCMAKE_SYSTEM_PREFIX_PATH=/usr \
|
||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=${{ env.pythonLocation }}/bin/python \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \
|
||||
-DCOLLADA_SUPPORT=Off \
|
||||
"-DSCHEMA_VERSIONS=2x3;4;4x3_add1" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
@@ -102,23 +117,11 @@ jobs:
|
||||
grep 0$ statuses
|
||||
grep 0$ statuses | wc -l
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
sudo /usr/bin/python -m pip install -U pip
|
||||
sudo /usr/bin/python -m pip install xmlschema xsdata numpy lxml
|
||||
sudo /usr/bin/python -m pip install src/bcf --no-deps
|
||||
sudo /usr/bin/python -m pip install pytest
|
||||
sudo /usr/bin/python -m pip install isodate
|
||||
sudo /usr/bin/python -m pip install lark
|
||||
sudo /usr/bin/python -m pip install networkx
|
||||
sudo /usr/bin/python -m pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
sudo /usr/bin/python -m pip install tabulate
|
||||
sudo /usr/bin/python -m pip install python-dateutil
|
||||
|
||||
- name: Test
|
||||
- name: Test ifcopenshell-python
|
||||
run: |
|
||||
cd test
|
||||
sudo /usr/bin/python tests.py
|
||||
python tests.py
|
||||
cd ../src/ifcopenshell-python
|
||||
mv ifcopenshell ifcopenshell-local # Force testing on installed module
|
||||
make test
|
||||
make test-safe
|
||||
|
||||
+6
-1
@@ -96,5 +96,10 @@ src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
|
||||
# apple
|
||||
.DS_Store
|
||||
|
||||
# clangd config
|
||||
.clangd
|
||||
# clangd cache
|
||||
.cache
|
||||
|
||||
# Brickschema
|
||||
src/blenderbim/blenderbim/bim/schema/Brick.ttl
|
||||
src/blenderbim/blenderbim/bim/schema/Brick.ttl
|
||||
|
||||
@@ -7,13 +7,13 @@ IfcOpenShell
|
||||
</p>
|
||||
|
||||
IfcOpenShell is an open source ([LGPL]) software library for working with Industry Foundation Classes ([IFC]). Complete
|
||||
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and IFC4x3. Extensive geometric support
|
||||
parsing support is provided for [IFC2x3 TC1], [IFC4 Add2 TC1], IFC4x1, IFC4x2, and [IFC4x3 Add2]. Extensive geometric support
|
||||
is implemented for the IFC releases [IFC2x3 TC1] and [IFC4 Add2 TC1]. Extending with support for arbitrary IFC schemas
|
||||
is possible at compile-time when using C++ and at run-time when using Python.
|
||||
|
||||
In addition to a C++ and Python API, IfcOpenShell comes with an ecosystem of tools, notably including IfcConvert (an application to convert IFC models to
|
||||
other formats), the BlenderBIM Add-on (an add-on to Blender providing a graphical IFC authoring platform), and many
|
||||
other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF and IDS.
|
||||
In addition to a C++ and Python API, IfcOpenShell comes with an ecosystem of tools, notably including IfcConvert (an application
|
||||
to convert IFC models to other formats), the BlenderBIM Add-on (an add-on to Blender providing a graphical IFC authoring platform),
|
||||
and many other libraries, CLI apps, and more. Support is also provided for auxiliary standards such as BCF and IDS.
|
||||
|
||||
For more information, see:
|
||||
|
||||
@@ -32,8 +32,8 @@ For more information, see:
|
||||
| Anaconda Daily Build | [](https://anaconda.org/ifcopenshell/ifcopenshell) |
|
||||
| Anaconda v0.7.0 Stable | [](https://anaconda.org/conda-forge/ifcopenshell) |
|
||||
| PyPi Daily Build | [](https://pypi.org/project/ifcopenshell/) |
|
||||
| ArchLinux AUR Package Stable | [](https://aur.archlinux.org/packages/ifcopenshell) |
|
||||
| ArchLinux AUR Package git | [](https://aur.archlinux.org/packages/ifcopenshell-git) |
|
||||
| ArchLinux AUR Package Stable | [](https://aur.archlinux.org/packages/ifcopenshell) |
|
||||
| ArchLinux AUR Package git | [](https://aur.archlinux.org/packages/ifcopenshell-git) |
|
||||
| BlenderBIM Add-on Chocolatey (under moderation) | [](https://community.chocolatey.org/packages/blenderbim-nightly/) |
|
||||
| Sponsor development on OpenCollective | [](https://opencollective.com/opensourcebim/) |
|
||||
| Docker hub | [](https://hub.docker.com/r/aecgeeks/ifcopenshell) |
|
||||
@@ -44,7 +44,7 @@ Contents
|
||||
Those marked with an asterisk are part of IfcOpenShell.
|
||||
|
||||
| Name | Description | License |
|
||||
| ------------------------- | ----------------------------------------------------------------------| ------------------- |
|
||||
| ------------------------- | --------------------------------------------------------------------- | ------------------- |
|
||||
| bcf | Library to read and write BCF-XML and query OpenCDE BCF-API modules | LGPL-3.0-or-later |
|
||||
| blenderbim | Add-on to Blender providing a graphical native IFC authoring platform | GPL-3.0-or-later |
|
||||
| bsdd | Library to query the bSDD API | LGPL-3.0-or-later |
|
||||
@@ -55,7 +55,6 @@ Those marked with an asterisk are part of IfcOpenShell.
|
||||
| ifcblender | Historic Blender IFC import add-on | LGPL-3.0-or-later\* |
|
||||
| ifccityjson | Convert CityJSON to IFC | LGPL-3.0-or-later |
|
||||
| ifcclash | Clash detection library and CLI app | LGPL-3.0-or-later |
|
||||
| ifccobie | Extract IFC data for COBie handover requirements | LGPL-3.0-or-later |
|
||||
| ifcconvert | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* |
|
||||
| ifccsv | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later |
|
||||
| ifcdiff | Compare changes between IFC models | LGPL-3.0-or-later |
|
||||
@@ -78,6 +77,7 @@ Those marked with an asterisk are part of IfcOpenShell.
|
||||
[IFC]: https://technical.buildingsmart.org/standards/ifc/ "IFC"
|
||||
[IFC2x3 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/ "IFC2x3 TC1"
|
||||
[IFC4 Add2 TC1]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/ "IFC4 Add2 TC1"
|
||||
[IFC4x3 Add2]: https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/ "IFC4x3 Add2"
|
||||
[Visual Studio]: https://www.visualstudio.com/ "Visual Studio"
|
||||
[Visual C++ Build Tools]: http://landinghub.visualstudio.com/visual-cpp-build-tools "Visual C++ Build Tools"
|
||||
[MSYS2]: https://msys2.github.io/ "MSYS2"
|
||||
|
||||
@@ -29,7 +29,7 @@ URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
|
||||
URL_BLENDER_CMAKE = "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake"
|
||||
RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+<span>Blender (\d+\.\d+)\..+</span>"
|
||||
RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+<span>Blender (\d+\.\d+\.\d+)</span>"
|
||||
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"SET\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
|
||||
RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"(?:set|SET)\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)"
|
||||
|
||||
|
||||
if sys.argv[1] == "--do_choco_release?":
|
||||
|
||||
+660
-592
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
@@ -1,8 +0,0 @@
|
||||
compile:
|
||||
conda activate conda-build && conda-build -c conda-forge . --keep-old-work --python 3.10.5
|
||||
|
||||
debug:
|
||||
conda activate conda-build && conda-debug -c conda-forge . --python 3.10.4
|
||||
|
||||
env:
|
||||
conda env update --f environment.yml --prune
|
||||
@@ -4,6 +4,7 @@ set MY_PY_VER=%PY_VER:.=%
|
||||
set LIBXML2="%LIBRARY_PREFIX%/lib/libxml2.lib"
|
||||
|
||||
cmake -G "Ninja" ^
|
||||
-D SCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" ^
|
||||
-D CMAKE_BUILD_TYPE:STRING=Release ^
|
||||
-D CMAKE_INSTALL_PREFIX:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
-D CMAKE_PREFIX_PATH:FILEPATH="%LIBRARY_PREFIX%" ^
|
||||
|
||||
@@ -13,6 +13,7 @@ if [ `uname` == Darwin ]; then
|
||||
fi
|
||||
|
||||
cmake -G Ninja \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x1;4x3;4x3_add1" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$PREFIX \
|
||||
${CMAKE_PLATFORM_FLAGS[@]} \
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
CONDA_BUILD_SYSROOT:
|
||||
- /opt/MacOSX10.13.sdk # [osx]
|
||||
- /opt/MacOSX10.13.sdk # [osx]
|
||||
|
||||
variant:
|
||||
- novtk
|
||||
- all
|
||||
@@ -1,13 +0,0 @@
|
||||
name: conda-build
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- conda-build
|
||||
- conda-verify
|
||||
- anaconda-client
|
||||
- ninja
|
||||
- doxygen
|
||||
- ripgrep
|
||||
- pip
|
||||
- pip:
|
||||
- lief==0.13
|
||||
+14
-9
@@ -1,6 +1,10 @@
|
||||
{% set name = "ifcopenshell" %}
|
||||
{% set version = "0.7.0" %}
|
||||
{% set build = 1 %}
|
||||
|
||||
# Higher number -> Always prioritize "novtk" variant over "all" variant
|
||||
{% set build = build + 200 %} # [variant == "novtk"]
|
||||
{% set build = build + 100 %} # [variant == "all"]
|
||||
|
||||
package:
|
||||
name: {{ name }}
|
||||
@@ -10,8 +14,9 @@ source:
|
||||
path: ..
|
||||
|
||||
build:
|
||||
string: py{{ CONDA_PY }}_{{ variant }}_h{{ PKG_HASH }}_{{ build }}
|
||||
binary_relocation: false [osx]
|
||||
number: 1
|
||||
number: {{ build }}
|
||||
|
||||
requirements:
|
||||
build:
|
||||
@@ -19,12 +24,12 @@ requirements:
|
||||
- {{ compiler('cxx') }}
|
||||
- ninja >=1.10.2
|
||||
- cmake
|
||||
- swig >=4.0.2
|
||||
- swig 4.1.1
|
||||
|
||||
host:
|
||||
- python
|
||||
- boost-cpp
|
||||
- occt ==7.7.0
|
||||
- occt 7.7.2 *{{ variant }}*
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
@@ -36,11 +41,11 @@ requirements:
|
||||
|
||||
run:
|
||||
- python
|
||||
- boost-cpp
|
||||
- occt ==7.7.0
|
||||
- occt 7.7.2 *{{ variant }}*
|
||||
- {{ pin_compatible('cgal-cpp', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('boost-cpp', max_pin='x.x.x') }}
|
||||
- {{ pin_compatible('hdf5', max_pin='x.x.x') }}
|
||||
- libxml2
|
||||
- cgal-cpp
|
||||
- hdf5
|
||||
- mpfr
|
||||
- gmp # [unix]
|
||||
- mpir # [win]
|
||||
@@ -52,12 +57,12 @@ test:
|
||||
- ifcopenshell
|
||||
|
||||
about:
|
||||
home: http://ifcopenshell.org
|
||||
home: https://ifcopenshell.org
|
||||
license: LGPL-3.0-or-later
|
||||
license_file: COPYING
|
||||
summary: 'IfcOpenShell is a library to support the IFC file format'
|
||||
description: |
|
||||
IfcOpenShell is an open source (LGPL) software library for
|
||||
working with the Industry Foundation Classes (IFC) file format.
|
||||
doc_url: http://ifcopenshell.org/
|
||||
doc_url: https://ifcopenshell.org/
|
||||
dev_url: https://github.com/IfcOpenShell/IfcOpenShell
|
||||
|
||||
+4
-3
@@ -76,7 +76,7 @@ PROJECT_NAME = "IfcOpenShell"
|
||||
USE_CURRENT_PYTHON_VERSION = os.getenv("USE_CURRENT_PYTHON_VERSION")
|
||||
ADD_COMMIT_SHA = os.getenv("ADD_COMMIT_SHA")
|
||||
|
||||
PYTHON_VERSIONS = ["3.6.14", "3.7.13", "3.8.13", "3.9.11", "3.10.3", "3.11.0"]
|
||||
PYTHON_VERSIONS = ["3.6.14", "3.7.13", "3.8.13", "3.9.11", "3.10.3", "3.11.7", "3.12.1"]
|
||||
JSON_VERSION = "v3.6.1"
|
||||
OCE_VERSION = "0.18.3"
|
||||
OCCT_VERSION = "7.5.3"
|
||||
@@ -892,8 +892,9 @@ if "IfcOpenShell-Python" in targets:
|
||||
module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
|
||||
|
||||
if platform.system() != "Darwin":
|
||||
# TODO: This symbol name depends on the Python version?
|
||||
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
|
||||
if BUILD_CFG == "Release":
|
||||
# TODO: This symbol name depends on the Python version?
|
||||
run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
|
||||
|
||||
return module_dir
|
||||
|
||||
|
||||
+14
-1
@@ -1,3 +1,9 @@
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
SED:=sed -i
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED:=sed -i '' -e
|
||||
endif
|
||||
|
||||
.PHONY: ci
|
||||
ci:
|
||||
tox
|
||||
@@ -12,6 +18,13 @@ models:
|
||||
cd src && xsdata generate -p bcf.v2.model --unnest-classes --kw-only --slots -ds Google bcf/v2/xsd
|
||||
cd src && xsdata generate -p bcf.v3.model --unnest-classes --kw-only --slots -ds Google bcf/v3/xsd
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
rm -rf dist
|
||||
$(SED) "s/999999/$(VERSION)/" pyproject.toml
|
||||
python -m build
|
||||
$(SED) "s/$(VERSION)/999999/" pyproject.toml
|
||||
|
||||
# .PHONY
|
||||
# api:
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
+3
-82
@@ -1,84 +1,5 @@
|
||||
# bcf
|
||||
|
||||
A simple Python implementation of BCF.
|
||||
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
|
||||
is available via `bcfapi.py`.
|
||||
|
||||
It tries to support BCF-XML version 2.1 and 3.0, and BCF-API 3.0.
|
||||
|
||||
## bcfxml
|
||||
|
||||
The `bcfxml.load` function lets you read a BCF-XML file.
|
||||
It takes care of using the right version based on the "bcf.version" file contained in the BCF package.
|
||||
|
||||
The BCF files are extracted and parsed on-demand, and edits are stored in memory until you call the `save` method.
|
||||
|
||||
```python
|
||||
from bcf.bcfxml import load
|
||||
|
||||
# Load a project
|
||||
with load("/path/to/file.bcf") as bcfxml:
|
||||
project = bcfxml.project
|
||||
print(project.name)
|
||||
|
||||
# To edit a project, just modify the object directly
|
||||
bcfxml.project.name = "New name"
|
||||
|
||||
# Get a dictionary of topics
|
||||
topics = bcfxml.topics
|
||||
|
||||
for topic_guid, topic_handler in bcfxml.topics.items():
|
||||
topic = topic_handler.topic
|
||||
print("Topic guid is", topic.guid)
|
||||
print("Topic title is", topic.title)
|
||||
|
||||
# Fetch extra data about a topic
|
||||
header = topic_handler.header
|
||||
comments = topic_handler.comments
|
||||
viewpoints = topic_handler.viewpoints
|
||||
|
||||
for comment in comments:
|
||||
print(comment.guid)
|
||||
print(comment.comment)
|
||||
print(comment.author)
|
||||
|
||||
# Get a particular topic
|
||||
topic = bcfxml.get_topic(guid)
|
||||
|
||||
# Modify a topic
|
||||
topic.title = "New title"
|
||||
|
||||
bcfxml.save()
|
||||
```
|
||||
|
||||
## bcfapi
|
||||
|
||||
The `bcfapi` module lets you interact with the BCF-API standard.
|
||||
|
||||
```python
|
||||
from bcf.v3.bcfapi import FoundationClient, BcfClient
|
||||
|
||||
foundation_client = FoundationClient("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET", "OPENCDE_BASEURL")
|
||||
auth_methods = foundation_client.get_auth_methods()
|
||||
|
||||
# Our library currently only implements the authorization_code flow
|
||||
if "authorization_code" in auth_methods:
|
||||
foundation_client.login()
|
||||
|
||||
bcf_client = BcfClient(foundation_client)
|
||||
|
||||
versions = foundation_client.get_versions()
|
||||
for version in versions:
|
||||
if "3.0" in versions:
|
||||
if version["api_id"] == "bcf" and version["version_id"] == "3.0":
|
||||
bcf_client.set_version(version)
|
||||
|
||||
data = bcf_client.get_projects()
|
||||
print(data)
|
||||
project_id = data[0]["project_id"]
|
||||
print(project_id)
|
||||
data = bcf_client.get_project(project_id)
|
||||
print(data)
|
||||
data = bcf_client.get_extensions(project_id)
|
||||
print(data)
|
||||
```
|
||||
A simple Python implementation of the BCF standard. Manipulation of BCF-XML is
|
||||
available via `bcfxml.py` and manipulation of BCF-API is available via
|
||||
`bcfapi.py`.
|
||||
|
||||
@@ -17,7 +17,7 @@ dependencies = [
|
||||
"numpy",
|
||||
"ifcopenshell",
|
||||
]
|
||||
version = "0.0.1"
|
||||
version = "0.0.999999"
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: OS Independent",
|
||||
@@ -76,7 +76,7 @@ deps =
|
||||
black
|
||||
isort
|
||||
pylint
|
||||
commands =
|
||||
commands =
|
||||
black {posargs:.}
|
||||
isort {posargs:.}
|
||||
pylint {posargs:.} --output-format=colorized
|
||||
@@ -138,4 +138,4 @@ max-attributes = 10
|
||||
|
||||
[tool.pylint.format]
|
||||
expected-line-ending-format = "LF"
|
||||
max-line-length = 120
|
||||
max-line-length = 120
|
||||
+113
-83
@@ -38,114 +38,161 @@ endif
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
|
||||
PYVERSION:=py310
|
||||
PYPI_IMP:=cp
|
||||
|
||||
ifeq ($(PYVERSION), py39)
|
||||
PYLIBDIR:=python3.9
|
||||
PYNUMBER:=39
|
||||
PYPI_VERSION:=3.9
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
PYLIBDIR:=python3.10
|
||||
PYNUMBER:=310
|
||||
PYPI_VERSION:=3.10
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
PYLIBDIR:=python3.11
|
||||
PYNUMBER:=311
|
||||
PYPI_VERSION:=3.11
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), linux)
|
||||
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
|
||||
LXML_ARCH:=manylinux_2_28_x86_64
|
||||
SHAPELY_ARCH:=manylinux_2_17_x86_64.manylinux2014_x86_64
|
||||
PILLOW_ARCH:=manylinux_2_28_x86_64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py39h40a70d0_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py39hdfdd6bb_0.conda
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/linux-64/hpp-fcl-2.4.0-py39h359ec1e_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/linux-64/eigenpy-3.2.0-py39hb1babde_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py39h7c9e3ff_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/2d/f2/8ec281d357e8bb7d08dc8d727f0e4c8ef3dae7d3fa75c69c8e452bb82d50/shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/01/61/3ff85fb4bb596ce3d223c8fcf93c8df5c12bc8899dfb4fb3cb1c5b20dd5f/Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/linux-64/hpp-fcl-2.3.4-py310h995690b_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/linux-64/eigenpy-3.1.0-py310hf02b7e0_0.conda
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/linux-64/hpp-fcl-2.4.0-py310hd002f03_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/linux-64/eigenpy-3.2.0-py310hcfe0b05_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py310hc4a4660_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/25/1e/19b46d8e8881fe0df2e20945d51919eeb1817836d62a90efa8506530e45c/lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/a8/a5/403728b5614b28083f6424dfdefec5fcf58068495fb03bb08532671c642f/shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/f6/51/320986ebd6d46a0e95c2240468ced73153b691ce07617078bcdf30c609ec/Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/linux-64/hpp-fcl-2.4.0-py311h04f086a_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/linux-64/eigenpy-3.2.0-py311h995a085_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/linux-64/boost-1.78.0-py311h59ea3da_3.tar.bz2
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/linux-64/qhull-2020.2-h4bd325d_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/linux-64/octomap-1.9.8-h924138e_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/linux-64/boost-cpp-1.78.0-h6582d0a_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
PYPI_PLATFORM:=--platform macosx_10_9_x86_64
|
||||
SHAPELY_ARCH:=macosx_10_9_x86_64
|
||||
PILLOW_ARCH:=macosx_10_10_x86_64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py39hd85b194_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py39hc4d6e28_0.conda
|
||||
LXML_ARCH:=macosx_11_0_x86_64
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-64/hpp-fcl-2.4.0-py39h26350b9_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-64/eigenpy-3.2.0-py39h3323527_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py39h953a6b8_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/36/a4/7e542a209f862f967d7cb8e939eff155f4294a27d17e16441fb8bdd51a2c/shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/88/7a/ddfe28b485b623361457d4783007c1f9ba83a87f93e7fec32f64793efb6c/Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-64/hpp-fcl-2.3.4-py310h1db6f5f_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-64/eigenpy-3.1.0-py310h43da829_0.conda
|
||||
LXML_ARCH:=macosx_11_0_x86_64
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-64/hpp-fcl-2.4.0-py310ha216469_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-64/eigenpy-3.2.0-py310h33a8c9b_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py310h3e792ce_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/a1/44/17b7dac7a18807d30e2fe10c3328c152808f5464565e230bfd0e77f178c6/lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/1f/2a/dc3353c2431cf53e8d04bb8fba27e584410ca3435c9c85f76d71bf0c0e80/shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/d8/60/b13c00d403f34110e96c1b5c0afa73ce461efe3fe960c3a7e3e7fe190d82/Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
LXML_ARCH:=macosx_11_0_universal2
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-64/hpp-fcl-2.4.0-py311hebdd1a2_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-64/eigenpy-3.2.0-py311h62e90b6_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-64/boost-1.78.0-py311h349b758_4.tar.bz2
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-64/qhull-2020.2-h940c156_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/osx-64/octomap-1.9.8-hb8565cd_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/osx-64/boost-cpp-1.78.0-hf5ba120_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-64/zlib-1.2.11-h7795811_1010.tar.bz2
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
PYPI_PLATFORM:=--platform macosx_11_0_arm64
|
||||
LXML_ARCH:=macosx_11_0_universal2
|
||||
SHAPELY_ARCH:=macosx_11_0_arm64
|
||||
PILLOW_ARCH:=macosx_11_0_arm64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py39hc34188a_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py39h13cfc01_0.conda
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-arm64/hpp-fcl-2.4.0-py39h82b2ff2_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-arm64/eigenpy-3.2.0-py39h3a9e884_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py39h99de9ae_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h9eb174b_0.tar.bz2
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/ea/aa/45fbd031edf3149cb767d8b9f9db45d5faf0324d743c6b8fb0298cc022d0/shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/aa/bc/21097cd891dd2fa02f2b3d767e02e883e026482e59d29975d1bc30024aa3/Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/osx-arm64/hpp-fcl-2.3.4-py310h46fc4cd_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/osx-arm64/eigenpy-3.1.0-py310ha2643af_0.conda
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-arm64/hpp-fcl-2.4.0-py310hdb6c3eb_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-arm64/eigenpy-3.2.0-py310h96e9823_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py310h629746b_4.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/ec/41/d59208743e737184e1b403e95a937aebb022b8459e99efbcd5208fc8be46/shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/0c/5f/117b653cad585f3aedfe0de996c292e67d4b020ed77f652e5a6c8c24f908/Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/osx-arm64/hpp-fcl-2.4.0-py311hc0c29b9_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/osx-arm64/eigenpy-3.2.0-py311h24fa84d_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/osx-arm64/boost-1.78.0-py311h5c64717_4.tar.bz2
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-arm64/assimp-5.0.1-h0f81e16_7.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/osx-arm64/qhull-2020.2-hc021e02_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/osx-arm64/octomap-1.9.8-hffc8910_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/osx-arm64/boost-cpp-1.78.0-h9ed8d21_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), win)
|
||||
PYPI_PLATFORM:=--platform win_amd64
|
||||
LXML_ARCH:=win_amd64
|
||||
SHAPELY_ARCH:=win_amd64
|
||||
PILLOW_ARCH:=win_amd64
|
||||
ifeq ($(PYVERSION), py39)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py39h39b25cb_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py39hb6c915b_0.conda
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/win-64/hpp-fcl-2.4.0-py39hf3ebc58_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/win-64/eigenpy-3.2.0-py39ha1f6a47_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py39hea4d8d1_4.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/a7/ae/eab645c4075093584b7a65ab71cb8ff4563a015bd935c9b55dba14b2c1eb/shapely-2.0.1-cp39-cp39-win_amd64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/19/3f/b4d4bcf05dbcbe07f2e9613a8f4180c297395e73a91d8ad22c32c6624f8c/Pillow-9.2.0-cp39-cp39-win_amd64.whl
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.3.4/download/win-64/hpp-fcl-2.3.4-py310heb59267_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.1.0/download/win-64/eigenpy-3.1.0-py310hd146abe_0.conda
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/win-64/hpp-fcl-2.4.0-py310h1bf3f0a_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/win-64/eigenpy-3.2.0-py310h7fb871d_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py310h220cb41_4.tar.bz2
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/f6/71/65c80a4caa1617a4c6e8fe1500cebb179db96232e2f623bfe6a1f4294e39/lxml-4.8.0-cp310-cp310-win_amd64.whl
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/81/8a/7ac076a86b2632f1872284c5e60ed5f2fc26094875a85b35d9fa17b52504/shapely-2.0.1-cp310-cp310-win_amd64.whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/02/55/67a3c17b9e7d972ed8c246f104da99ca4f3ea42fba566697e479011b84b6/Pillow-9.2.0-cp310-cp310-win_amd64.whl
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
|
||||
ifeq ($(PYVERSION), py311)
|
||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/2.4.0/download/win-64/hpp-fcl-2.4.0-py311hd263b2c_0.conda
|
||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/3.2.0/download/win-64/eigenpy-3.2.0-py311hd354db1_0.conda
|
||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.78.0/download/win-64/boost-1.78.0-py311h12feb9d_3.tar.bz2
|
||||
endif
|
||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.2.5/download/win-64/assimp-5.2.5-h4dcb625_0.tar.bz2
|
||||
QHULL_URL:=https://anaconda.org/conda-forge/qhull/2020.2/download/win-64/qhull-2020.2-h70d2c02_2.tar.bz2
|
||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.8/download/win-64/octomap-1.9.8-h91493d7_0.tar.bz2
|
||||
BOOSTCPP_URL:=https://anaconda.org/conda-forge/boost-cpp/1.78.0/download/win-64/boost-cpp-1.78.0-h9f4b32c_3.conda
|
||||
ZLIB_URL:=https://anaconda.org/conda-forge/zlib/1.2.11/download/win-64/zlib-1.2.11-h62dcd97_1010.tar.bz2
|
||||
endif
|
||||
|
||||
LXML_NAME:=lxml
|
||||
LXML_VER:=4.9.4
|
||||
SHAPELY_NAME:=shapely
|
||||
SHAPELY_VER:=2.0.2
|
||||
PILLOW_NAME:=pillow
|
||||
PILLOW_PKG_NAME:=Pillow
|
||||
PILLOW_VER:=9.5.0
|
||||
SHAPELY_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/s/$(SHAPELY_NAME)/$(SHAPELY_NAME)-$(SHAPELY_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(SHAPELY_ARCH).whl
|
||||
PILLOW_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/p/$(PILLOW_NAME)/$(PILLOW_PKG_NAME)-$(PILLOW_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(PILLOW_ARCH).whl
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
ifeq ($(PYVERSION), py39)
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py39h0520ce3_1.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py310)
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py310h02f21da_0.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py311)
|
||||
LXML_URL:=https://anaconda.org/conda-forge/lxml/4.9.1/download/osx-arm64/lxml-4.9.1-py311h246f609_1.tar.bz2
|
||||
endif
|
||||
else
|
||||
LXML_URL:=https://files.pythonhosted.org/packages/cp$(PYNUMBER)/l/$(LXML_NAME)/$(LXML_NAME)-$(LXML_VER)-cp$(PYNUMBER)-cp$(PYNUMBER)-$(LXML_ARCH).whl
|
||||
endif
|
||||
$(info $$LXML_URL is [${LXML_URL}])
|
||||
$(info $$SHAPELY_URL is [${SHAPELY_URL}])
|
||||
$(info $$PILLOW_URL is [${PILLOW_URL}])
|
||||
|
||||
.PHONY: bump
|
||||
bump:
|
||||
cd . && $(SED) "s/$(OLD)/$(NEW)/" Makefile
|
||||
@@ -164,7 +211,7 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-fc50bdd-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v0.7.0-6c9e130-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcopenshell-python*
|
||||
cp -r dist/working/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
|
||||
@@ -194,8 +241,10 @@ endif
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcbimtester/bimtester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCTester functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifctester/ifctester dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCOBie functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCFM functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcfm/ifcfm dist/blenderbim/libs/site/packages/
|
||||
# Provides bSDD functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/bsdd/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCDiff functionality
|
||||
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
|
||||
# Provides IFCCSV functionality
|
||||
@@ -376,6 +425,10 @@ endif
|
||||
mkdir dist/working
|
||||
cd dist/working && $(PYTHON) -m venv env
|
||||
cd dist/working && mkdir site-packages
|
||||
# Required by IfcCSV
|
||||
cd dist/working && . env/bin/activate && $(PIP) install pandas $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install openpyxl --target=./site-packages
|
||||
cd dist/working && . env/bin/activate && $(PIP) install odfpy --target=./site-packages
|
||||
# Required by IFCCityJSON
|
||||
cd dist/working && . env/bin/activate && $(PIP) install cjio --target=./site-packages
|
||||
# Provides express rule validation for ifcopenshell.validate
|
||||
@@ -426,8 +479,7 @@ ifeq ($(PLATFORM), win)
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by hpp-fcl except on Windows
|
||||
ifneq ($(PLATFORM), win)
|
||||
# Required by hpp-fcl
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(QHULL_URL)
|
||||
cd dist/working && tar -xf qhull*
|
||||
@@ -440,8 +492,10 @@ endif
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
ifeq ($(PLATFORM), win)
|
||||
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by hpp-fcl
|
||||
mkdir dist/working
|
||||
@@ -476,7 +530,7 @@ ifeq ($(PLATFORM), macosm1)
|
||||
cp -r dist/working/lib/*.dylib dist/blenderbim/libs/
|
||||
endif
|
||||
ifeq ($(PLATFORM), win)
|
||||
# Uh, do nothing, apparently? No DLLs are shipped.
|
||||
cp -r dist/working/Library/bin/*.dll dist/blenderbim/libs/site/packages/hppfcl/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
@@ -582,28 +636,6 @@ endif
|
||||
cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCOBie for XLSX support
|
||||
# TODO: see if we can replace this with openpyxl which does both read/write
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/0c/bc/82d6783f83f65f56d8b77d052773c4a2f952fa86385f0cd54e1e006658d7/XlsxWriter-1.2.9.tar.gz
|
||||
cd dist/working && tar -xzvf XlsxWriter*
|
||||
cd dist/working/XlsxWriter-1.2.9/ && cp -r xlsxwriter ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by IFCCOBie for ODS support
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz
|
||||
cd dist/working && tar -xzvf odfpy*
|
||||
cd dist/working/odfpy-1.4.1/ && cp -r odf ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by odfpy
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz
|
||||
cd dist/working && tar -xzvf defusedxml*
|
||||
cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by augin
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/76/b4/b7baffbda025efd5dc8fcd8d2e953e3aa939c236a484084fa8f4c3588ee9/boto3-1.17.17.tar.gz
|
||||
@@ -639,13 +671,6 @@ endif
|
||||
cp -r dist/working/IFC2JSON_python-master/file_converters/ifcjson dist/blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides OpenLCA functionality
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://files.pythonhosted.org/packages/85/33/f91b96e9e8608ff65a003b692e8a9cdd60f2178f60617e5b1d21334c009c/olca-ipc-0.0.10.tar.gz
|
||||
cd dist/working && tar -xzvf olca-ipc*
|
||||
cd dist/working/olca-ipc-0.0.10/ && cp -r olca ../../blenderbim/libs/site/packages/
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides Brickschema functionality
|
||||
mkdir dist/working
|
||||
# For now lets bundle the latest nightly schema
|
||||
@@ -671,6 +696,11 @@ endif
|
||||
# Required for Desktop icon and file association
|
||||
cp -r blenderbim/libs/desktop dist/blenderbim/libs/
|
||||
|
||||
# generate translations module for BBIM build
|
||||
git clone https://github.com/IfcOpenShell/blenderbim-translations.git dist/working
|
||||
python scripts/bbim_translations.py -i "dist/working" -o "dist/blenderbim"
|
||||
rm -rf dist/working
|
||||
|
||||
# Remove dependencies also bundled with Blender
|
||||
rm -rf dist/blenderbim/libs/site/packages/numpy
|
||||
rm -rf dist/blenderbim/libs/site/packages/numpy.libs
|
||||
|
||||
@@ -24,12 +24,17 @@ import blenderbim
|
||||
import importlib
|
||||
from . import handler, ui, prop, operator, helper
|
||||
|
||||
try:
|
||||
from blenderbim.translations import translations_dict
|
||||
except ImportError:
|
||||
translations_dict = {}
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
modules = {
|
||||
"project": None,
|
||||
"search": None,
|
||||
"bcf": None,
|
||||
"bsdd": None,
|
||||
"root": None,
|
||||
"unit": None,
|
||||
"model": None,
|
||||
@@ -43,8 +48,9 @@ modules = {
|
||||
"spatial": None,
|
||||
"void": None,
|
||||
"aggregate": None,
|
||||
"nest": None,
|
||||
"geometry": None,
|
||||
"cobie": None,
|
||||
"fm": None,
|
||||
"resource": None,
|
||||
"cost": None,
|
||||
"sequence": None,
|
||||
@@ -66,7 +72,6 @@ modules = {
|
||||
"document": None,
|
||||
"pset_template": None,
|
||||
"clash": None,
|
||||
"lca": None,
|
||||
"csv": None,
|
||||
"tester": None,
|
||||
"bimtester": None,
|
||||
@@ -77,6 +82,7 @@ modules = {
|
||||
"augin": None,
|
||||
"debug": None,
|
||||
"ifcgit": None,
|
||||
"covering": None,
|
||||
# Uncomment this line to enable loading of the demo module. Happy hacking!
|
||||
# The name "demo" must correlate to a folder name in `bim/module/`.
|
||||
# "demo": None,
|
||||
@@ -112,6 +118,7 @@ classes = [
|
||||
prop.ObjProperty,
|
||||
prop.Attribute,
|
||||
prop.BIMAreaProperties,
|
||||
prop.BIMTabProperties,
|
||||
prop.BIMProperties,
|
||||
prop.IfcParameter,
|
||||
prop.PsetQto,
|
||||
@@ -120,36 +127,51 @@ classes = [
|
||||
prop.BIMCollectionProperties,
|
||||
prop.BIMMaterialProperties,
|
||||
prop.BIMMeshProperties,
|
||||
prop.BIMFacet,
|
||||
prop.BIMFilterGroup,
|
||||
ui.BIM_UL_generic,
|
||||
ui.BIM_UL_topics,
|
||||
ui.BIM_ADDON_preferences,
|
||||
# Project overview
|
||||
ui.BIM_PT_project_info,
|
||||
ui.BIM_PT_project_setup,
|
||||
ui.BIM_PT_geometry,
|
||||
ui.BIM_PT_tab_grouping_and_filtering,
|
||||
# Tabs panel
|
||||
ui.BIM_PT_tabs,
|
||||
# Project overview
|
||||
ui.BIM_PT_tab_project_info,
|
||||
ui.BIM_PT_tab_project_setup,
|
||||
ui.BIM_PT_tab_geometry,
|
||||
ui.BIM_PT_tab_stakeholders,
|
||||
ui.BIM_PT_tab_grouping_and_filtering,
|
||||
# Object information
|
||||
ui.BIM_PT_tab_object_metadata,
|
||||
ui.BIM_PT_tab_misc,
|
||||
# Geometry and materials
|
||||
ui.BIM_PT_tab_placement,
|
||||
ui.BIM_PT_tab_representations,
|
||||
ui.BIM_PT_tab_geometric_relationships,
|
||||
ui.BIM_PT_tab_parametric_geometry,
|
||||
ui.BIM_PT_tab_profiles,
|
||||
ui.BIM_PT_tab_materials,
|
||||
ui.BIM_PT_tab_styles,
|
||||
# Drawings and documents
|
||||
ui.BIM_PT_tab_sheets,
|
||||
ui.BIM_PT_tab_drawings,
|
||||
ui.BIM_PT_tab_schedules,
|
||||
ui.BIM_PT_tab_references,
|
||||
# Services and systems
|
||||
ui.BIM_PT_tab_services,
|
||||
ui.BIM_PT_tab_services_object,
|
||||
ui.BIM_PT_tab_zones,
|
||||
# Structural analysis
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
ui.BIM_PT_tab_status,
|
||||
ui.BIM_PT_tab_resources,
|
||||
ui.BIM_PT_tab_cost,
|
||||
ui.BIM_PT_tab_sequence,
|
||||
# Facility management
|
||||
ui.BIM_PT_tab_handover,
|
||||
ui.BIM_PT_tab_operations,
|
||||
# Quality and coordination
|
||||
ui.BIM_PT_tab_quality_control,
|
||||
ui.BIM_PT_tab_clash_detection,
|
||||
ui.BIM_PT_tab_collaboration,
|
||||
ui.BIM_PT_tab_sandbox,
|
||||
# TODO: move this somewhere else and clean it up
|
||||
@@ -163,6 +185,7 @@ addon_keymaps = []
|
||||
icons = None
|
||||
is_registering = False
|
||||
last_commit_hash = "8888888"
|
||||
overridden_scene_panels = dict()
|
||||
|
||||
|
||||
def on_register(scene):
|
||||
@@ -187,6 +210,7 @@ def register():
|
||||
bpy.app.handlers.load_post.append(handler.loadIfcStore)
|
||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||
bpy.types.Screen.BIMAreaProperties = bpy.props.CollectionProperty(type=prop.BIMAreaProperties)
|
||||
bpy.types.Screen.BIMTabProperties = bpy.props.PointerProperty(type=prop.BIMTabProperties)
|
||||
bpy.types.Collection.BIMCollectionProperties = bpy.props.PointerProperty(type=prop.BIMCollectionProperties)
|
||||
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||
@@ -204,8 +228,8 @@ def register():
|
||||
|
||||
wm = bpy.context.window_manager
|
||||
if wm.keyconfigs.addon:
|
||||
km = wm.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY')
|
||||
kmi = km.keymap_items.new('bim.switch_tab', 'TAB', 'PRESS', ctrl=True)
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Window", space_type="EMPTY")
|
||||
kmi = km.keymap_items.new("bim.switch_tab", "TAB", "PRESS", ctrl=True)
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
global icons
|
||||
@@ -223,12 +247,16 @@ def register():
|
||||
global last_commit_hash
|
||||
try:
|
||||
import git
|
||||
|
||||
path = Path(__file__).resolve().parent
|
||||
repo = git.Repo(str(path), search_parent_directories=True)
|
||||
last_commit_hash = repo.head.object.hexsha
|
||||
except:
|
||||
pass
|
||||
|
||||
bpy.app.translations.register("blenderbim", translations_dict)
|
||||
|
||||
|
||||
def unregister():
|
||||
global icons
|
||||
|
||||
@@ -265,16 +293,10 @@ def unregister():
|
||||
km.keymap_items.remove(kmi)
|
||||
addon_keymaps.clear()
|
||||
|
||||
for panel in [
|
||||
"SCENE_PT_scene",
|
||||
"SCENE_PT_unit",
|
||||
"SCENE_PT_physics",
|
||||
"SCENE_PT_rigid_body_world",
|
||||
"SCENE_PT_audio",
|
||||
"SCENE_PT_keying_sets",
|
||||
"SCENE_PT_custom_props",
|
||||
]:
|
||||
try:
|
||||
bpy.utils.unregister_class(getattr(handler, f"Override_{panel}"))
|
||||
except:
|
||||
pass
|
||||
for panel in tuple(overridden_scene_panels.keys()):
|
||||
original_panel, override_panel = overridden_scene_panels[panel]
|
||||
bpy.utils.unregister_class(override_panel)
|
||||
bpy.utils.register_class(original_panel)
|
||||
del overridden_scene_panels[panel]
|
||||
|
||||
bpy.app.translations.unregister("blenderbim")
|
||||
|
||||
@@ -44,17 +44,29 @@
|
||||
<circle r="5" fill="white" stroke="black" style="stroke-width: 0.25;" />
|
||||
<line x1="-5" y1="0" x2="5" y2="0" style="stroke: black; stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="SurveyArea">
|
||||
<g id="dot">
|
||||
<circle r="1" fill="black" stroke="black" style="stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="SurveyArea-CONTROLPOINT">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.25;" d="M 2.286165,1.4798666 H -1.0000316e-7 -2.2861651 L -1.1430826,-0.5000101 -1.0000316e-7,-2.479888 1.1430827,-0.5000101 Z" />
|
||||
<g id="setout-tag">
|
||||
<circle fill="white" stroke="black" r="2.5" style="stroke-width: 0.5"/>
|
||||
<path d="M 0 0 L -2.5 0 A 2.5 2.5 0 0 1 0 -2.5 Z" fill="black"/>
|
||||
<path d="M 0 0 L 2.5 0 A 2.5 2.5 0 0 1 0 2.5 Z" fill="black"/>
|
||||
<text x="3" y="-8" class="regular" text-anchor="start" dominant-baseline="auto" data-type="text-template"></text>
|
||||
<text x="3" y="-4" class="regular" text-anchor="start" dominant-baseline="auto" data-type="text-template"></text>
|
||||
</g>
|
||||
<g id="setout-point">
|
||||
<circle fill="white" stroke="black" r="2.5" style="stroke-width: 0.5"/>
|
||||
<path d="M 0 0 L -2.5 0 A 2.5 2.5 0 0 1 0 -2.5 Z" fill="black"/>
|
||||
<path d="M 0 0 L 2.5 0 A 2.5 2.5 0 0 1 0 2.5 Z" fill="black"/>
|
||||
</g>
|
||||
<g id="control-point">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.5;" d="M 2.286165,1.4798666 H -1.0000316e-7 -2.2861651 L -1.1430826,-0.5000101 -1.0000316e-7,-2.479888 1.1430827,-0.5000101 Z" />
|
||||
<circle r="0.5" fill="black" stroke="black" style="stroke-width: 0.25;" />
|
||||
</g>
|
||||
<g id="SurveyArea-TRAVERSEPOINT">
|
||||
<g id="traverse-point">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.5;" d="M -2,-2 2,2 M -2,2 2,-2" />
|
||||
</g>
|
||||
<g id="SurveyArea-SPOTELEVATION">
|
||||
<g id="spot-elevation">
|
||||
<path style="fill: white; stroke: black; stroke-width: 0.5;" d="M 0,-2 0,2 M -2,0 2,0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -51,5 +51,5 @@ var json_data = `{{{json_data}}}`;
|
||||
<script>
|
||||
var data = `{{{data}}}`;
|
||||
setupPage(data);
|
||||
create_gantt_chart(json_data)
|
||||
</script>
|
||||
create_gantt_chart(atob(json_data))
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
|
||||
FILE_NAME('/dev/null','2023-07-30T21:07:33+10:00',(),(),'IfcOpenShell v0.7.0-8f41ae0c1','IfcOpenShell v0.7.0-8f41ae0c1','Nobody');
|
||||
FILE_NAME('/dev/null','2023-09-07T13:40:06+10:00',(),(),'IfcOpenShell v0.7.0-fbd8ea1ed','IfcOpenShell v0.7.0-fbd8ea1ed','Nobody');
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROJECT('0TszJmTqLCyxcP10DtK_MP',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('0Y_CJXSsbCgBlSGFw0mbLa',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
|
||||
#3=IFCRELDECLARES('3IxQaOPH5Fx90CGmGm1gtQ',$,$,$,#1,(#2));
|
||||
#1=IFCPROJECT('1arVsMni92iPUaaryGTUz2',$,'BlenderBIM Demo',$,$,$,$,(#12,#16),#7);
|
||||
#2=IFCPROJECTLIBRARY('1FfDG5b21DuBDOO4eUE9rF',$,'BlenderBIM Demo Library',$,$,$,$,$,$);
|
||||
#3=IFCRELDECLARES('3tfDIC3Tf9wP8EUOoFeycK',$,$,$,#1,(#2));
|
||||
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#6=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#7=IFCUNITASSIGNMENT((#6,#4,#5));
|
||||
#7=IFCUNITASSIGNMENT((#5,#6,#4));
|
||||
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#9=IFCDIRECTION((0.,0.,1.));
|
||||
#10=IFCDIRECTION((1.,0.,0.));
|
||||
@@ -25,82 +25,82 @@ DATA;
|
||||
#18=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
|
||||
#19=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#16,$,.PLAN_VIEW.,$);
|
||||
#20=IFCMATERIAL('Unknown',$,$);
|
||||
#21=IFCWALLTYPE('3U0gAUglf9WBlcfpB15EK0',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#21=IFCWALLTYPE('2o8NqQQbHA5BeErrgwfr7d',$,'WAL50',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#22=IFCMATERIALLAYERSET((#24),$,$);
|
||||
#23=IFCRELASSOCIATESMATERIAL('3J36H22Gz1ovVzdiKgApaM',$,$,$,(#21),#22);
|
||||
#23=IFCRELASSOCIATESMATERIAL('1Q$5DZKr98n82Eitz2dWey',$,$,$,(#21),#22);
|
||||
#24=IFCMATERIALLAYER(#20,0.05,$,$,$,$,$);
|
||||
#25=IFCRELDECLARES('28ApgMJzH10h$Ng$fmCpYf',$,$,$,#2,(#63,#21,#54,#87,#153,#34,#67,#210,#38,#82,#77,#42,#26,#48,#72,#59,#92,#30,#96));
|
||||
#26=IFCWALLTYPE('1wGfdUdC10TuASJo4jnO99',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#25=IFCRELDECLARES('1KXKUEn8z0g8lvyEsCTWD3',$,$,$,#2,(#38,#82,#77,#42,#26,#48,#72,#59,#92,#30,#96,#63,#21,#54,#87,#153,#34,#67,#210));
|
||||
#26=IFCWALLTYPE('3KlIOv_P9A79tkt8D_jq_m',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#27=IFCMATERIALLAYERSET((#29),$,$);
|
||||
#28=IFCRELASSOCIATESMATERIAL('0NRoxSQPzCgQHfsu5ru91z',$,$,$,(#26),#27);
|
||||
#28=IFCRELASSOCIATESMATERIAL('32B9Wx8Z1FxhIN7m77MiHG',$,$,$,(#26),#27);
|
||||
#29=IFCMATERIALLAYER(#20,0.1,$,$,$,$,$);
|
||||
#30=IFCWALLTYPE('1lt5MuGLLDfwmZd0tNruF7',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#30=IFCWALLTYPE('2DqrSeel1AGw6h3b$ujafZ',$,'WAL200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#31=IFCMATERIALLAYERSET((#33),$,$);
|
||||
#32=IFCRELASSOCIATESMATERIAL('21rGVxPODC$xmWYFaDy1T6',$,$,$,(#30),#31);
|
||||
#32=IFCRELASSOCIATESMATERIAL('2FeYUpt3D4tAvwgGmwXWZn',$,$,$,(#30),#31);
|
||||
#33=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#34=IFCWALLTYPE('0NZm1D8h12xAxyW7fL5nc1',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#34=IFCWALLTYPE('2jNdEOFIP1eQb3WZePlFGY',$,'WAL300',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#35=IFCMATERIALLAYERSET((#37),$,$);
|
||||
#36=IFCRELASSOCIATESMATERIAL('2yvmN42crCJevtirp5cMeV',$,$,$,(#34),#35);
|
||||
#36=IFCRELASSOCIATESMATERIAL('2bFeM6xevAwucuwMlauX77',$,$,$,(#34),#35);
|
||||
#37=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
|
||||
#38=IFCCOVERINGTYPE('1eb4wZ_5n6xRGrs3rlKMiM',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#38=IFCCOVERINGTYPE('3310gCgH59w9tu$DfmiCEN',$,'COV10',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#39=IFCMATERIALLAYERSET((#41),$,$);
|
||||
#40=IFCRELASSOCIATESMATERIAL('3HKIKf06T6xRqM6Lp69$OM',$,$,$,(#38),#39);
|
||||
#40=IFCRELASSOCIATESMATERIAL('3pTNmWd_jBoOWyHc8Yf4qI',$,$,$,(#38),#39);
|
||||
#41=IFCMATERIALLAYER(#20,0.01,$,$,$,$,$);
|
||||
#42=IFCCOVERINGTYPE('2E89aYmtrEfxhX_jsNqviN',$,'COV20',$,$,(#46),$,$,$,.NOTDEFINED.);
|
||||
#42=IFCCOVERINGTYPE('2sksWFuPzCgeNk1ofXS6PZ',$,'COV20',$,$,(#46),$,$,$,.NOTDEFINED.);
|
||||
#43=IFCMATERIALLAYERSET((#45),$,$);
|
||||
#44=IFCRELASSOCIATESMATERIAL('2115lEhhP4Z9G1Yhd_DqSC',$,$,$,(#42),#43);
|
||||
#44=IFCRELASSOCIATESMATERIAL('36rl5NddT1ux6ejkQAkT2z',$,$,$,(#42),#43);
|
||||
#45=IFCMATERIALLAYER(#20,0.02,$,$,$,$,$);
|
||||
#46=IFCPROPERTYSET('0vtU63YmjAgg8DeHlTgNx7',$,'EPset_Parametric',$,(#47));
|
||||
#46=IFCPROPERTYSET('2VZJ9xfdn1NeOY6uEmnnYW',$,'EPset_Parametric',$,(#47));
|
||||
#47=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS2'),$);
|
||||
#48=IFCCOVERINGTYPE('0XiSBI57L9Uf8f2NaQ_ksi',$,'COV30',$,$,(#52),$,$,$,.NOTDEFINED.);
|
||||
#48=IFCCOVERINGTYPE('1uU6rxt95AC91lZ0jTRG6G',$,'COV30',$,$,(#52),$,$,$,.NOTDEFINED.);
|
||||
#49=IFCMATERIALLAYERSET((#51),$,$);
|
||||
#50=IFCRELASSOCIATESMATERIAL('3U3JdGAmbABA4mB$Fgpq1l',$,$,$,(#48),#49);
|
||||
#50=IFCRELASSOCIATESMATERIAL('2lFv4xQYr5bwtiSsMTVCgz',$,$,$,(#48),#49);
|
||||
#51=IFCMATERIALLAYER(#20,0.03,$,$,$,$,$);
|
||||
#52=IFCPROPERTYSET('17nhQ5I4b9ZvhDu4gRJLKO',$,'EPset_Parametric',$,(#53));
|
||||
#52=IFCPROPERTYSET('0HnrRhcd16Yfbgtci4sqCG',$,'EPset_Parametric',$,(#53));
|
||||
#53=IFCPROPERTYSINGLEVALUE('LayerSetDirection',$,IFCLABEL('AXIS3'),$);
|
||||
#54=IFCRAMPTYPE('1BTHjVDP19tgb3MDCSkWCJ',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#54=IFCRAMPTYPE('3nonXCVn58jhVV08N8c97B',$,'RAM200',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#55=IFCMATERIALLAYERSET((#57),$,$);
|
||||
#56=IFCRELASSOCIATESMATERIAL('3HN21bHtL16vvHA2GnnxH1',$,$,$,(#54),#55);
|
||||
#56=IFCRELASSOCIATESMATERIAL('2NdIrmbUP6bh4x$p_RY31C',$,$,$,(#54),#55);
|
||||
#57=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#58=IFCCIRCLEPROFILEDEF(.AREA.,$,$,0.3);
|
||||
#59=IFCPILETYPE('04_2WZSqH8TgT7ET6DCCpd',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#59=IFCPILETYPE('2uuf5kq5TDDfO9jGN7XIHh',$,'P1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#60=IFCMATERIALPROFILESET($,$,(#62),$);
|
||||
#61=IFCRELASSOCIATESMATERIAL('1qTZZRXsPF9hmYwT$tgFgp',$,$,$,(#59),#60);
|
||||
#61=IFCRELASSOCIATESMATERIAL('3hi821Ffj8YR9yFjp226LU',$,$,$,(#59),#60);
|
||||
#62=IFCMATERIALPROFILE($,$,#20,#58,$,$);
|
||||
#63=IFCSLABTYPE('3_OV9$Sd52KRhIW2TWIxbW',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#63=IFCSLABTYPE('2janmz3nH4P9IHaOVvGY_B',$,'FLR150',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#64=IFCMATERIALLAYERSET((#66),$,$);
|
||||
#65=IFCRELASSOCIATESMATERIAL('1agyf45$X1NhR127pUL0Iw',$,$,$,(#63),#64);
|
||||
#65=IFCRELASSOCIATESMATERIAL('0og5Zg4ib67g6XWgOgqBWy',$,$,$,(#63),#64);
|
||||
#66=IFCMATERIALLAYER(#20,0.2,$,$,$,$,$);
|
||||
#67=IFCSLABTYPE('0LvmpIGf9AVPcG$Kt06TEk',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#67=IFCSLABTYPE('26UKpEaTb9fh4qrqi3Ymzj',$,'FLR250',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#68=IFCMATERIALLAYERSET((#70),$,$);
|
||||
#69=IFCRELASSOCIATESMATERIAL('0EKQPSL656secN86SgeutH',$,$,$,(#67),#68);
|
||||
#69=IFCRELASSOCIATESMATERIAL('1BeBXPqen9TQEE42SxzhjF',$,$,$,(#67),#68);
|
||||
#70=IFCMATERIALLAYER(#20,0.3,$,$,$,$,$);
|
||||
#71=IFCRECTANGLEPROFILEDEF(.AREA.,'500x600',$,0.5,0.6);
|
||||
#72=IFCCOLUMNTYPE('1SI8m9m813tAXfSnM1fCbm',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#72=IFCCOLUMNTYPE('1KnWzBBoLCNfJ3_J79LT7d',$,'C1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#73=IFCMATERIALPROFILESET($,$,(#75),$);
|
||||
#74=IFCRELASSOCIATESMATERIAL('1EhwSTRtPBQgBt2x6dhuT$',$,$,$,(#72),#73);
|
||||
#74=IFCRELASSOCIATESMATERIAL('1W8OWQjEL05gH5hEpve6Lh',$,$,$,(#72),#73);
|
||||
#75=IFCMATERIALPROFILE($,$,#20,#71,$,$);
|
||||
#76=IFCCIRCLEHOLLOWPROFILEDEF(.AREA.,'500.0x5.0 CHS',$,0.25,0.005);
|
||||
#77=IFCCOLUMNTYPE('25GZwy2zLFcgi57r5S8iY3',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#77=IFCCOLUMNTYPE('33jPFERfL9bfTeWfTL5kZ7',$,'C2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#78=IFCMATERIALPROFILESET($,$,(#80),$);
|
||||
#79=IFCRELASSOCIATESMATERIAL('2VURAOhz93ZQqT9YJUA1DO',$,$,$,(#77),#78);
|
||||
#79=IFCRELASSOCIATESMATERIAL('3czOrQw2v9BfS0PeQCrzWq',$,$,$,(#77),#78);
|
||||
#80=IFCMATERIALPROFILE($,$,#20,#76,$,$);
|
||||
#81=IFCRECTANGLEHOLLOWPROFILEDEF(.AREA.,'150x75x2.0 RHS',$,0.075,0.15,0.002,0.005,0.005);
|
||||
#82=IFCCOLUMNTYPE('1Wg$I84s9BTh$iDuZnmSkH',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#82=IFCCOLUMNTYPE('21YcNdKNj2$95U55yNx1Nw',$,'C3',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#83=IFCMATERIALPROFILESET($,$,(#85),$);
|
||||
#84=IFCRELASSOCIATESMATERIAL('3fIIwEe7DAle7x_zISvMUD',$,$,$,(#82),#83);
|
||||
#84=IFCRELASSOCIATESMATERIAL('3n6Cru3xr6jvDptU9u7QEF',$,$,$,(#82),#83);
|
||||
#85=IFCMATERIALPROFILE($,$,#20,#81,$,$);
|
||||
#86=IFCISHAPEPROFILEDEF(.AREA.,'DEMO-I',$,0.1,0.2,0.005,0.01,0.005,$,$);
|
||||
#87=IFCBEAMTYPE('2yg2Irs7L26gFsD_AvvFUu',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#87=IFCBEAMTYPE('2CsGpD$6nDCxWv9jXTtvq9',$,'B1',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#88=IFCMATERIALPROFILESET($,$,(#90),$);
|
||||
#89=IFCRELASSOCIATESMATERIAL('1PU_gbkifF9hYpi9DvXTAU',$,$,$,(#87),#88);
|
||||
#89=IFCRELASSOCIATESMATERIAL('2LJExc74j9rgBSBx51HDPK',$,$,$,(#87),#88);
|
||||
#90=IFCMATERIALPROFILE($,$,#20,#86,$,$);
|
||||
#91=IFCCSHAPEPROFILEDEF(.AREA.,'DEMO-C',$,0.2,0.1,0.0015,0.03,0.005);
|
||||
#92=IFCBEAMTYPE('2Nf2L5qpz8K8KhUNFBg_fT',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#92=IFCBEAMTYPE('32pHN2b0P0awGw$U8PpneO',$,'B2',$,$,$,$,$,$,.NOTDEFINED.);
|
||||
#93=IFCMATERIALPROFILESET($,$,(#95),$);
|
||||
#94=IFCRELASSOCIATESMATERIAL('1SgarGNCzEQQpfs29NMq0y',$,$,$,(#92),#93);
|
||||
#94=IFCRELASSOCIATESMATERIAL('2wg8R1Drb2xhQ2Y_pRSh3c',$,$,$,(#92),#93);
|
||||
#95=IFCMATERIALPROFILE($,$,#20,#91,$,$);
|
||||
#96=IFCWINDOWTYPE('2zRUFP8Ef81xDOR2iJx1zK',$,'WT01',$,$,$,(#135,#152),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#96=IFCWINDOWTYPE('1Gg9HfZEr69u8SdWfDs2J3',$,'WT01',$,$,$,(#135,#152),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#97=IFCINDEXEDPOLYGONALFACE((13,17,18,14));
|
||||
#98=IFCINDEXEDPOLYGONALFACE((5,6,3,4));
|
||||
#99=IFCINDEXEDPOLYGONALFACE((7,8,2,1));
|
||||
@@ -157,7 +157,7 @@ DATA;
|
||||
#150=IFCDIRECTION((0.,0.,1.));
|
||||
#151=IFCAXIS2PLACEMENT3D(#148,#150,#149);
|
||||
#152=IFCREPRESENTATIONMAP(#151,#147);
|
||||
#153=IFCDOORTYPE('0oekOIA$z8lxWiQs9_tFWU',$,'DT01',$,$,$,(#196,#209),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#153=IFCDOORTYPE('0NBUmPKyT9WecsIeYJrEqg',$,'DT01',$,$,$,(#196,#209),$,$,.NOTDEFINED.,.NOTDEFINED.,$,$);
|
||||
#154=IFCINDEXEDPOLYGONALFACE((17,16,15,14));
|
||||
#155=IFCINDEXEDPOLYGONALFACE((2,3,29,28));
|
||||
#156=IFCINDEXEDPOLYGONALFACE((27,28,29,30,32,31));
|
||||
@@ -214,7 +214,7 @@ DATA;
|
||||
#207=IFCDIRECTION((0.,0.,1.));
|
||||
#208=IFCAXIS2PLACEMENT3D(#205,#207,#206);
|
||||
#209=IFCREPRESENTATIONMAP(#208,#204);
|
||||
#210=IFCFURNITURETYPE('0I2mc$m3T60hLXZYg9Y1wu',$,'BUN01',$,$,$,(#931,#952),$,$,.NOTDEFINED.,.NOTDEFINED.);
|
||||
#210=IFCFURNITURETYPE('3Kyc6IyarAUw3_8fkNtXIg',$,'BUN01',$,$,$,(#931,#952),$,$,.NOTDEFINED.,.NOTDEFINED.);
|
||||
#211=IFCINDEXEDPOLYGONALFACE((187,278,44));
|
||||
#212=IFCINDEXEDPOLYGONALFACE((21,52,60));
|
||||
#213=IFCINDEXEDPOLYGONALFACE((91,100,31));
|
||||
@@ -957,131 +957,161 @@ DATA;
|
||||
#950=IFCDIRECTION((0.,0.,1.));
|
||||
#951=IFCAXIS2PLACEMENT3D(#948,#950,#949);
|
||||
#952=IFCREPRESENTATIONMAP(#951,#947);
|
||||
#953=IFCTYPEPRODUCT('2be705eyD4DPBr6j4K7ZEx',$,'DASHED',$,'IfcAnnotation/LINEWORK',(#954),$,$);
|
||||
#954=IFCPROPERTYSET('0uEfutIxDDIxMIBUJOZpSM',$,'EPset_Annotation',$,(#955));
|
||||
#955=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('dashed'),$);
|
||||
#956=IFCTYPEPRODUCT('1M7mm9wUHFoxdM0MdAMyqM',$,'FINE',$,'IfcAnnotation/LINEWORK',(#957),$,$);
|
||||
#957=IFCPROPERTYSET('3AZ8m1Bdn1gfbvagzKr2c3',$,'EPset_Annotation',$,(#958));
|
||||
#958=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('fine'),$);
|
||||
#959=IFCTYPEPRODUCT('3LdrNIFOvAkBzrNnqWAZfA',$,'THIN',$,'IfcAnnotation/LINEWORK',(#960),$,$);
|
||||
#960=IFCPROPERTYSET('0$9iwgSID7xBul4Tb9r2du',$,'EPset_Annotation',$,(#961));
|
||||
#961=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thin'),$);
|
||||
#962=IFCTYPEPRODUCT('3fFNO0t1TB$wYRaIkKwUCH',$,'MEDIUM',$,'IfcAnnotation/LINEWORK',(#963),$,$);
|
||||
#963=IFCPROPERTYSET('33Fm$9zgb1cgsCOy859RlR',$,'EPset_Annotation',$,(#964));
|
||||
#964=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('medium'),$);
|
||||
#965=IFCTYPEPRODUCT('1CpMBhemj5VQC43bU9J7zG',$,'THICK',$,'IfcAnnotation/LINEWORK',(#966),$,$);
|
||||
#966=IFCPROPERTYSET('1OLDsFSDz2TPz5ifkQvWo8',$,'EPset_Annotation',$,(#967));
|
||||
#967=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thick'),$);
|
||||
#968=IFCTYPEPRODUCT('0NITtya3b16hoA3sh17SL4',$,'STRONG',$,'IfcAnnotation/LINEWORK',(#969),$,$);
|
||||
#969=IFCPROPERTYSET('1_kfqWVDn0puI29Ah6eitX',$,'EPset_Annotation',$,(#970));
|
||||
#970=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('strong'),$);
|
||||
#971=IFCTYPEPRODUCT('0rRN9EsvL5AQ3PorxrZSKM',$,'DOOR-TAG',$,'IfcAnnotation/TEXT',(#972),(#991),$);
|
||||
#972=IFCPROPERTYSET('3lRZ$d2VH8cxZYh$mm45iq',$,'EPset_Annotation',$,(#973));
|
||||
#973=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('door-tag'),$);
|
||||
#974=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#975=IFCDIRECTION((0.,0.,1.));
|
||||
#976=IFCDIRECTION((1.,0.,0.));
|
||||
#977=IFCAXIS2PLACEMENT3D(#974,#975,#976);
|
||||
#978=IFCPLANAREXTENT(1000.,1000.);
|
||||
#979=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#977,.RIGHT.,#978,'center');
|
||||
#980=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#981=IFCDIRECTION((0.,0.,1.));
|
||||
#982=IFCDIRECTION((1.,0.,0.));
|
||||
#983=IFCAXIS2PLACEMENT3D(#980,#981,#982);
|
||||
#984=IFCPLANAREXTENT(1000.,1000.);
|
||||
#985=IFCTEXTLITERALWITHEXTENT('{{Name}}',#983,.RIGHT.,#984,'center');
|
||||
#986=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#979,#985));
|
||||
#987=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#988=IFCDIRECTION((1.,0.,0.));
|
||||
#989=IFCDIRECTION((0.,0.,1.));
|
||||
#990=IFCAXIS2PLACEMENT3D(#987,#989,#988);
|
||||
#991=IFCREPRESENTATIONMAP(#990,#986);
|
||||
#992=IFCTYPEPRODUCT('0bFZUWHjbA2e03hRHNinTt',$,'WINDOW-TAG',$,'IfcAnnotation/TEXT',(#993),(#1006),$);
|
||||
#993=IFCPROPERTYSET('3TJOWv5NDBXBNCshYFjsFV',$,'EPset_Annotation',$,(#994));
|
||||
#994=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('window-tag'),$);
|
||||
#995=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#996=IFCDIRECTION((0.,0.,1.));
|
||||
#953=IFCTYPEPRODUCT('0TBMBnD_b66QLUWKD9HLsc',$,'SETOUT-POINT',$,'IfcAnnotation/SYMBOL',(#954),$,$);
|
||||
#954=IFCPROPERTYSET('2dQzX_K4r1Xet6dz9zBlgs',$,'EPset_Annotation',$,(#955));
|
||||
#955=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('setout-point'),$);
|
||||
#956=IFCTYPEPRODUCT('0vo_7PU3H9ygTnrIipqPRI',$,'CONTROL-POINT',$,'IfcAnnotation/SYMBOL',(#957),$,$);
|
||||
#957=IFCPROPERTYSET('3APQOw$FP1ivxp08UxHsl6',$,'EPset_Annotation',$,(#958));
|
||||
#958=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('control-point'),$);
|
||||
#959=IFCTYPEPRODUCT('32V27G3$T1OO3TbXg6948F',$,'TRAVERSE-POINT',$,'IfcAnnotation/SYMBOL',(#960),$,$);
|
||||
#960=IFCPROPERTYSET('0cTSYXMc9B$PTXVDrhBYYW',$,'EPset_Annotation',$,(#961));
|
||||
#961=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('traverse-point'),$);
|
||||
#962=IFCTYPEPRODUCT('2PXIC7Bg914gJhRh2XeGnN',$,'DASHED',$,'IfcAnnotation/LINEWORK',(#963),$,$);
|
||||
#963=IFCPROPERTYSET('0RiYsOxp529gXnCkw44wF8',$,'EPset_Annotation',$,(#964));
|
||||
#964=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('dashed'),$);
|
||||
#965=IFCTYPEPRODUCT('1Ou1kA3Vb4HhuNBvM0uGe0',$,'FINE',$,'IfcAnnotation/LINEWORK',(#966),$,$);
|
||||
#966=IFCPROPERTYSET('2gzly9D0L1qPK2MExeXgRO',$,'EPset_Annotation',$,(#967));
|
||||
#967=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('fine'),$);
|
||||
#968=IFCTYPEPRODUCT('3lx$KQPRbEZwbQ7Xdfm5gw',$,'THIN',$,'IfcAnnotation/LINEWORK',(#969),$,$);
|
||||
#969=IFCPROPERTYSET('0vsVpqs6zArvA2bfBtvQew',$,'EPset_Annotation',$,(#970));
|
||||
#970=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thin'),$);
|
||||
#971=IFCTYPEPRODUCT('00j$y97p903w2HOb35lAQ2',$,'MEDIUM',$,'IfcAnnotation/LINEWORK',(#972),$,$);
|
||||
#972=IFCPROPERTYSET('240rEBDOn8PAvfnm_43s55',$,'EPset_Annotation',$,(#973));
|
||||
#973=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('medium'),$);
|
||||
#974=IFCTYPEPRODUCT('2tVdFGorj6dfrL4uA1kyW8',$,'THICK',$,'IfcAnnotation/LINEWORK',(#975),$,$);
|
||||
#975=IFCPROPERTYSET('2IEGHncr1D$R8fKA9zCEJP',$,'EPset_Annotation',$,(#976));
|
||||
#976=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('thick'),$);
|
||||
#977=IFCTYPEPRODUCT('1T5C$$ONTBB8A9a7vv0Gn6',$,'STRONG',$,'IfcAnnotation/LINEWORK',(#978),$,$);
|
||||
#978=IFCPROPERTYSET('1gRomAS1L2WguA51ZI0E40',$,'EPset_Annotation',$,(#979));
|
||||
#979=IFCPROPERTYSINGLEVALUE('Classes',$,IFCLABEL('strong'),$);
|
||||
#980=IFCTYPEPRODUCT('2LmKYrAEr2K8EwJyCAsyc4',$,'SETOUT-TAG',$,'IfcAnnotation/TEXT',(#981),(#1000),$);
|
||||
#981=IFCPROPERTYSET('0nF9du8qDAF9aldqjCUsbX',$,'EPset_Annotation',$,(#982));
|
||||
#982=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('setout-tag'),$);
|
||||
#983=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#984=IFCDIRECTION((0.,0.,1.));
|
||||
#985=IFCDIRECTION((1.,0.,0.));
|
||||
#986=IFCAXIS2PLACEMENT3D(#983,#984,#985);
|
||||
#987=IFCPLANAREXTENT(1000.,1000.);
|
||||
#988=IFCTEXTLITERALWITHEXTENT('E ``round({{easting}}, 0.001)``',#986,.RIGHT.,#987,'center');
|
||||
#989=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#990=IFCDIRECTION((0.,0.,1.));
|
||||
#991=IFCDIRECTION((1.,0.,0.));
|
||||
#992=IFCAXIS2PLACEMENT3D(#989,#990,#991);
|
||||
#993=IFCPLANAREXTENT(1000.,1000.);
|
||||
#994=IFCTEXTLITERALWITHEXTENT('N ``round({{northing}}, 0.001)``',#992,.RIGHT.,#993,'center');
|
||||
#995=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#988,#994));
|
||||
#996=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#997=IFCDIRECTION((1.,0.,0.));
|
||||
#998=IFCAXIS2PLACEMENT3D(#995,#996,#997);
|
||||
#999=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1000=IFCTEXTLITERALWITHEXTENT('{{Name}}',#998,.RIGHT.,#999,'center');
|
||||
#1001=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1000));
|
||||
#1002=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1003=IFCDIRECTION((1.,0.,0.));
|
||||
#1004=IFCDIRECTION((0.,0.,1.));
|
||||
#1005=IFCAXIS2PLACEMENT3D(#1002,#1004,#1003);
|
||||
#1006=IFCREPRESENTATIONMAP(#1005,#1001);
|
||||
#1007=IFCTYPEPRODUCT('2DlmDdasX05un_uMP4O23U',$,'SPACE-TAG',$,'IfcAnnotation/TEXT',(#1008),(#1033),$);
|
||||
#1008=IFCPROPERTYSET('1$OAHURHr3GON7ZzAJnv72',$,'EPset_Annotation',$,(#1009));
|
||||
#1009=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('space-tag'),$);
|
||||
#998=IFCDIRECTION((0.,0.,1.));
|
||||
#999=IFCAXIS2PLACEMENT3D(#996,#998,#997);
|
||||
#1000=IFCREPRESENTATIONMAP(#999,#995);
|
||||
#1001=IFCTYPEPRODUCT('1cdhVmqEPF6e13_TFOdlQw',$,'DOOR-TAG',$,'IfcAnnotation/TEXT',(#1002),(#1021),$);
|
||||
#1002=IFCPROPERTYSET('1MUPGQolnDEgp0NxcMN56E',$,'EPset_Annotation',$,(#1003));
|
||||
#1003=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('door-tag'),$);
|
||||
#1004=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1005=IFCDIRECTION((0.,0.,1.));
|
||||
#1006=IFCDIRECTION((1.,0.,0.));
|
||||
#1007=IFCAXIS2PLACEMENT3D(#1004,#1005,#1006);
|
||||
#1008=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1009=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#1007,.RIGHT.,#1008,'center');
|
||||
#1010=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1011=IFCDIRECTION((0.,0.,1.));
|
||||
#1012=IFCDIRECTION((1.,0.,0.));
|
||||
#1013=IFCAXIS2PLACEMENT3D(#1010,#1011,#1012);
|
||||
#1014=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1015=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1013,.RIGHT.,#1014,'center');
|
||||
#1016=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1017=IFCDIRECTION((0.,0.,1.));
|
||||
#1016=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1009,#1015));
|
||||
#1017=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1018=IFCDIRECTION((1.,0.,0.));
|
||||
#1019=IFCAXIS2PLACEMENT3D(#1016,#1017,#1018);
|
||||
#1020=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1021=IFCTEXTLITERALWITHEXTENT('{{Description}}',#1019,.RIGHT.,#1020,'center');
|
||||
#1022=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1023=IFCDIRECTION((0.,0.,1.));
|
||||
#1024=IFCDIRECTION((1.,0.,0.));
|
||||
#1025=IFCAXIS2PLACEMENT3D(#1022,#1023,#1024);
|
||||
#1026=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1027=IFCTEXTLITERALWITHEXTENT('``round({{Qto_SpaceBaseQuantities.NetFloorArea}} or 0., 2)``',#1025,.RIGHT.,#1026,'center');
|
||||
#1028=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1015,#1021,#1027));
|
||||
#1029=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1030=IFCDIRECTION((1.,0.,0.));
|
||||
#1031=IFCDIRECTION((0.,0.,1.));
|
||||
#1032=IFCAXIS2PLACEMENT3D(#1029,#1031,#1030);
|
||||
#1033=IFCREPRESENTATIONMAP(#1032,#1028);
|
||||
#1034=IFCTYPEPRODUCT('2vNx3CIlrFs8bLFlmGvJF3',$,'MATERIAL-TAG',$,'IfcAnnotation/TEXT',(#1035),(#1048),$);
|
||||
#1035=IFCPROPERTYSET('1l$T4lUqnBb93zcgfbSPvx',$,'EPset_Annotation',$,(#1036));
|
||||
#1036=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('rectangle-tag'),$);
|
||||
#1037=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1038=IFCDIRECTION((0.,0.,1.));
|
||||
#1039=IFCDIRECTION((1.,0.,0.));
|
||||
#1040=IFCAXIS2PLACEMENT3D(#1037,#1038,#1039);
|
||||
#1041=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1042=IFCTEXTLITERALWITHEXTENT('{{material.Name}}',#1040,.RIGHT.,#1041,'center');
|
||||
#1043=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1042));
|
||||
#1044=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1045=IFCDIRECTION((1.,0.,0.));
|
||||
#1046=IFCDIRECTION((0.,0.,1.));
|
||||
#1047=IFCAXIS2PLACEMENT3D(#1044,#1046,#1045);
|
||||
#1048=IFCREPRESENTATIONMAP(#1047,#1043);
|
||||
#1049=IFCTYPEPRODUCT('3hqzFtbSb56RjhAZNaB4Vh',$,'TYPE-TAG',$,'IfcAnnotation/TEXT',(#1050),(#1063),$);
|
||||
#1050=IFCPROPERTYSET('3U7kaKt_nBUvD$U2ryif_H',$,'EPset_Annotation',$,(#1051));
|
||||
#1051=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1019=IFCDIRECTION((0.,0.,1.));
|
||||
#1020=IFCAXIS2PLACEMENT3D(#1017,#1019,#1018);
|
||||
#1021=IFCREPRESENTATIONMAP(#1020,#1016);
|
||||
#1022=IFCTYPEPRODUCT('0OuDi3gRH2CxW9mrtE0vXw',$,'WINDOW-TAG',$,'IfcAnnotation/TEXT',(#1023),(#1036),$);
|
||||
#1023=IFCPROPERTYSET('2X7dAo_1P5ruRnlKA4kIl6',$,'EPset_Annotation',$,(#1024));
|
||||
#1024=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('window-tag'),$);
|
||||
#1025=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1026=IFCDIRECTION((0.,0.,1.));
|
||||
#1027=IFCDIRECTION((1.,0.,0.));
|
||||
#1028=IFCAXIS2PLACEMENT3D(#1025,#1026,#1027);
|
||||
#1029=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1030=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1028,.RIGHT.,#1029,'center');
|
||||
#1031=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1030));
|
||||
#1032=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1033=IFCDIRECTION((1.,0.,0.));
|
||||
#1034=IFCDIRECTION((0.,0.,1.));
|
||||
#1035=IFCAXIS2PLACEMENT3D(#1032,#1034,#1033);
|
||||
#1036=IFCREPRESENTATIONMAP(#1035,#1031);
|
||||
#1037=IFCTYPEPRODUCT('3WEV_9wQn6AQTESgjW36PH',$,'SPACE-TAG',$,'IfcAnnotation/TEXT',(#1038),(#1063),$);
|
||||
#1038=IFCPROPERTYSET('02FAX8dIzAlunWD5ak$6sU',$,'EPset_Annotation',$,(#1039));
|
||||
#1039=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('space-tag'),$);
|
||||
#1040=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1041=IFCDIRECTION((0.,0.,1.));
|
||||
#1042=IFCDIRECTION((1.,0.,0.));
|
||||
#1043=IFCAXIS2PLACEMENT3D(#1040,#1041,#1042);
|
||||
#1044=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1045=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1043,.RIGHT.,#1044,'center');
|
||||
#1046=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1047=IFCDIRECTION((0.,0.,1.));
|
||||
#1048=IFCDIRECTION((1.,0.,0.));
|
||||
#1049=IFCAXIS2PLACEMENT3D(#1046,#1047,#1048);
|
||||
#1050=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1051=IFCTEXTLITERALWITHEXTENT('{{Description}}',#1049,.RIGHT.,#1050,'center');
|
||||
#1052=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1053=IFCDIRECTION((0.,0.,1.));
|
||||
#1054=IFCDIRECTION((1.,0.,0.));
|
||||
#1055=IFCAXIS2PLACEMENT3D(#1052,#1053,#1054);
|
||||
#1056=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1057=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#1055,.RIGHT.,#1056,'center');
|
||||
#1058=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1057));
|
||||
#1057=IFCTEXTLITERALWITHEXTENT('``round({{Qto_SpaceBaseQuantities.NetFloorArea}}, 0.01)``',#1055,.RIGHT.,#1056,'center');
|
||||
#1058=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1045,#1051,#1057));
|
||||
#1059=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1060=IFCDIRECTION((1.,0.,0.));
|
||||
#1061=IFCDIRECTION((0.,0.,1.));
|
||||
#1062=IFCAXIS2PLACEMENT3D(#1059,#1061,#1060);
|
||||
#1063=IFCREPRESENTATIONMAP(#1062,#1058);
|
||||
#1064=IFCTYPEPRODUCT('2SnSxJPiD9sh$NDx92OCSS',$,'NAME-TAG',$,'IfcAnnotation/TEXT',(#1065),(#1078),$);
|
||||
#1065=IFCPROPERTYSET('3Lu86QVuv5nfjkuPLvdQSH',$,'EPset_Annotation',$,(#1066));
|
||||
#1066=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1064=IFCTYPEPRODUCT('1evqvQLLL1zxdsIWqEn0lK',$,'MATERIAL-TAG',$,'IfcAnnotation/TEXT',(#1065),(#1078),$);
|
||||
#1065=IFCPROPERTYSET('2KPJlGyer0UBmphfR7952k',$,'EPset_Annotation',$,(#1066));
|
||||
#1066=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('rectangle-tag'),$);
|
||||
#1067=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1068=IFCDIRECTION((0.,0.,1.));
|
||||
#1069=IFCDIRECTION((1.,0.,0.));
|
||||
#1070=IFCAXIS2PLACEMENT3D(#1067,#1068,#1069);
|
||||
#1071=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1072=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1070,.RIGHT.,#1071,'center');
|
||||
#1072=IFCTEXTLITERALWITHEXTENT('{{material.Name}}',#1070,.RIGHT.,#1071,'center');
|
||||
#1073=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1072));
|
||||
#1074=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1075=IFCDIRECTION((1.,0.,0.));
|
||||
#1076=IFCDIRECTION((0.,0.,1.));
|
||||
#1077=IFCAXIS2PLACEMENT3D(#1074,#1076,#1075);
|
||||
#1078=IFCREPRESENTATIONMAP(#1077,#1073);
|
||||
#1079=IFCTYPEPRODUCT('3Nem6d4xX87O3deyWDi3AW',$,'TYPE-TAG',$,'IfcAnnotation/TEXT',(#1080),(#1093),$);
|
||||
#1080=IFCPROPERTYSET('15bspgnA9CEuFox6xFjoTL',$,'EPset_Annotation',$,(#1081));
|
||||
#1081=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1082=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1083=IFCDIRECTION((0.,0.,1.));
|
||||
#1084=IFCDIRECTION((1.,0.,0.));
|
||||
#1085=IFCAXIS2PLACEMENT3D(#1082,#1083,#1084);
|
||||
#1086=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1087=IFCTEXTLITERALWITHEXTENT('{{type.Name}}',#1085,.RIGHT.,#1086,'center');
|
||||
#1088=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1087));
|
||||
#1089=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1090=IFCDIRECTION((1.,0.,0.));
|
||||
#1091=IFCDIRECTION((0.,0.,1.));
|
||||
#1092=IFCAXIS2PLACEMENT3D(#1089,#1091,#1090);
|
||||
#1093=IFCREPRESENTATIONMAP(#1092,#1088);
|
||||
#1094=IFCTYPEPRODUCT('0klFX9AjnEnPBkdIURv8XD',$,'NAME-TAG',$,'IfcAnnotation/TEXT',(#1095),(#1108),$);
|
||||
#1095=IFCPROPERTYSET('1d53tifbv2rwcoFFJosiHf',$,'EPset_Annotation',$,(#1096));
|
||||
#1096=IFCPROPERTYSINGLEVALUE('Symbol',$,IFCLABEL('capsule-tag'),$);
|
||||
#1097=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1098=IFCDIRECTION((0.,0.,1.));
|
||||
#1099=IFCDIRECTION((1.,0.,0.));
|
||||
#1100=IFCAXIS2PLACEMENT3D(#1097,#1098,#1099);
|
||||
#1101=IFCPLANAREXTENT(1000.,1000.);
|
||||
#1102=IFCTEXTLITERALWITHEXTENT('{{Name}}',#1100,.RIGHT.,#1101,'center');
|
||||
#1103=IFCSHAPEREPRESENTATION(#19,'Annotation','Annotation2D',(#1102));
|
||||
#1104=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#1105=IFCDIRECTION((1.,0.,0.));
|
||||
#1106=IFCDIRECTION((0.,0.,1.));
|
||||
#1107=IFCAXIS2PLACEMENT3D(#1104,#1106,#1105);
|
||||
#1108=IFCREPRESENTATIONMAP(#1107,#1103);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
||||
@@ -5,27 +5,27 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21));
|
||||
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15,#16,#17,#18,#19,#20,#21,#22));
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('2T$a4OFsv2LeD5JeBKEV4f',$,'IsNTS','Whether or not the scale is intended to be significant',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#5=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#6=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#7=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#8=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#9=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#10=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','Selector expression to include ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#11=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','Selector expression to exclude ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#12=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#13=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#14=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#15=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#16=IFCSIMPLEPROPERTYTEMPLATE('1$xfo9EVb26QLqmPll2_RK',$,'MetricPrecision','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#17=IFCSIMPLEPROPERTYTEMPLATE('38uAtrp9nD_901NO42zd$7',$,'ImperialPrecision','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#18=IFCSIMPLEPROPERTYTEMPLATE('1MX0uffTL6TOvtvEJpxFmk',$,'DecimalPlaces','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
|
||||
#19=IFCSIMPLEPROPERTYTEMPLATE('0joEq0Rd10cxweEh0NeHT6',$,'JoinCriteria','Comma separated selection keys which determine what cut objects are to be joined.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#20=IFCSIMPLEPROPERTYTEMPLATE('0nYMT3OSj5gArVniCWZRtv',$,'ShadingStyles','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#21=IFCSIMPLEPROPERTYTEMPLATE('3VWG22eZXBdQwdKlzMeVQH',$,'CurrentShadingStyle','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#5=IFCSIMPLEPROPERTYTEMPLATE('2T$a4OFsv2LeD5JeBKEV4f',$,'IsNTS','Whether or not the scale is intended to be significant',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#6=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#7=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#8=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#9=IFCSIMPLEPROPERTYTEMPLATE('2NPPxuABv1huDTVh32TFgw',$,'GlobalReferencing','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
#10=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#11=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','Selector expression to include ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#12=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','Selector expression to exclude ifc elements in the drawing',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#13=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#14=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#15=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#16=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#17=IFCSIMPLEPROPERTYTEMPLATE('1$xfo9EVb26QLqmPll2_RK',$,'MetricPrecision','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#18=IFCSIMPLEPROPERTYTEMPLATE('38uAtrp9nD_901NO42zd$7',$,'ImperialPrecision','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#19=IFCSIMPLEPROPERTYTEMPLATE('1MX0uffTL6TOvtvEJpxFmk',$,'DecimalPlaces','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
|
||||
#20=IFCSIMPLEPROPERTYTEMPLATE('0joEq0Rd10cxweEh0NeHT6',$,'JoinCriteria','Comma separated selection keys which determine what cut objects are to be joined.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#21=IFCSIMPLEPROPERTYTEMPLATE('0nYMT3OSj5gArVniCWZRtv',$,'ShadingStyles','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#22=IFCSIMPLEPROPERTYTEMPLATE('3VWG22eZXBdQwdKlzMeVQH',$,'CurrentShadingStyle','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION((),'2;1');
|
||||
FILE_NAME('EPset_Status.ifc','2020-01-01T00:00:00',(),(),'EPset_Status','EPset_Status',$);
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROPERTYSETTEMPLATE('3JoyTt$cz4fhYuQvJeTMhs',$,'EPset_Status','',.PSET_TYPEDRIVENOVERRIDE.,'IfcProduct',(#2,#4));
|
||||
#2= IFCSIMPLEPROPERTYTEMPLATE('2HBOdOfz5E3hZ7IBLCq$$D',$,'Status','Status of the element, predominately used in renovation or retrofitting projects. The status can be assigned to as "New" - element designed as new addition, "Existing" - element exists and remains, "Demolish" - element existed but is to be demolished, "Temporary" - element will exists only temporary (like a temporary support structure).',.P_ENUMERATEDVALUE.,'IfcLabel',$,#3,$,$,$,.READWRITE.);
|
||||
#3= IFCPROPERTYENUMERATION('EPEnum_ElementStatus',(IFCLABEL('NEW'),IFCLABEL('EXISTING'),IFCLABEL('DEMOLISH'),IFCLABEL('TEMPORARY'),IFCLABEL('OTHER'),IFCLABEL('NOTKNOWN'),IFCLABEL('UNSET')),$);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('3ttXxqmlz62BlxjzEPpqvH',$,'UserDefinedStatus','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -0,0 +1,23 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||
FILE_NAME('','2023-09-28T23:00:50',(),(),'IfcOpenShell v0.7.0-fbd8ea1ed','IfcOpenShell v0.7.0-fbd8ea1ed','');
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPROPERTYSETTEMPLATE('1TLQmgUKf0I8Zi07bm9Jqn',$,'ePSet_ProjectedCRS','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#2,#3,#4,#5,#6,#7));
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('2cH53PtVT3v88uf_uGFblG',$,'Name','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('3JsReA6vXDqR7aNC$haRYW',$,'Description','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('0u0uEsT5HFtuX5GvozO9ba',$,'GeodeticDatum','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
|
||||
#5=IFCSIMPLEPROPERTYTEMPLATE('1JgEX9Fsf0$ggxYltdkqiY',$,'VerticalDatum','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
|
||||
#6=IFCSIMPLEPROPERTYTEMPLATE('3ahTZjYrD0MAOo5IH6QuGH',$,'MapProjection','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
|
||||
#7=IFCSIMPLEPROPERTYTEMPLATE('3hqJR2ghD0GAti7q4sXKj7',$,'MapZone','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
|
||||
#8=IFCPROPERTYSETTEMPLATE('1jGHTycOj0vA8xHfRl4TNs',$,'ePSet_MapConversion','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#9,#10,#11,#12,#13,#15));
|
||||
#9=IFCSIMPLEPROPERTYTEMPLATE('0nxsULXo17JRrAvNteYtNR',$,'Eastings','',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
|
||||
#10=IFCSIMPLEPROPERTYTEMPLATE('26RfaGJs5CqemN0Xin12XL',$,'Northings','',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
|
||||
#11=IFCSIMPLEPROPERTYTEMPLATE('0ziCn3nZv3m850VEUwWH42',$,'OrthogonalHeight','',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
|
||||
#12=IFCSIMPLEPROPERTYTEMPLATE('3d4BOt4In8WR119ms1SaAe',$,'XAxisAbscissa','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#13=IFCSIMPLEPROPERTYTEMPLATE('1Q4Voqj2j84RQRI8l67m28',$,'XAxisOrdinate','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#15=IFCSIMPLEPROPERTYTEMPLATE('0O1Hc0$XnDNOG_jPBmbW5r',$,'Scale','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -82,7 +82,7 @@ class IfcExporter:
|
||||
)
|
||||
self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||
self.file.wrapped_data.header.file_name.originating_system = "{} {}".format(
|
||||
self.get_application_name(), self.get_application_version()
|
||||
self.get_application_name(), tool.Blender.get_blenderbim_version()
|
||||
)
|
||||
|
||||
def sync_all_objects(self):
|
||||
@@ -93,6 +93,8 @@ class IfcExporter:
|
||||
try:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
continue
|
||||
if obj.library:
|
||||
continue
|
||||
tool.Collector.sync(obj)
|
||||
result = self.sync_object_placement(obj)
|
||||
if result:
|
||||
@@ -162,7 +164,7 @@ class IfcExporter:
|
||||
return "BlenderBIM"
|
||||
|
||||
def get_application_version(self):
|
||||
return ".".join(
|
||||
version = ".".join(
|
||||
[
|
||||
str(x)
|
||||
for x in [
|
||||
@@ -172,6 +174,9 @@ class IfcExporter:
|
||||
][0]
|
||||
]
|
||||
)
|
||||
if blenderbim.bim.last_commit_hash != "8888888":
|
||||
version += f"-{blenderbim.bim.last_commit_hash[:7]}"
|
||||
return version
|
||||
|
||||
|
||||
class IfcExportSettings:
|
||||
|
||||
@@ -21,12 +21,14 @@ import bpy
|
||||
import json
|
||||
import addon_utils
|
||||
import ifcopenshell.api.owner.settings
|
||||
import blenderbim.bim
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.owner as core_owner
|
||||
from bpy.app.handlers import persistent
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.owner.prop import get_user_person, get_user_organisation
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.model.workspace import LIST_OF_TOOLS, TOOLS_TO_CLASSES_MAP
|
||||
from mathutils import Vector
|
||||
from math import cos, degrees
|
||||
|
||||
@@ -35,24 +37,6 @@ cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
global_subscription_owner = object()
|
||||
|
||||
|
||||
def mode_callback(obj, data):
|
||||
objects = bpy.context.selected_objects
|
||||
if bpy.context.active_object:
|
||||
objects += [bpy.context.active_object]
|
||||
for obj in objects:
|
||||
if (
|
||||
obj.mode != "EDIT"
|
||||
or not obj.data
|
||||
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
|
||||
or not obj.BIMObjectProperties.ifc_definition_id
|
||||
):
|
||||
continue
|
||||
if obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
tool.Ifc.edit(obj)
|
||||
elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"):
|
||||
tool.Ifc.edit(obj)
|
||||
|
||||
|
||||
def name_callback(obj, data):
|
||||
try:
|
||||
obj.name
|
||||
@@ -73,17 +57,31 @@ def name_callback(obj, data):
|
||||
refresh_ui_data()
|
||||
return
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id or "/" not in obj.name:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
return
|
||||
|
||||
if obj.BIMObjectProperties.is_renaming:
|
||||
obj.BIMObjectProperties.is_renaming = False
|
||||
return
|
||||
|
||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if "/" in obj.name:
|
||||
object_name = obj.name
|
||||
element_name = obj.name.split("/", 1)[1]
|
||||
else:
|
||||
element_name = obj.name
|
||||
object_name = element.is_a() + f"/{element_name}"
|
||||
obj.name = object_name # NOTE: doesn't trigger infinite recursion
|
||||
|
||||
if element.is_a("IfcGridAxis"):
|
||||
element.AxisTag = obj.name.split("/")[1]
|
||||
element.AxisTag = object_name.split("/")[1]
|
||||
refresh_ui_data()
|
||||
|
||||
if not element.is_a("IfcRoot"):
|
||||
return
|
||||
element.Name = element_name
|
||||
if obj.BIMObjectProperties.collection:
|
||||
obj.BIMObjectProperties.collection.name = obj.name
|
||||
element.Name = "/".join(obj.name.split("/")[1:])
|
||||
obj.BIMObjectProperties.collection.name = object_name
|
||||
refresh_ui_data()
|
||||
|
||||
|
||||
@@ -106,7 +104,7 @@ def update_bim_tool_props():
|
||||
return
|
||||
mode = bpy.context.mode
|
||||
current_tool = bpy.context.workspace.tools.from_space_view3d_mode(mode)
|
||||
if not current_tool or current_tool.idname != "bim.bim_tool":
|
||||
if not current_tool or current_tool.idname not in LIST_OF_TOOLS:
|
||||
return
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
@@ -114,6 +112,16 @@ def update_bim_tool_props():
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return
|
||||
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
if element.is_a("IfcElementType") or element.is_a("IfcElement"):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
if element_type:
|
||||
is_bim_tool = current_tool.idname == "bim.bim_tool"
|
||||
if is_bim_tool:
|
||||
props.ifc_class = element_type.is_a()
|
||||
if is_bim_tool or TOOLS_TO_CLASSES_MAP.get(current_tool.idname) == element_type.is_a():
|
||||
props.relating_type_id = str(element_type.id())
|
||||
extrusion = tool.Model.get_extrusion(representation)
|
||||
if not extrusion:
|
||||
return
|
||||
@@ -124,7 +132,6 @@ def update_bim_tool_props():
|
||||
return x_angle
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
|
||||
@@ -175,30 +182,20 @@ def refresh_ui_data():
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
|
||||
tool.Ifc.get().clear_cache()
|
||||
|
||||
|
||||
def purge_module_data():
|
||||
from blenderbim.bim import modules
|
||||
|
||||
refresh_ui_data()
|
||||
for name, value in modules.items():
|
||||
try:
|
||||
getattr(getattr(getattr(ifcopenshell.api, name), "data"), "Data").purge()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# TODO: deprecate prop purge functions and refactor into data classes.
|
||||
try:
|
||||
getattr(value, "prop").purge()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
|
||||
tool.Ifc.get().clear_cache()
|
||||
|
||||
|
||||
@persistent
|
||||
def loadIfcStore(scene):
|
||||
IfcStore.purge()
|
||||
purge_module_data()
|
||||
refresh_ui_data()
|
||||
if not IfcStore.get_file():
|
||||
return
|
||||
IfcStore.get_schema()
|
||||
@@ -210,7 +207,7 @@ def undo_post(scene):
|
||||
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||
IfcStore.undo(until_key=bpy.context.scene.BIMProperties.last_transaction)
|
||||
purge_module_data()
|
||||
refresh_ui_data()
|
||||
tool.Ifc.rebuild_element_maps()
|
||||
|
||||
|
||||
@@ -219,7 +216,7 @@ def redo_post(scene):
|
||||
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||
IfcStore.redo(until_key=bpy.context.scene.BIMProperties.last_transaction)
|
||||
purge_module_data()
|
||||
refresh_ui_data()
|
||||
tool.Ifc.rebuild_element_maps()
|
||||
|
||||
|
||||
@@ -257,74 +254,58 @@ def viewport_shading_changed_callback(area):
|
||||
bpy.context.scene.BIMStylesProperties.active_style_type = "External"
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_scene"):
|
||||
@classmethod
|
||||
def poll_check_blender_tab(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
class Override_SCENE_PT_scene(bpy.types.SCENE_PT_scene):
|
||||
bl_idname = "SCENE_PT_scene_override"
|
||||
|
||||
def get_override_scene_panel(panel_name):
|
||||
original_panel = getattr(bpy.types, panel_name)
|
||||
|
||||
override_panel = type(f"Override_{panel_name}", (original_panel,), {})
|
||||
override_panel.bl_idname = f"{panel_name}_override"
|
||||
|
||||
# some blender panel depend on other and register_class will throw an error
|
||||
# if we won't override their `bl_parent_id`
|
||||
bl_parent_id = getattr(override_panel, "bl_parent_id", None)
|
||||
if bl_parent_id is not None:
|
||||
override_panel.bl_parent_id = f"{bl_parent_id}_override"
|
||||
|
||||
# override poll method
|
||||
poll = getattr(override_panel, "poll", None)
|
||||
if poll is None:
|
||||
override_panel.poll = poll_check_blender_tab
|
||||
else:
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
def wrapped_poll(cls, context):
|
||||
return super(override_panel, cls).poll(context) and poll_check_blender_tab.__func__(cls, context)
|
||||
|
||||
override_panel.poll = wrapped_poll
|
||||
|
||||
return override_panel
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_unit"):
|
||||
|
||||
class Override_SCENE_PT_unit(bpy.types.SCENE_PT_unit):
|
||||
bl_idname = "SCENE_PT_unit_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_physics"):
|
||||
|
||||
class Override_SCENE_PT_physics(bpy.types.SCENE_PT_physics):
|
||||
bl_idname = "SCENE_PT_physics_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_rigid_body_world"):
|
||||
|
||||
class Override_SCENE_PT_rigid_body_world(bpy.types.SCENE_PT_rigid_body_world):
|
||||
bl_idname = "SCENE_PT_rigid_body_world_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_audio"):
|
||||
|
||||
class Override_SCENE_PT_audio(bpy.types.SCENE_PT_audio):
|
||||
bl_idname = "SCENE_PT_audio_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_keying_sets"):
|
||||
|
||||
class Override_SCENE_PT_keying_sets(bpy.types.SCENE_PT_keying_sets):
|
||||
bl_idname = "SCENE_PT_keying_sets_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
|
||||
|
||||
if getattr(bpy.types, "SCENE_PT_custom_props"):
|
||||
|
||||
class Override_SCENE_PT_custom_props(bpy.types.SCENE_PT_custom_props):
|
||||
bl_idname = "SCENE_PT_custom_props_override"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "BLENDER")
|
||||
# TODO: possibly override scene panels from other addons too?
|
||||
# list can be updated from
|
||||
# https://projects.blender.org/blender/blender/src/branch/main/scripts/startup/bl_ui/properties_scene.py#L421
|
||||
OVERRIDE_SCENE_PANELS = (
|
||||
"SCENE_PT_scene",
|
||||
"SCENE_PT_unit",
|
||||
"SCENE_PT_physics",
|
||||
"SCENE_PT_rigid_body_world",
|
||||
"SCENE_PT_rigid_body_world_settings",
|
||||
"SCENE_PT_rigid_body_cache",
|
||||
"SCENE_PT_rigid_body_field_weights",
|
||||
"SCENE_PT_audio",
|
||||
"SCENE_PT_keying_sets",
|
||||
"SCENE_PT_custom_props",
|
||||
# after SCENE_PT_keying_sets
|
||||
"SCENE_PT_keying_set_paths",
|
||||
"SCENE_PT_keyframing_settings",
|
||||
)
|
||||
if bpy.app.version >= (4, 0):
|
||||
OVERRIDE_SCENE_PANELS += ("SCENE_PT_simulation",)
|
||||
|
||||
|
||||
@persistent
|
||||
@@ -353,6 +334,9 @@ def load_post(scene):
|
||||
ifcopenshell.api.owner.settings.get_application = get_application
|
||||
AuthoringData.type_thumbnails = {}
|
||||
|
||||
if not bpy.context.preferences.addons["blenderbim"].preferences.should_setup_toolbar:
|
||||
tool.Blender.unregister_toolbar()
|
||||
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
|
||||
if "BIM" in bpy.data.workspaces:
|
||||
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||
@@ -362,26 +346,12 @@ def load_post(scene):
|
||||
# To improve usability for new users, we hijack the scene properties
|
||||
# tab. We override default scene properties panels with our own poll
|
||||
# to hide them unless the user has chosen to view Blender properties.
|
||||
for panel in [
|
||||
"SCENE_PT_scene",
|
||||
"SCENE_PT_unit",
|
||||
"SCENE_PT_physics",
|
||||
"SCENE_PT_rigid_body_world",
|
||||
"SCENE_PT_audio",
|
||||
"SCENE_PT_keying_sets",
|
||||
"SCENE_PT_custom_props",
|
||||
]:
|
||||
if getattr(bpy.types, panel, None):
|
||||
try:
|
||||
bpy.utils.register_class(globals()[f"Override_{panel}"])
|
||||
bpy.utils.unregister_class(getattr(bpy.types, panel))
|
||||
except:
|
||||
pass
|
||||
|
||||
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
|
||||
for screen in bpy.data.screens:
|
||||
if len(screen.BIMAreaProperties) == 20:
|
||||
continue
|
||||
screen.BIMAreaProperties.clear()
|
||||
for i in range(20): # 20 is an arbitrary value of split areas
|
||||
screen.BIMAreaProperties.add()
|
||||
for panel in OVERRIDE_SCENE_PANELS:
|
||||
if panel in blenderbim.bim.overridden_scene_panels:
|
||||
continue
|
||||
override_panel = get_override_scene_panel(panel)
|
||||
original_panel = getattr(bpy.types, panel)
|
||||
bpy.utils.register_class(override_panel)
|
||||
bpy.utils.unregister_class(original_panel)
|
||||
blenderbim.bim.overridden_scene_panels[panel] = (original_panel, override_panel)
|
||||
tool.Blender.setup_tabs()
|
||||
|
||||
@@ -87,8 +87,15 @@ def import_attributes(ifc_class, props, data, callback=None):
|
||||
|
||||
# A more elegant attribute importer signature, intended to supersede import_attributes
|
||||
def import_attributes2(element, props, callback=None):
|
||||
for attribute in element.wrapped_data.declaration().as_entity().all_attributes():
|
||||
import_attribute(attribute, props, element.get_info(), callback=callback)
|
||||
if isinstance(element, str):
|
||||
attributes = tool.Ifc.schema().declaration_by_name(element).as_entity().all_attributes()
|
||||
info = {a.name(): None for a in attributes}
|
||||
info["type"] = element
|
||||
else:
|
||||
attributes = element.wrapped_data.declaration().as_entity().all_attributes()
|
||||
info = element.get_info()
|
||||
for attribute in attributes:
|
||||
import_attribute(attribute, props, info, callback=callback)
|
||||
|
||||
|
||||
def import_attribute(attribute, props, data, callback=None):
|
||||
@@ -238,7 +245,9 @@ def get_obj_ifc_definition_id(context, obj, obj_type):
|
||||
].ifc_definition_id
|
||||
elif obj_type == "WorkSchedule":
|
||||
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
|
||||
|
||||
elif obj_type == "Group":
|
||||
prop = context.scene.BIMGroupProperties
|
||||
return prop.groups[prop.active_group_index].ifc_definition_id
|
||||
|
||||
# hack to close popup
|
||||
# https://blender.stackexchange.com/a/202576/130742
|
||||
@@ -260,12 +269,7 @@ def convert_property_group_from_si(property_group, skip_props=()):
|
||||
for prop_name in property_group.bl_rna.properties.keys():
|
||||
if prop_name in skip_props:
|
||||
continue
|
||||
prop_bl_rna = property_group.bl_rna.properties[prop_name]
|
||||
if prop_bl_rna.array_length > 0:
|
||||
prop_value = prop_bl_rna.default_array
|
||||
else:
|
||||
prop_value = prop_bl_rna.default
|
||||
|
||||
prop_value = tool.Blender.get_blender_prop_default_value(property_group, prop_name)
|
||||
if type(prop_value) is float:
|
||||
prop_value = prop_value * conversion_k
|
||||
elif type(prop_value) is bpy.types.bpy_prop_array:
|
||||
@@ -273,6 +277,85 @@ def convert_property_group_from_si(property_group, skip_props=()):
|
||||
setattr(property_group, prop_name, prop_value)
|
||||
|
||||
|
||||
def draw_filter(layout, filter_groups, data, module):
|
||||
if not data.is_loaded:
|
||||
data.load()
|
||||
|
||||
sprops = bpy.context.scene.BIMSearchProperties
|
||||
|
||||
if tool.Ifc.get():
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{len(data.data['saved_searches'])} Saved Searches")
|
||||
|
||||
if data.data["saved_searches"]:
|
||||
row.operator("bim.load_search", text="", icon="IMPORT").module = module
|
||||
row.operator("bim.save_search", text="", icon="EXPORT").module = module
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD").module = module
|
||||
row.operator("bim.edit_filter_query", text="", icon="FILTER").module = module
|
||||
|
||||
for i, filter_group in enumerate(filter_groups):
|
||||
box = layout.box()
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(sprops, "facet", text="")
|
||||
op = row.operator("bim.add_filter", text="Add Filter", icon="ADD")
|
||||
op.type = sprops.facet
|
||||
op.index = i
|
||||
op.module = module
|
||||
|
||||
for j, ifc_filter in enumerate(filter_group.filters):
|
||||
if ifc_filter.type == "entity":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_3D")
|
||||
elif ifc_filter.type == "attribute":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="COPY_ID")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "type":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="FILE_VOLUME")
|
||||
elif ifc_filter.type == "material":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="MATERIAL")
|
||||
elif ifc_filter.type == "property":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "pset", text="", icon="PROPERTIES")
|
||||
row.prop(ifc_filter, "name", text="")
|
||||
row.prop(ifc_filter, "comparison", text="")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "classification":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="OUTLINER")
|
||||
elif ifc_filter.type == "location":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="PACKAGE")
|
||||
elif ifc_filter.type == "group":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="OUTLINER_COLLECTION")
|
||||
elif ifc_filter.type == "query":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
|
||||
row.prop(ifc_filter, "value", text="")
|
||||
elif ifc_filter.type == "instance":
|
||||
row = box.row(align=True)
|
||||
row.prop(ifc_filter, "value", text="", icon="GRIP")
|
||||
op = row.operator("bim.select_filter_elements", text="", icon="EYEDROPPER")
|
||||
op.group_index = i
|
||||
op.index = j
|
||||
op.module = module
|
||||
op = row.operator("bim.remove_filter", text="", icon="X")
|
||||
op.group_index = i
|
||||
op.index = j
|
||||
op.module = module
|
||||
|
||||
row = box.row()
|
||||
op = row.operator("bim.remove_filter_group", icon="X")
|
||||
op.index = i
|
||||
op.module = module
|
||||
|
||||
|
||||
# TODO this should move into ifcopenshell.util
|
||||
|
||||
|
||||
|
||||
@@ -163,8 +163,12 @@ class IfcStore:
|
||||
if not IfcStore.get_file():
|
||||
return
|
||||
for obj in bpy.data.objects:
|
||||
if obj.library:
|
||||
continue
|
||||
IfcStore.relink_object(obj)
|
||||
for obj in bpy.data.materials:
|
||||
if obj.library:
|
||||
continue
|
||||
IfcStore.relink_object(obj)
|
||||
|
||||
@staticmethod
|
||||
@@ -220,8 +224,9 @@ class IfcStore:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
|
||||
elif isinstance(obj, bpy.types.Object):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
|
||||
blenderbim.bim.handler.subscribe_to(obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback)
|
||||
blenderbim.bim.handler.subscribe_to(
|
||||
obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback
|
||||
)
|
||||
|
||||
if IfcStore.history:
|
||||
data = {"id": element.id(), "guid": getattr(element, "GlobalId", None), "obj": obj.name}
|
||||
@@ -247,8 +252,9 @@ class IfcStore:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "diffuse_color", blenderbim.bim.handler.color_callback)
|
||||
elif isinstance(obj, bpy.types.Object):
|
||||
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
|
||||
blenderbim.bim.handler.subscribe_to(obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback)
|
||||
blenderbim.bim.handler.subscribe_to(
|
||||
obj, "active_material_index", blenderbim.bim.handler.active_material_index_callback
|
||||
)
|
||||
# TODO Listeners are not re-registered. Does this cause nasty problems to debug later on?
|
||||
# TODO We're handling id_map and guid_map, but what about edited_objs? This might cause big problems.
|
||||
|
||||
@@ -321,7 +327,7 @@ class IfcStore:
|
||||
IfcStore.begin_transaction(operator)
|
||||
if tool.Ifc.get():
|
||||
tool.Ifc.get().begin_transaction()
|
||||
if BrickStore.graph:
|
||||
if BrickStore.graph is not None: # `if BrickStore.graph` by itself takes ages.
|
||||
BrickStore.begin_transaction()
|
||||
# This empty transaction ensures that each operator has at least one transaction
|
||||
IfcStore.add_transaction_operation(operator, rollback=lambda data: True, commit=lambda data: True)
|
||||
@@ -339,7 +345,7 @@ class IfcStore:
|
||||
IfcStore.add_transaction_operation(
|
||||
operator, rollback=lambda d: tool.Ifc.get().undo(), commit=lambda d: tool.Ifc.get().redo()
|
||||
)
|
||||
if BrickStore.graph:
|
||||
if BrickStore.graph is not None: # `if BrickStore.graph` by itself takes ages.
|
||||
BrickStore.end_transaction()
|
||||
IfcStore.end_transaction(operator)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,14 +20,14 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.BIM_OT_assign_object,
|
||||
operator.BIM_OT_unassign_object,
|
||||
operator.BIM_OT_enable_editing_aggregate,
|
||||
operator.BIM_OT_disable_editing_aggregate,
|
||||
operator.BIM_OT_add_aggregate,
|
||||
operator.BIM_OT_select_parts,
|
||||
operator.BIM_OT_select_aggregate,
|
||||
operator.BIM_OT_add_part_to_object,
|
||||
operator.BIM_OT_aggregate_assign_object,
|
||||
operator.BIM_OT_disable_editing_aggregate,
|
||||
operator.BIM_OT_enable_editing_aggregate,
|
||||
operator.BIM_OT_select_aggregate,
|
||||
operator.BIM_OT_select_parts,
|
||||
operator.BIM_OT_aggregate_unassign_object,
|
||||
prop.BIMObjectAggregateProperties,
|
||||
ui.BIM_PT_aggregate,
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ class AggregateData:
|
||||
"has_relating_object": cls.has_relating_object(),
|
||||
"relating_object_label": cls.get_relating_object_label(),
|
||||
"has_related_objects": cls.has_related_objects(),
|
||||
"related_objects_amount": cls.get_related_objects_amount(),
|
||||
"total_parts": cls.total_parts(),
|
||||
"ifc_class": cls.ifc_class(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
@@ -59,7 +59,7 @@ class AggregateData:
|
||||
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
|
||||
@classmethod
|
||||
def get_related_objects_amount(cls):
|
||||
def total_parts(cls):
|
||||
parts = cls.get_related_objects()
|
||||
return len(parts) if parts else 0
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ class Operator:
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_assign_object(bpy.types.Operator, Operator):
|
||||
class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
|
||||
"""Create aggregation relationship between two ifc elements"""
|
||||
|
||||
bl_idname = "bim.assign_object"
|
||||
bl_label = "Assign Object"
|
||||
bl_idname = "bim.aggregate_assign_object"
|
||||
bl_label = "Assign Object To Aggregation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
relating_object: bpy.props.IntProperty()
|
||||
|
||||
@@ -67,11 +67,11 @@ class BIM_OT_assign_object(bpy.types.Operator, Operator):
|
||||
self.report({"ERROR"}, f" Cannot aggregate {obj.name} to {relating_obj.name}")
|
||||
|
||||
|
||||
class BIM_OT_unassign_object(bpy.types.Operator, Operator):
|
||||
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
|
||||
"""Remove aggregation relationship between two ifc elements"""
|
||||
|
||||
bl_idname = "bim.unassign_object"
|
||||
bl_label = "Unassign Object"
|
||||
bl_idname = "bim.aggregate_unassign_object"
|
||||
bl_label = "Unassign Object From Aggregation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -164,6 +164,7 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def create_aggregate(self, context, ifc_class):
|
||||
aggregate = bpy.data.objects.new("Assembly", None)
|
||||
aggregate.location = context.scene.cursor.location
|
||||
bpy.ops.bim.assign_class(obj=aggregate.name, ifc_class=ifc_class)
|
||||
return aggregate
|
||||
|
||||
@@ -202,6 +203,7 @@ class BIM_OT_select_aggregate(bpy.types.Operator):
|
||||
aggregate_obj = tool.Ifc.get_object(aggregate)
|
||||
if aggregate_obj in context.selectable_objects:
|
||||
aggregate_obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = aggregate_obj
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class BIM_PT_aggregate(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "relating_object", text="")
|
||||
if props.relating_object:
|
||||
op = row.operator("bim.assign_object", icon="CHECKMARK", text="")
|
||||
op = row.operator("bim.aggregate_assign_object", icon="CHECKMARK", text="")
|
||||
op.relating_object = props.relating_object.BIMObjectProperties.ifc_definition_id
|
||||
row.operator("bim.disable_editing_aggregate", icon="CANCEL", text="")
|
||||
else:
|
||||
@@ -65,15 +65,21 @@ class BIM_PT_aggregate(Panel):
|
||||
op.obj = context.active_object.name
|
||||
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||
op = row.operator("bim.unassign_object", icon="X", text="")
|
||||
op = row.operator("bim.aggregate_unassign_object", icon="X", text="")
|
||||
else:
|
||||
row.label(text="No Aggregate", icon="TRIA_UP")
|
||||
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
parts = AggregateData.data["related_objects_amount"]
|
||||
row.label(text=f"{parts or 'No'} Part{'s' if parts > 1 else ''}", icon="TRIA_DOWN")
|
||||
total_parts = AggregateData.data["total_parts"]
|
||||
if total_parts == 0:
|
||||
row.label(text="No Parts", icon="TRIA_DOWN")
|
||||
elif total_parts == 1:
|
||||
row.label(text="1 Part", icon="TRIA_DOWN")
|
||||
else:
|
||||
row.label(text=f"{total_parts} Parts", icon="TRIA_DOWN")
|
||||
|
||||
if AggregateData.data["has_related_objects"]:
|
||||
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.obj = context.active_object.name
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import bcf
|
||||
import bcf.v2.bcfxml
|
||||
|
||||
@@ -23,12 +25,31 @@ import bcf.v2.bcfxml
|
||||
class BcfStore:
|
||||
bcfxml = None
|
||||
|
||||
@staticmethod
|
||||
def get_bcfxml():
|
||||
if not BcfStore.bcfxml:
|
||||
BcfStore.bcfxml = bcf.v2.bcfxml.BcfXml()
|
||||
return BcfStore.bcfxml
|
||||
@classmethod
|
||||
def get_bcfxml(cls):
|
||||
if not cls.bcfxml:
|
||||
bcf_filepath = bpy.context.scene.BCFProperties.bcf_file
|
||||
if not os.path.isabs(bcf_filepath):
|
||||
bcf_filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), bcf_filepath))
|
||||
if bcf_filepath:
|
||||
try:
|
||||
cls.bcfxml = bcf.bcfxml.load(bcf_filepath)
|
||||
except:
|
||||
# there will be a plenty of "Permission denied" errors
|
||||
# as many poll() methods will try to access the bcfxml simultaneously
|
||||
# the first time it's loaded
|
||||
pass
|
||||
return cls.bcfxml
|
||||
|
||||
@classmethod
|
||||
def set(cls, bcfxml):
|
||||
def set(cls, bcfxml, filepath):
|
||||
cls.bcfxml = bcfxml
|
||||
bpy.context.scene.BCFProperties.bcf_file = filepath
|
||||
|
||||
@classmethod
|
||||
def set_by_filepath(cls, filepath):
|
||||
cls.set(None, filepath)
|
||||
|
||||
@classmethod
|
||||
def unload_bcfxml(cls):
|
||||
cls.set(None, "")
|
||||
|
||||
@@ -42,11 +42,9 @@ class NewBcfProject(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BCFProperties.is_loaded = False
|
||||
bcfxml = bcf.v2.bcfxml.BcfXml.create_new("New Project")
|
||||
bcfstore.BcfStore.set(bcfxml)
|
||||
bcfstore.BcfStore.set(bcfxml, "")
|
||||
bpy.ops.bim.load_bcf_project()
|
||||
context.scene.BCFProperties.is_loaded = True
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -59,11 +57,10 @@ class LoadBcfProject(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
if self.filepath:
|
||||
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
|
||||
bcfstore.BcfStore.set_by_filepath(self.filepath)
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
context.scene.BCFProperties.name = bcfxml.project.name
|
||||
bpy.ops.bim.load_bcf_topics()
|
||||
context.scene.BCFProperties.is_loaded = True
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -77,8 +74,7 @@ class UnloadBcfProject(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
bcfstore.BcfStore.set(None)
|
||||
context.scene.BCFProperties.is_loaded = False
|
||||
bcfstore.BcfStore.unload_bcfxml()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -267,6 +263,7 @@ class SaveBcfProject(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
bcfxml.save(self.filepath)
|
||||
bcfstore.BcfStore.set(bcfxml, self.filepath)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -964,7 +961,8 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
context_override = {}
|
||||
context_override["object"] = context_override["active_object"] = objs[0]
|
||||
context_override["selected_objects"] = context_override["selected_editable_objects"] = objs
|
||||
bpy.ops.object.hide_view_set(context_override, unselected=True)
|
||||
with context.temp_override(**context_override):
|
||||
bpy.ops.object.hide_view_set(unselected=True)
|
||||
context.area.type = old
|
||||
|
||||
if viewpoint.visualization_info.components.view_setup_hints:
|
||||
@@ -978,14 +976,16 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
self.hide_spaces(context)
|
||||
self.set_openings_visibility(False, context)
|
||||
|
||||
# set selection at the end not to conflict with .hide_spaces
|
||||
self.set_selection(viewpoint)
|
||||
self.set_colours(viewpoint)
|
||||
|
||||
def hide_spaces(self, context):
|
||||
old = context.area.type
|
||||
context.area.type = "VIEW_3D"
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bpy.ops.object.select_pattern(pattern="IfcSpace/*")
|
||||
bpy.ops.object.hide_view_set({})
|
||||
bpy.ops.object.hide_view_set()
|
||||
context.area.type = old
|
||||
|
||||
def set_openings_visibility(self, is_visible, context):
|
||||
|
||||
@@ -41,37 +41,37 @@ def purge():
|
||||
|
||||
|
||||
def updateBcfReferenceLink(self, context):
|
||||
if context.scene.BCFProperties.is_loaded:
|
||||
if bcfstore.BcfStore.get_bcfxml():
|
||||
bpy.ops.bim.edit_bcf_reference_links()
|
||||
|
||||
|
||||
def updateBcfLabel(self, context):
|
||||
if context.scene.BCFProperties.is_loaded:
|
||||
if bcfstore.BcfStore.get_bcfxml():
|
||||
bpy.ops.bim.edit_bcf_labels()
|
||||
|
||||
|
||||
def updateBcfProjectName(self, context):
|
||||
if self.is_loaded:
|
||||
if bcfstore.BcfStore.get_bcfxml():
|
||||
bpy.ops.bim.edit_bcf_project_name()
|
||||
|
||||
|
||||
def updateBcfAuthor(self, context):
|
||||
if self.is_loaded:
|
||||
if bcfstore.BcfStore.get_bcfxml():
|
||||
bpy.ops.bim.edit_bcf_author()
|
||||
|
||||
|
||||
def updateBcfTopicName(self, context):
|
||||
if context.scene.BCFProperties.is_loaded:
|
||||
if bcfstore.BcfStore.get_bcfxml():
|
||||
bpy.ops.bim.edit_bcf_topic_name()
|
||||
|
||||
|
||||
def updateBcfTopicIsEditable(self, context):
|
||||
if context.scene.BCFProperties.is_loaded and not self.is_editable:
|
||||
if bcfstore.BcfStore.get_bcfxml() and not self.is_editable:
|
||||
bpy.ops.bim.edit_bcf_topic()
|
||||
|
||||
|
||||
def updateBcfCommentIsEditable(self, context):
|
||||
if context.scene.BCFProperties.is_loaded and not self.is_editable:
|
||||
if bcfstore.BcfStore.get_bcfxml() and not self.is_editable:
|
||||
bpy.ops.bim.edit_bcf_comment(comment_guid=self.name)
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ class BcfTopic(PropertyGroup):
|
||||
|
||||
|
||||
class BCFProperties(PropertyGroup):
|
||||
is_loaded: BoolProperty(name="Is Loaded", default=False)
|
||||
bcf_file: StringProperty(name="BCF File")
|
||||
comment_text_width: IntProperty(name="Comment Text Width", default=40)
|
||||
name: StringProperty(default="", name="Project Name", update=updateBcfProjectName)
|
||||
author: StringProperty(default="john@doe.com", name="Author Email", update=updateBcfAuthor)
|
||||
|
||||
@@ -39,8 +39,7 @@ class BIM_PT_bcf(Panel):
|
||||
scene = context.scene
|
||||
props = scene.BCFProperties
|
||||
|
||||
|
||||
if not props.is_loaded:
|
||||
if not bcfstore.BcfStore.get_bcfxml():
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.new_bcf_project", text="New Project")
|
||||
row.operator("bim.load_bcf_project", text="Load Project")
|
||||
|
||||
@@ -62,14 +62,14 @@ class BoundaryDecorator:
|
||||
gpu.state.point_size_set(6)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind() # required to be able to change uniforms of the shader
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
selected_vertices = []
|
||||
selected_edges = []
|
||||
|
||||
@@ -22,6 +22,7 @@ import logging
|
||||
import shapely
|
||||
import mathutils
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.shape
|
||||
@@ -30,7 +31,7 @@ import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.import_ifc as import_ifc
|
||||
from math import pi, inf
|
||||
from math import pi, inf, degrees, acos, radians
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
@@ -63,7 +64,8 @@ def disable_editing_boundary_geometry(context):
|
||||
|
||||
|
||||
class Loader:
|
||||
def __init__(self):
|
||||
def __init__(self, operator=None):
|
||||
self.operator = operator
|
||||
self.ifc_file = None
|
||||
self.logger = None
|
||||
self.ifc_importer = None
|
||||
@@ -85,14 +87,33 @@ class Loader:
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
except RuntimeError:
|
||||
# Fallback solution for invalid geometry provided by Revit. (InnerBoundaries cuting OuterBoundary)
|
||||
print(f"Failed to create mesh from IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid")
|
||||
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
|
||||
self.operator.report(
|
||||
{"WARNING"},
|
||||
(
|
||||
f"IfcRelSpaceBoundary {boundary.id()} mesh creation failed. Geometry might be invalid. Using fallback solution"
|
||||
),
|
||||
)
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(self.fallback_settings, surface.OuterBoundary)
|
||||
except RuntimeError:
|
||||
self.operator.report(
|
||||
{"WARNING"},
|
||||
f"Skipping IfcRelSpaceBoundary {boundary.id()}. Fallback solution failed. Geometry might be invalid",
|
||||
)
|
||||
return None
|
||||
mesh = bpy.data.meshes.new(str(surface.id()))
|
||||
bm = bmesh.new()
|
||||
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
|
||||
bm.faces.new(verts)
|
||||
for inner_boundary in surface.InnerBoundaries:
|
||||
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
|
||||
try:
|
||||
shape = ifcopenshell.geom.create_shape(self.fallback_settings, inner_boundary)
|
||||
except RuntimeError:
|
||||
self.operator.report(
|
||||
{"WARNING"},
|
||||
f"Skipping an inner boundary for IfcRelSpaceBoundary with ID {boundary.id()}. Geometry might be invalid",
|
||||
)
|
||||
return None
|
||||
verts = [bm.verts.new(shape.verts[i : i + 3]) for i in range(0, len(shape.verts), 3)]
|
||||
for i in range(len(verts) - 1):
|
||||
bm.edges.new(verts[i : i + 2])
|
||||
@@ -103,7 +124,7 @@ class Loader:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
matrix = mathutils.Matrix(
|
||||
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
|
||||
)
|
||||
)
|
||||
matrix.translation *= unit_scale
|
||||
mesh.transform(matrix)
|
||||
return mesh
|
||||
@@ -126,7 +147,15 @@ class Loader:
|
||||
self.ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||
self.ifc_importer.file = self.ifc_file
|
||||
|
||||
def load_boundary(self, boundary, blender_space):
|
||||
def load_boundary(self, boundary, blender_space=None):
|
||||
if not blender_space:
|
||||
if not boundary.RelatingSpace:
|
||||
self.operator.report(
|
||||
{"WARNING"},
|
||||
f"Skipping IfcRelSpaceBoundary {boundary.id()} which have no relating space. Invalid boundary.",
|
||||
)
|
||||
return
|
||||
blender_space = tool.Ifc.get_object(boundary.RelatingSpace)
|
||||
obj = tool.Ifc.get_object(boundary)
|
||||
if obj:
|
||||
return obj
|
||||
@@ -145,9 +174,9 @@ class LoadProjectSpaceBoundaries(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
loader = Loader()
|
||||
loader = Loader(self)
|
||||
for rel in tool.Ifc.get().by_type("IfcRelSpaceBoundary"):
|
||||
loader.load_boundary(rel, tool.Ifc.get_object(rel.RelatingSpace))
|
||||
loader.load_boundary(rel)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -158,7 +187,7 @@ class LoadBoundary(bpy.types.Operator):
|
||||
boundary_id: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
loader = Loader()
|
||||
loader = Loader(self)
|
||||
for obj in context.visible_objects:
|
||||
obj.select_set(False)
|
||||
obj = loader.load_boundary(tool.Ifc.get().by_id(self.boundary_id), context.active_object)
|
||||
@@ -173,7 +202,7 @@ class LoadSpaceBoundaries(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
loader = Loader()
|
||||
loader = Loader(self)
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
for rel in element.BoundedBy or []:
|
||||
loader.load_boundary(rel, context.active_object)
|
||||
@@ -421,7 +450,7 @@ class EnableEditingBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
ProfileDecorator.install(context, exit_edit_mode_callback=lambda: disable_editing_boundary_geometry(context))
|
||||
if not bpy.app.background:
|
||||
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
|
||||
tool.Blender.set_viewport_tool("bim.cad_tool")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -459,7 +488,7 @@ class EditBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_surface)
|
||||
|
||||
old_mesh = obj.data
|
||||
loader = Loader()
|
||||
loader = Loader(self)
|
||||
obj.data = loader.create_mesh(element)
|
||||
tool.Geometry.delete_data(old_mesh)
|
||||
|
||||
@@ -484,7 +513,7 @@ class ShowBoundaries(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = bpy.context.scene.BIMBoundaryProperties
|
||||
loader = Loader()
|
||||
loader = Loader(self)
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not getattr(element, "BoundedBy", None):
|
||||
@@ -538,6 +567,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
related_building_element = None
|
||||
relating_space_obj = None
|
||||
related_building_element_obj = None
|
||||
parent_boundaries = []
|
||||
|
||||
objs = context.selected_objects
|
||||
if len(objs) == 2:
|
||||
@@ -552,8 +582,19 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
related_building_element = element
|
||||
related_building_element_obj = obj
|
||||
parent_boundary = self.create_element_boundary(
|
||||
context, relating_space, relating_space_obj, related_building_element, related_building_element_obj
|
||||
)
|
||||
if parent_boundary:
|
||||
parent_boundaries.append(parent_boundary)
|
||||
elif len(objs) == 1:
|
||||
# New prototype, old code not yet removed. Still testing.
|
||||
space = tool.Ifc.get_entity(objs[0])
|
||||
if space.is_a("IfcSpace"):
|
||||
self.auto_generate_boundaries(space, objs[0])
|
||||
elif len(objs) == 1:
|
||||
# Optionally the user may select just the space, and the building element shall be auto-detected
|
||||
# TODO : refactor to be able to generate all boundaries for selected space automatically or with an option
|
||||
|
||||
def msg(self, context):
|
||||
self.layout.label(text="NO ACTIVE STOREY")
|
||||
@@ -563,8 +604,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
relating_space = element
|
||||
relating_space_obj = objs[0]
|
||||
|
||||
target = bpy.context.scene.cursor.location
|
||||
|
||||
collection = context.view_layer.active_layer_collection.collection
|
||||
collection_obj = bpy.data.objects.get(collection.name)
|
||||
if not collection_obj:
|
||||
@@ -576,19 +615,258 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return
|
||||
|
||||
for subelement in ifcopenshell.util.element.get_decomposition(spatial_element):
|
||||
if not (subelement.is_a("IfcWall") or subelement.is_a("IfcSlab")):
|
||||
if not (
|
||||
subelement.is_a("IfcWall") or subelement.is_a("IfcSlab") or subelement.is_a("IfcVirtualElement")
|
||||
):
|
||||
continue
|
||||
obj = tool.Ifc.get_object(subelement)
|
||||
if obj:
|
||||
raycast = obj.closest_point_on_mesh(obj.matrix_world.inverted() @ target, distance=0.1)
|
||||
if raycast[0]:
|
||||
related_building_element = subelement
|
||||
related_building_element_obj = obj
|
||||
break
|
||||
related_building_element = subelement
|
||||
related_building_element_obj = obj
|
||||
parent_boundary = self.create_element_boundary(
|
||||
context,
|
||||
relating_space,
|
||||
relating_space_obj,
|
||||
related_building_element,
|
||||
related_building_element_obj,
|
||||
)
|
||||
if parent_boundary:
|
||||
parent_boundaries.append(parent_boundary)
|
||||
|
||||
bpy.ops.bim.show_boundaries()
|
||||
for parent_boundary in parent_boundaries:
|
||||
obj = tool.Ifc.get_object(parent_boundary)
|
||||
obj.select_set(True)
|
||||
|
||||
def auto_generate_boundaries(self, space, space_obj):
|
||||
# Identify all potential building elements
|
||||
# TODO: don't select everything, use AABB culling in Blender
|
||||
building_elements = (
|
||||
tool.Ifc.get().by_type("IfcWall")
|
||||
+ tool.Ifc.get().by_type("IfcSlab")
|
||||
+ tool.Ifc.get().by_type("IfcVirtualElement")
|
||||
)
|
||||
|
||||
# Don't generate boundaries of building elements that we've already got bounaries for.
|
||||
for boundary in space.BoundedBy:
|
||||
if boundary.RelatedBuildingElement in building_elements:
|
||||
building_elements.remove(boundary.RelatedBuildingElement)
|
||||
|
||||
# Create tree of gross shapes of all potential related building elements
|
||||
include = building_elements + [space]
|
||||
tree = ifcopenshell.geom.tree()
|
||||
shapes = {}
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
iterator = ifcopenshell.geom.iterator(settings, tool.Ifc.get(), multiprocessing.cpu_count(), include=include)
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
tree.add_element(iterator.get_native())
|
||||
shape = iterator.get()
|
||||
shapes[shape.id] = shape
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
# Spatially query all potential boundary elements via a 100mm extension of the space
|
||||
building_elements = [e for e in tree.select(space, extend=0.1) if e != space]
|
||||
|
||||
if not building_elements:
|
||||
return
|
||||
|
||||
# Create a dissolved bmesh for the space
|
||||
space_bm = bmesh.new()
|
||||
space_bm.from_mesh(space_obj.data)
|
||||
bmesh.ops.dissolve_limit(space_bm, angle_limit=pi * 2 / 360, verts=space_bm.verts, edges=space_bm.edges)
|
||||
|
||||
# Create dissolved bmeshes for all boundary elements
|
||||
building_element_bms = {}
|
||||
for building_element in building_elements:
|
||||
bm = bmesh.new()
|
||||
shape = shapes[building_element.id()]
|
||||
|
||||
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
|
||||
for vert in verts:
|
||||
bm.verts.new(Vector(vert))
|
||||
bm.verts.ensure_lookup_table()
|
||||
|
||||
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
|
||||
for face in faces:
|
||||
bm.faces.new([bm.verts[i] for i in face])
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.faces.ensure_lookup_table()
|
||||
bm.normal_update() # Needed so that dissolve_limit will work.
|
||||
bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts[:], edges=bm.edges[:])
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.faces.ensure_lookup_table()
|
||||
building_element_bms[building_element.id()] = bm
|
||||
|
||||
# Compare space faces and building element faces to see if they relate to one another
|
||||
for space_face in space_bm.faces:
|
||||
space_face_normal = space_obj.matrix_world.to_3x3() @ space_face.normal
|
||||
space_face_vert = space_obj.matrix_world @ space_face.verts[0].co
|
||||
for building_element in building_elements:
|
||||
for face in building_element_bms[building_element.id()].faces:
|
||||
building_obj = tool.Ifc.get_object(building_element)
|
||||
face_normal = building_obj.matrix_world.to_3x3() @ face.normal
|
||||
angle = degrees(acos(max(min(space_face_normal.dot(face_normal), 1), -1)))
|
||||
if tool.Cad.is_x(angle, 180, tolerance=2):
|
||||
pass # Faces need to be parallel and have opposite normals to be related.
|
||||
elif building_element.is_a("IfcVirtualElement") and tool.Cad.is_x(angle, 0, tolerance=2):
|
||||
pass # Virtual elements only need to be parallel to be related, since they are planes.
|
||||
else:
|
||||
continue
|
||||
|
||||
# Both faces should be close to one another. Say within 50mm.
|
||||
space_vert = building_obj.matrix_world.inverted() @ space_face_vert
|
||||
dist = mathutils.geometry.distance_point_to_plane(space_vert, face.verts[0].co, face.normal)
|
||||
if abs(dist) > 0.05:
|
||||
continue
|
||||
|
||||
# Project the building element face onto the space face
|
||||
space_face_verts = [v.co.copy() for v in space_face.verts]
|
||||
space_face_matrix = self.get_face_matrix(*[v.copy() for v in space_face_verts[0:3]])
|
||||
space_face_matrix_i = space_face_matrix.inverted()
|
||||
|
||||
space_face_polygon = shapely.Polygon(
|
||||
[tuple((space_face_matrix_i @ v).xy) for v in space_face_verts]
|
||||
)
|
||||
|
||||
space_matrix_world_i = space_obj.matrix_world.inverted()
|
||||
face_verts = [space_matrix_world_i @ building_obj.matrix_world @ v.co.copy() for v in face.verts]
|
||||
face_polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in face_verts])
|
||||
|
||||
gross_boundary_polygon = space_face_polygon.intersection(face_polygon)
|
||||
|
||||
if type(gross_boundary_polygon) == shapely.GeometryCollection:
|
||||
for geom in gross_boundary_polygon.geoms:
|
||||
if type(geom) == shapely.Polygon:
|
||||
gross_boundary_polygon = geom
|
||||
break
|
||||
|
||||
if (
|
||||
not (isinstance(gross_boundary_polygon, shapely.Polygon) and gross_boundary_polygon.is_valid)
|
||||
or gross_boundary_polygon.is_empty
|
||||
):
|
||||
continue
|
||||
|
||||
# The gross boundary polygon may not be a true gross boundary since it
|
||||
# may have openings already removed, such as in IFC4 Reference View. So
|
||||
# we cheat by using the exterior boundary to mean "gross".
|
||||
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
|
||||
|
||||
parent_boundary = tool.Ifc.run(
|
||||
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
|
||||
)
|
||||
if building_element.is_a("IfcVirtualElement"):
|
||||
parent_boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
|
||||
else:
|
||||
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
|
||||
parent_boundary.InternalOrExternalBoundary = "NOTDEFINED"
|
||||
if building_element.is_a("IfcWall"):
|
||||
is_external = ifcopenshell.util.element.get_pset(
|
||||
building_element, "Pset_WallCommon", "IsExternal"
|
||||
)
|
||||
if is_external is True:
|
||||
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
|
||||
elif is_external is False:
|
||||
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
|
||||
elif building_element.is_a("IfcSlab"):
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(building_element)
|
||||
if predefined_type == "BASESLAB":
|
||||
parent_boundary.InternalOrExternalBoundary = "EXTERNAL_EARTH"
|
||||
else:
|
||||
is_external = ifcopenshell.util.element.get_pset(
|
||||
building_element, "Pset_SlabCommon", "IsExternal"
|
||||
)
|
||||
if is_external is True:
|
||||
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
|
||||
elif is_external is False:
|
||||
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
|
||||
parent_boundary.RelatingSpace = space
|
||||
parent_boundary.RelatedBuildingElement = building_element
|
||||
parent_boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
|
||||
exterior_boundary_polygon, space_face_matrix
|
||||
)
|
||||
self.set_boundary_name(parent_boundary)
|
||||
|
||||
for rel in getattr(building_element, "HasOpenings", []):
|
||||
opening = rel.RelatedOpeningElement
|
||||
filling = opening.HasFillings[0].RelatedBuildingElement if opening.HasFillings else None
|
||||
|
||||
# Create shape of opening as a dissolved BMesh
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.STRICT_TOLERANCE, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, opening)
|
||||
m = shape.transformation.matrix.data
|
||||
mat = Matrix(
|
||||
(
|
||||
[m[0], m[3], m[6], m[9]],
|
||||
[m[1], m[4], m[7], m[10]],
|
||||
[m[2], m[5], m[8], m[11]],
|
||||
[0, 0, 0, 1],
|
||||
)
|
||||
)
|
||||
opening_bm = bmesh.new()
|
||||
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
|
||||
for vert in verts:
|
||||
opening_bm.verts.new(Vector(vert))
|
||||
opening_bm.verts.ensure_lookup_table()
|
||||
faces = ifcopenshell.util.shape.get_faces(shape.geometry)
|
||||
for face in faces:
|
||||
opening_bm.faces.new([opening_bm.verts[i] for i in face])
|
||||
opening_bm.verts.ensure_lookup_table()
|
||||
opening_bm.faces.ensure_lookup_table()
|
||||
opening_bm.normal_update() # Needed so that dissolve_limit will work.
|
||||
bmesh.ops.dissolve_limit(
|
||||
opening_bm, angle_limit=radians(1), verts=opening_bm.verts[:], edges=opening_bm.edges[:]
|
||||
)
|
||||
opening_bm.verts.ensure_lookup_table()
|
||||
opening_bm.faces.ensure_lookup_table()
|
||||
|
||||
# Get relevant faces of BMesh that can turn into boundaries
|
||||
opening_polygons = []
|
||||
for opening_face in opening_bm.faces:
|
||||
opening_face_normal = mat.to_3x3() @ opening_face.normal
|
||||
angle = degrees(acos(max(min(opening_face_normal.dot(face_normal), 1), -1)))
|
||||
if not tool.Cad.is_x(angle, 180, tolerance=2):
|
||||
continue # Any non-parallel faces are not relevant
|
||||
opening_face_verts = [space_matrix_world_i @ mat @ v.co.copy() for v in opening_face.verts]
|
||||
polygon = shapely.Polygon([tuple((space_face_matrix_i @ v).xy) for v in opening_face_verts])
|
||||
opening_polygons.append(polygon)
|
||||
|
||||
# Merge them all into a single opening polygon for our boundary
|
||||
opening_polygon = shapely.ops.unary_union(opening_polygons)
|
||||
|
||||
# Only openings that are projected onto our exterior boundary are relevant.
|
||||
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
|
||||
continue
|
||||
|
||||
boundary = tool.Ifc.run(
|
||||
"root.create_entity", ifc_class=bpy.context.scene.BIMModelProperties.boundary_class
|
||||
)
|
||||
boundary.RelatingSpace = space
|
||||
boundary.RelatedBuildingElement = filling or opening
|
||||
boundary.ConnectionGeometry = self.create_connection_geometry_from_polygon(
|
||||
opening_polygon, space_face_matrix
|
||||
)
|
||||
if filling:
|
||||
boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
|
||||
else:
|
||||
boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
|
||||
boundary.InternalOrExternalBoundary = parent_boundary.InternalOrExternalBoundary
|
||||
if boundary.is_a() != "IfcRelSpaceBoundary":
|
||||
boundary.ParentBoundary = parent_boundary
|
||||
self.set_boundary_name(boundary)
|
||||
|
||||
def create_element_boundary(
|
||||
self, context, relating_space, relating_space_obj, related_building_element, related_building_element_obj
|
||||
):
|
||||
if not relating_space or not related_building_element:
|
||||
return
|
||||
|
||||
# Find which face on space should be bounded to related building element
|
||||
# TODO: Handle round wall where multiple faces need to be bound to the same related building element
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(relating_space_obj.data)
|
||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi * 2 / 360, verts=bm.verts, edges=bm.edges)
|
||||
@@ -609,8 +887,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not target_face:
|
||||
return
|
||||
|
||||
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
|
||||
|
||||
# Is this right? Or should I use loop?
|
||||
target_face_verts = [v.co.copy() for v in target_face.verts]
|
||||
target_face_matrix = self.get_face_matrix(*[v.copy() for v in target_face_verts[0:3]])
|
||||
@@ -630,6 +906,18 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
gross_boundary_polygon = geom
|
||||
break
|
||||
|
||||
if (
|
||||
not (isinstance(gross_boundary_polygon, shapely.Polygon) and gross_boundary_polygon.is_valid)
|
||||
or gross_boundary_polygon.is_empty
|
||||
):
|
||||
return
|
||||
|
||||
parent_boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
|
||||
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
|
||||
# Set to EXTERNAL by default and turn later to internal if there is a corresponding boundary relating to an
|
||||
# internal space
|
||||
parent_boundary.InternalOrExternalBoundary = "EXTERNAL"
|
||||
|
||||
# The gross boundary polygon may not be a true gross boundary since it
|
||||
# may have openings already removed, such as in IFC4 Reference View. So
|
||||
# we cheat by using the exterior boundary to mean "gross". Later, we
|
||||
@@ -637,19 +925,25 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# space.
|
||||
exterior_boundary_polygon = shapely.Polygon(gross_boundary_polygon.exterior.coords)
|
||||
|
||||
net_boundary_polygon = shapely.Polygon(gross_boundary_polygon)
|
||||
|
||||
inner_boundaries = []
|
||||
|
||||
for rel in getattr(related_building_element, "HasOpenings", []):
|
||||
opening = rel.RelatedOpeningElement
|
||||
filling = None
|
||||
if opening.HasFillings:
|
||||
filling = opening.HasFillings[0].RelatedBuildingElement
|
||||
if not opening.HasFillings:
|
||||
continue
|
||||
# TODO: HasFilling is a zero to many relationship. How to handle many ?
|
||||
filling = opening.HasFillings[0].RelatedBuildingElement
|
||||
|
||||
opening_polygon = self.get_flattened_polygon(opening, relating_space_obj, target_face_matrix_i)
|
||||
|
||||
net_boundary_polygon = net_boundary_polygon.difference(opening_polygon)
|
||||
# An inner boundary is supposed to overlap its parent boundary according to IFC4 documentation
|
||||
# so we extend our exterior boundary with all opening which has a filling
|
||||
# Also see Implementation aggreement SB 1.1 for IFC 2x3 TC1 Space Boundary Addon View
|
||||
# https://standards.buildingsmart.org/MVD/RELEASE/IFC2x3/TC1/SB1_1/IFC%20Space%20Boundary%20Implementation%20Agreement%20Addendum%202010-03-22.pdf
|
||||
unionised_object = exterior_boundary_polygon.union(opening_polygon)
|
||||
if isinstance(unionised_object, shapely.Polygon):
|
||||
exterior_boundary_polygon = unionised_object
|
||||
|
||||
# Only openings that are projected onto our exterior boundary are relevant.
|
||||
if opening_polygon.intersection(exterior_boundary_polygon).area == 0:
|
||||
continue
|
||||
@@ -657,24 +951,25 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
connection_geometry = self.create_connection_geometry_from_polygon(opening_polygon, target_face_matrix)
|
||||
boundary = tool.Ifc.run("root.create_entity", ifc_class=context.scene.BIMModelProperties.boundary_class)
|
||||
boundary.RelatingSpace = relating_space
|
||||
boundary.RelatedBuildingElement = filling or related_building_element
|
||||
boundary.RelatedBuildingElement = filling
|
||||
boundary.ConnectionGeometry = connection_geometry
|
||||
boundary.PhysicalOrVirtualBoundary = "PHYSICAL" if filling else "VIRTUAL"
|
||||
boundary.InternalOrExternalBoundary = "INTERNAL"
|
||||
|
||||
if related_building_element.is_a("IfcVirtualElement"):
|
||||
boundary.PhysicalOrVirtualBoundary = "VIRTUAL"
|
||||
else:
|
||||
boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
|
||||
boundary.InternalOrExternalBoundary = parent_boundary.InternalOrExternalBoundary
|
||||
self.set_boundary_name(boundary)
|
||||
if boundary.is_a("IfcRelSpaceBoundary2ndLevel"):
|
||||
boundary.ParentBoundary = parent_boundary
|
||||
|
||||
connection_geometry = self.create_connection_geometry_from_polygon(net_boundary_polygon, target_face_matrix)
|
||||
connection_geometry = self.create_connection_geometry_from_polygon(
|
||||
exterior_boundary_polygon, target_face_matrix
|
||||
)
|
||||
parent_boundary.RelatingSpace = relating_space
|
||||
parent_boundary.RelatedBuildingElement = related_building_element
|
||||
parent_boundary.ConnectionGeometry = connection_geometry
|
||||
parent_boundary.PhysicalOrVirtualBoundary = "PHYSICAL"
|
||||
parent_boundary.InternalOrExternalBoundary = "INTERNAL"
|
||||
|
||||
bpy.ops.bim.show_boundaries()
|
||||
obj = tool.Ifc.get_object(parent_boundary)
|
||||
obj.select_set(True)
|
||||
self.set_boundary_name(parent_boundary)
|
||||
return parent_boundary
|
||||
|
||||
def get_face_matrix(self, p1, p2, p3):
|
||||
edge1 = p2 - p1
|
||||
@@ -729,11 +1024,13 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Model.unit_scale = self.unit_scale
|
||||
|
||||
surface = tool.Ifc.get().createIfcCurveBoundedPlane()
|
||||
surface.BasisSurface = tool.Ifc.get().createIfcPlane(tool.Ifc.get().createIfcAxis2Placement3D(
|
||||
tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
|
||||
))
|
||||
surface.BasisSurface = tool.Ifc.get().createIfcPlane(
|
||||
tool.Ifc.get().createIfcAxis2Placement3D(
|
||||
tool.Ifc.get().createIfcCartesianPoint([o / self.unit_scale for o in p1]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]),
|
||||
tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]),
|
||||
)
|
||||
)
|
||||
|
||||
if tool.Ifc.get().schema != "IFC2X3":
|
||||
points = [tool.Model.convert_si_to_unit(list(co)) for co in polygon.exterior.coords]
|
||||
@@ -746,9 +1043,24 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
point_list = tool.Ifc.get().createIfcCartesianPointList2D(points)
|
||||
inner_boundaries.append(tool.Ifc.get().createIfcIndexedPolyCurve(point_list, None, False))
|
||||
else:
|
||||
pass # TODO
|
||||
pass # TODO
|
||||
|
||||
surface.OuterBoundary = outer_boundary
|
||||
surface.InnerBoundaries = inner_boundaries
|
||||
|
||||
return surface
|
||||
|
||||
def set_boundary_name(self, boundary):
|
||||
"""
|
||||
By convention 1stLevel and 2ndLevel boundary have specific name and description
|
||||
See https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRelSpaceBoundary.htm
|
||||
Warning: IfcRelSpaceBoundary2ndLevel is a subclass of IfcRelSpaceBoundary1stLevel test order matter
|
||||
"""
|
||||
if boundary.is_a("IfcRelSpaceBoundary2ndLevel"):
|
||||
boundary.Name = "2ndLevel"
|
||||
if boundary.CorrespondingBoundary:
|
||||
boundary.Description = "2a"
|
||||
else:
|
||||
boundary.Description = "2b"
|
||||
elif boundary.is_a("IfcRelSpaceBoundary1stLevel"):
|
||||
boundary.Name = "1stLevel"
|
||||
|
||||
@@ -31,7 +31,7 @@ class BIM_PT_SceneBoundaries(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -35,13 +35,16 @@ classes = (
|
||||
operator.ViewBrickItem,
|
||||
operator.SerializeBrick,
|
||||
operator.AddBrickNamespace,
|
||||
operator.SetBrickListRoot,
|
||||
operator.RemoveBrickRelation,
|
||||
prop.Brick,
|
||||
prop.BIMBrickProperties,
|
||||
ui.BIM_PT_brickschema,
|
||||
ui.BIM_PT_ifc_brickschema_references,
|
||||
ui.BIM_PT_brickschema_project_info,
|
||||
ui.BIM_PT_brickschema_namespaces,
|
||||
ui.BIM_PT_brickschema_create_entity,
|
||||
ui.BIM_PT_brickschema_viewport,
|
||||
ui.BIM_UL_bricks,
|
||||
ui.BIM_PT_ifc_brickschema_references,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class BrickschemaData:
|
||||
|
||||
@classmethod
|
||||
def get_is_loaded(cls):
|
||||
return BrickStore.graph is not None
|
||||
return BrickStore.graph is not None # `if BrickStore.graph` by itself takes ages.
|
||||
|
||||
@classmethod
|
||||
def active_relations(cls):
|
||||
@@ -67,45 +67,63 @@ class BrickschemaData:
|
||||
PREFIX brick: <https://brickschema.org/schema/Brick#>
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||
SELECT DISTINCT ?predicate ?object ?sp ?sv WHERE {
|
||||
<{uri}> ?predicate ?object .
|
||||
OPTIONAL {
|
||||
{ ?predicate rdfs:range brick:TimeseriesReference . }
|
||||
UNION
|
||||
{ ?predicate a brick:EntityProperty . }
|
||||
?object ?sp ?sv }
|
||||
SELECT DISTINCT ?predicate ?object ?label ?sp ?sv WHERE {
|
||||
<{uri}> ?predicate ?object .
|
||||
OPTIONAL {
|
||||
?object rdfs:label ?label .
|
||||
}
|
||||
OPTIONAL {
|
||||
{ ?predicate rdfs:range brick:TimeseriesReference . }
|
||||
UNION
|
||||
{ ?predicate a brick:EntityProperty . }
|
||||
?object ?sp ?sv .
|
||||
}
|
||||
}
|
||||
GROUP BY ?object
|
||||
""".replace(
|
||||
"{uri}", uri
|
||||
)
|
||||
)
|
||||
for row in query:
|
||||
predicate = row.get("predicate")
|
||||
predicate_name = predicate.toPython().split("#")[-1]
|
||||
object = row.get("object")
|
||||
object_name = object.toPython().split("#")[-1]
|
||||
predicate_uri = row.get("predicate")
|
||||
predicate_name = predicate_uri.toPython().split("#")[-1]
|
||||
object_uri = row.get("object")
|
||||
object_name = row.get("label")
|
||||
if not object_name:
|
||||
if isinstance(object_uri, BNode):
|
||||
object_name = "[]"
|
||||
else:
|
||||
try:
|
||||
object_name = object_uri.toPython().split("#")[-1]
|
||||
except:
|
||||
object_name = str(object_uri)
|
||||
results.append(
|
||||
{
|
||||
"predicate": predicate,
|
||||
"predicate_uri": predicate_uri,
|
||||
"predicate_name": predicate_name,
|
||||
"object": object,
|
||||
"object_uri": object_uri,
|
||||
"object_name": object_name,
|
||||
"is_uri": isinstance(object, URIRef),
|
||||
"object_uri": object.toPython(),
|
||||
"is_globalid": predicate == "globalID",
|
||||
"is_uri": isinstance(object_uri, URIRef),
|
||||
"is_globalid": predicate_name == "ifcGlobalID",
|
||||
}
|
||||
)
|
||||
# if isinstance(row.get("object"), BNode):
|
||||
# for s, p, o in BrickStore.graph.triples((object, None, None)):
|
||||
# results.append(
|
||||
# {
|
||||
# "predicate": predicate + ":" + p.toPython().split("#")[-1],
|
||||
# "object": o.toPython().split("#")[-1],
|
||||
# "is_uri": isinstance(o, URIRef),
|
||||
# "object_uri": o.toPython(),
|
||||
# "is_globalid": p.toPython().split("#")[-1] == "globalID",
|
||||
# }
|
||||
# )
|
||||
if isinstance(object_uri, BNode):
|
||||
for subject2, predicate2, object2 in BrickStore.graph.triples((object_uri, None, None)):
|
||||
predicate2_name = predicate2.toPython().split("#")[-1]
|
||||
try:
|
||||
object2_name = object2.toPython().split("#")[-1]
|
||||
except:
|
||||
object2_name = str(object2)
|
||||
results.append(
|
||||
{
|
||||
"predicate_uri": None,
|
||||
"predicate_name": predicate_name + ":" + predicate2_name,
|
||||
"object_uri": object2,
|
||||
"object_name": object2_name,
|
||||
"is_uri": isinstance(object2, URIRef),
|
||||
"is_globalid": predicate2_name == "ifcGlobalID",
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
@@ -131,7 +149,7 @@ class BrickschemaReferencesData:
|
||||
for library in ifc.by_type("IfcLibraryInformation"):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||
elif ".ttl" in library.Location:
|
||||
elif library.Location and ".ttl" in library.Location:
|
||||
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||
return results
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class LoadBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.load_brick_project"
|
||||
bl_label = "Load Brickschema Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Load in a Brick project from a file"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ttl", options={"HIDDEN"})
|
||||
|
||||
@@ -56,6 +57,7 @@ class ViewBrickClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.view_brick_class"
|
||||
bl_label = "View Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Inspect the subclasses of this class"
|
||||
brick_class: bpy.props.StringProperty(name="Brick Class")
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
@@ -67,17 +69,22 @@ class ViewBrickItem(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.view_brick_item"
|
||||
bl_label = "View Brick Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Inspect this entity in the viewer"
|
||||
item: bpy.props.StringProperty(name="Brick Item")
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
core.view_brick_item(tool.Brick, item=self.item, split_screen=self.split_screen)
|
||||
try:
|
||||
core.view_brick_item(tool.Brick, item=self.item, split_screen=self.split_screen)
|
||||
except:
|
||||
self.report({'ERROR'}, f'Could not find {self.item}')
|
||||
|
||||
|
||||
class RewindBrickClass(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.rewind_brick_class"
|
||||
bl_label = "Rewind Brick Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Go back to the previous list view"
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
@@ -88,6 +95,7 @@ class CloseBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.close_brick_project"
|
||||
bl_label = "Close Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Close the Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.close_brick_project(tool.Brick)
|
||||
@@ -97,6 +105,7 @@ class ConvertBrickProject(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.convert_brick_project"
|
||||
bl_label = "Convert Brick Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create an Ifc library for this Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_brick_project(tool.Ifc, tool.Brick)
|
||||
@@ -106,9 +115,18 @@ class AssignBrickReference(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.assign_brick_reference"
|
||||
bl_label = "Assign Brick Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Assign the selected Ifc entity to the selected Brick entity"
|
||||
|
||||
def _execute(self, context):
|
||||
if not context.active_object:
|
||||
self.report({'ERROR'}, f'No Ifc selected')
|
||||
return
|
||||
props = context.scene.BIMBrickProperties
|
||||
try:
|
||||
props.bricks[props.active_brick_index]
|
||||
except:
|
||||
self.report({'ERROR'}, f'No Brick selected')
|
||||
return
|
||||
core.assign_brick_reference(
|
||||
tool.Ifc,
|
||||
tool.Brick,
|
||||
@@ -122,19 +140,17 @@ class AddBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick"
|
||||
bl_label = "Add Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create the Brick entity"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
library = None
|
||||
if props.libraries:
|
||||
library = tool.Ifc.get().by_id(int(props.libraries))
|
||||
core.add_brick(
|
||||
tool.Ifc,
|
||||
tool.Brick,
|
||||
element=tool.Ifc.get_entity(context.active_object) if context.selected_objects else None,
|
||||
namespace=props.namespace,
|
||||
brick_class=props.brick_entity_class,
|
||||
library=library,
|
||||
library=tool.Ifc.get().by_id(int(props.libraries)) if props.libraries else None,
|
||||
label=props.new_brick_label,
|
||||
)
|
||||
|
||||
@@ -143,6 +159,7 @@ class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick_relation"
|
||||
bl_label = "Add Brick Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create the Brick relationship"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -152,7 +169,7 @@ class AddBrickRelation(bpy.types.Operator, Operator):
|
||||
elif props.split_screen_toggled:
|
||||
object = props.split_screen_bricks[props.split_screen_active_brick_index].uri
|
||||
else:
|
||||
object = props.new_brick_relation_namespace + props.new_brick_relation_object
|
||||
object = props.namespace + props.new_brick_relation_object
|
||||
core.add_brick_relation(
|
||||
tool.Brick,
|
||||
brick_uri=brick.uri,
|
||||
@@ -165,6 +182,7 @@ class ConvertIfcToBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.convert_ifc_to_brick"
|
||||
bl_label = "Convert IFC To Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Convert Ifc entities and relations to Brick entities and relations"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -178,6 +196,7 @@ class NewBrickFile(bpy.types.Operator):
|
||||
bl_idname = "bim.new_brick_file"
|
||||
bl_label = "New Brick File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create a Brick project from scratch"
|
||||
|
||||
def execute(self, context):
|
||||
IfcStore.begin_transaction(self)
|
||||
@@ -210,16 +229,17 @@ class RefreshBrickViewer(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.refresh_brick_viewer"
|
||||
bl_label = "Refresh Brick Viewer"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
bl_description = "Refresh the list view"
|
||||
|
||||
def _execute(self, context):
|
||||
core.refresh_brick_viewer(tool.Brick, split_screen=self.split_screen)
|
||||
core.refresh_brick_viewer(tool.Brick)
|
||||
|
||||
|
||||
class RemoveBrick(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_brick"
|
||||
bl_label = "Remove Brick"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete this entity"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
@@ -262,32 +282,20 @@ class SerializeBrick(bpy.types.Operator):
|
||||
class AddBrickNamespace(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_brick_namespace"
|
||||
bl_label = "Add Brick Namespace"
|
||||
bl_description = "Bind a new namespace to the Brick project"
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBrickProperties
|
||||
alias = props.new_brick_namespace_alias
|
||||
uri = props.new_brick_namespace_uri
|
||||
core.add_namespace(tool.Brick, alias=alias, uri=uri)
|
||||
|
||||
|
||||
class SetBrickListRoot(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.set_brick_list_root"
|
||||
bl_label = "Set Brick View Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
split_screen: bpy.props.BoolProperty(name="Split Screen", default=False, options={"HIDDEN"})
|
||||
|
||||
def _execute(self, context):
|
||||
if self.split_screen:
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
else:
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=self.split_screen)
|
||||
core.add_brick_namespace(tool.Brick, alias=alias, uri=uri)
|
||||
|
||||
|
||||
class RemoveBrickRelation(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.remove_brick_relation"
|
||||
bl_label = "Remove Relation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete this relationship"
|
||||
predicate: bpy.props.StringProperty(name="Relation")
|
||||
object: bpy.props.StringProperty(name="Object")
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
import blenderbim.core.brick as core
|
||||
import blenderbim.tool.brick as tool
|
||||
from blenderbim.tool.brick import BrickStore
|
||||
|
||||
def update_active_brick_index(self, context):
|
||||
@@ -43,12 +45,12 @@ def get_libraries(self, context):
|
||||
|
||||
|
||||
def get_namespaces(self, context):
|
||||
return BrickStore.namespaces
|
||||
return [(uri, f"{alias}: {uri}", "") for alias, uri in BrickStore.namespaces]
|
||||
|
||||
|
||||
def get_brick_entity_classes(self, context):
|
||||
entity = self.brick_entity_create_type
|
||||
return BrickStore.entity_classes[entity]
|
||||
return [(uri, uri.split("#")[-1], "") for uri in BrickStore.entity_classes[entity]]
|
||||
|
||||
|
||||
def get_brick_roots(self, context):
|
||||
@@ -56,13 +58,22 @@ def get_brick_roots(self, context):
|
||||
|
||||
|
||||
def get_brick_relations(self, context):
|
||||
def is_label(relation):
|
||||
return relation["predicate_name"] == "label"
|
||||
if not list(filter(is_label, BrickschemaData.data["active_relations"])):
|
||||
new_relations = BrickStore.relationships.copy()
|
||||
new_relations.append(("http://www.w3.org/2000/01/rdf-schema#label", "label", ""))
|
||||
return new_relations
|
||||
return BrickStore.relationships
|
||||
relations = [(uri, uri.split("#")[-1], "") for uri in BrickStore.relationships]
|
||||
for relation in BrickschemaData.data["active_relations"]:
|
||||
if relation["predicate_name"] == "label":
|
||||
return relations
|
||||
relations.append(("http://www.w3.org/2000/01/rdf-schema#label", "label", ""))
|
||||
return relations
|
||||
|
||||
|
||||
|
||||
def update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=False)
|
||||
|
||||
def split_screen_update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=True)
|
||||
|
||||
|
||||
class Brick(PropertyGroup):
|
||||
@@ -79,10 +90,9 @@ class BIMBrickProperties(PropertyGroup):
|
||||
active_brick_index: IntProperty(name="Active Brick Index", update=update_active_brick_index)
|
||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||
set_list_root_toggled: BoolProperty(name="Set List Root Toggled", default=False)
|
||||
brick_list_root: EnumProperty(name="Brick List Root", items=get_brick_roots)
|
||||
brick_list_root: EnumProperty(name="Brick List Root", items=get_brick_roots, update=update_view)
|
||||
# namespace manager
|
||||
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
||||
brick_settings_toggled: BoolProperty(name="Brick Settings Toggled", default=False)
|
||||
new_brick_namespace_alias: StringProperty(name="New Brick Namespace Alias")
|
||||
new_brick_namespace_uri: StringProperty(name="New Brick Namespace URI")
|
||||
# create brick entity
|
||||
@@ -93,13 +103,12 @@ class BIMBrickProperties(PropertyGroup):
|
||||
brick_create_relations_toggled: BoolProperty(name="Brick Create Relations Toggled", default=False)
|
||||
brick_edit_relations_toggled: BoolProperty(name="Brick Edit Relations Toggled", default=False)
|
||||
new_brick_relation_type: EnumProperty(name="New Brick Relation Type", items=get_brick_relations)
|
||||
new_brick_relation_namespace: EnumProperty(name="New Brick Relation Namespace", items=get_namespaces)
|
||||
new_brick_relation_object: StringProperty(name="New Brick Relation Object")
|
||||
add_relation_failed: BoolProperty(name="Add Relation Failed", default=False)
|
||||
add_brick_relation_failed: BoolProperty(name="Add Relation Failed", default=False)
|
||||
# create relations split screen
|
||||
split_screen_toggled: BoolProperty(name="Split Screen Toggled", default=False)
|
||||
split_screen_bricks: CollectionProperty(name="Split Screen Bricks", type=Brick)
|
||||
split_screen_active_brick_index: IntProperty(name="Split Screen Active Brick Index", update=update_active_brick_index)
|
||||
split_screen_active_brick_class: StringProperty(name="Split Screen Active Brick Class")
|
||||
split_screen_brick_breadcrumbs: CollectionProperty(name="Split Screen Brick Breadcrumbs", type=StrProperty)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots, update=split_screen_update_view)
|
||||
@@ -34,17 +34,36 @@ class BIM_PT_brickschema(Panel):
|
||||
def draw(self, context):
|
||||
if not BrickschemaData.is_loaded:
|
||||
BrickschemaData.load()
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
|
||||
class BIM_PT_brickschema_project_info(Panel):
|
||||
bl_label = "Project Info"
|
||||
bl_idname = "BIM_PT_brickschema_project_info"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
if not BrickschemaData.data["is_loaded"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.new_brick_file", text="Create Project")
|
||||
row.operator("bim.load_brick_project", text="Load Project")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.path:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=BrickStore.path, icon="FILEBROWSER")
|
||||
else:
|
||||
row.label(text="No file", icon="FILEBROWSER")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.last_saved:
|
||||
row.label(text=BrickStore.last_saved, icon="TIME")
|
||||
else:
|
||||
row.label(text="Not saved", icon="TIME")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.serialize_brick", icon="EXPORT", text="Save")
|
||||
@@ -53,134 +72,173 @@ class BIM_PT_brickschema(Panel):
|
||||
op.should_save_as = True
|
||||
row.operator("bim.close_brick_project", text="", icon="CANCEL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_settings_toggled", text="", icon="PREFERENCES")
|
||||
|
||||
if self.props.brick_settings_toggled:
|
||||
box = self.layout.box()
|
||||
row = box.row(align=True)
|
||||
row.label(text="Active Namespace:")
|
||||
|
||||
row = box.row(align=True)
|
||||
prop_with_search(row, self.props, "namespace", text="")
|
||||
class BIM_PT_brickschema_namespaces(Panel):
|
||||
bl_label = "Namespaces"
|
||||
bl_idname = "BIM_PT_brickschema_namespaces"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
row = box.row(align=True)
|
||||
row.label(text="Bind New Namespace:")
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return BrickStore.graph != None
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(data=self.props, property="new_brick_namespace_alias", text="")
|
||||
col = row.column()
|
||||
col.alignment = "CENTER"
|
||||
col.scale_x = 1.1
|
||||
col.label(text=":")
|
||||
row.prop(data=self.props, property="new_brick_namespace_uri", text="")
|
||||
row.operator("bim.add_brick_namespace", text="", icon="ADD")
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Create Entity:")
|
||||
row.label(text="Active Namespace:")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_entity_create_type", text="")
|
||||
prop_with_search(row, self.props, "namespace", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="new_brick_label", text="")
|
||||
prop_with_search(row, self.props, "brick_entity_class", text="")
|
||||
row.operator("bim.add_brick", text="", icon="ADD")
|
||||
# row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||
row.label(text="Bind New Namespace:")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="new_brick_namespace_alias", text="")
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.alignment = "CENTER"
|
||||
col.scale_x = 1.1
|
||||
col.label(text=":")
|
||||
row.prop(data=self.props, property="new_brick_namespace_uri", text="")
|
||||
row.operator("bim.add_brick_namespace", text="", icon="ADD")
|
||||
|
||||
|
||||
class BIM_PT_brickschema_create_entity(Panel):
|
||||
bl_label = "Create Entity"
|
||||
bl_idname = "BIM_PT_brickschema_create_entity"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return BrickStore.graph != None
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
# TO DO: hide this if selected entity already has a reference, or something similar
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(data=self.props, property="brick_entity_create_type", text="Class")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "brick_entity_class", text="Type")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
active = tool.Ifc.get_entity(context.active_object)
|
||||
if active and context.selected_objects:
|
||||
row.label(text="Label:")
|
||||
row.label(text=active.Name if active.Name else "Unnamed")
|
||||
else:
|
||||
row.prop(data=self.props, property="new_brick_label", text="Label")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_brick", text="Create Entity")
|
||||
|
||||
|
||||
class BIM_PT_brickschema_viewport(Panel):
|
||||
bl_label = "Viewport"
|
||||
bl_idname = "BIM_PT_brickschema_viewport"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_brickschema"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return BrickStore.graph != None
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.column().alignment = "RIGHT"
|
||||
row.prop(data=self.props, property="set_list_root_toggled", text="", icon="OUTLINER")
|
||||
row.prop(data=self.props, property="split_screen_toggled", text="", icon="WINDOW")
|
||||
row.operator("bim.refresh_brick_viewer", text="", icon="FILE_REFRESH")
|
||||
|
||||
grid = self.layout.grid_flow(even_columns=True)
|
||||
grid1 = grid.column(align=True)
|
||||
row = grid1.row(align=True)
|
||||
grid_left = grid.column(align=True)
|
||||
row = grid_left.row(align=True)
|
||||
if len(self.props.brick_breadcrumbs):
|
||||
op = row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||
op.split_screen = False
|
||||
row.prop(data=self.props, property="set_list_root_toggled", text="", icon="OUTLINER")
|
||||
row.label(text=self.props.active_brick_class)
|
||||
|
||||
if self.props.set_list_root_toggled:
|
||||
row = grid1.row(align=True)
|
||||
op = row.operator("bim.set_brick_list_root", text="Set View")
|
||||
op.split_screen = False
|
||||
row = grid_left.row(align=True)
|
||||
row.prop(data=self.props, property="brick_list_root", text="")
|
||||
|
||||
row = grid1.row()
|
||||
row = grid_left.row()
|
||||
BIM_UL_bricks.split_screen = False
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "bricks", self.props, "active_brick_index")
|
||||
|
||||
if self.props.split_screen_toggled:
|
||||
grid2 = grid.column(align=True)
|
||||
row = grid2.row(align=True)
|
||||
grid_right = grid.column(align=True)
|
||||
row = grid_right.row(align=True)
|
||||
if len(self.props.split_screen_brick_breadcrumbs):
|
||||
op = row.operator("bim.rewind_brick_class", text="", icon="FRAME_PREV")
|
||||
op.split_screen = True
|
||||
row.label(text=self.props.split_screen_active_brick_class)
|
||||
|
||||
if self.props.set_list_root_toggled:
|
||||
row = grid2.row(align=True)
|
||||
op = row.operator("bim.set_brick_list_root", text="Set View")
|
||||
op.split_screen = True
|
||||
row = grid_right.row(align=True)
|
||||
row.prop(data=self.props, property="split_screen_brick_list_root", text="")
|
||||
|
||||
row = grid2.row()
|
||||
row = grid_right.row()
|
||||
BIM_UL_bricks.split_screen = True
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "split_screen_bricks", self.props, "split_screen_active_brick_index")
|
||||
|
||||
if BrickschemaData.data["active_relations"]:
|
||||
row = self.layout.row(align=True)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
row.column().alignment = "RIGHT"
|
||||
row.prop(data=self.props, property="brick_create_relations_toggled", text="", icon="PLUGIN")
|
||||
row.prop(data=self.props, property="brick_edit_relations_toggled", text="", icon="TOOL_SETTINGS")
|
||||
row.operator("bim.remove_brick", text="", icon="X")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Create Relation:")
|
||||
|
||||
if self.props.brick_create_relations_toggled and self.props.new_brick_relation_type == "http://www.w3.org/2000/01/rdf-schema#label":
|
||||
if self.props.brick_create_relations_toggled and self.props.split_screen_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.prop(data=self.props, property="new_brick_relation_object", text="")
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
elif self.props.brick_create_relations_toggled and self.props.split_screen_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
split_screen_selection = self.props.split_screen_bricks[self.props.split_screen_active_brick_index]
|
||||
if split_screen_selection.total_items:
|
||||
try:
|
||||
split_screen_selection = self.props.split_screen_bricks[self.props.split_screen_active_brick_index]
|
||||
except:
|
||||
split_screen_selection = None
|
||||
if not split_screen_selection or split_screen_selection.total_items:
|
||||
row.label(text="No selection", icon="INFO")
|
||||
else:
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.label(text=split_screen_selection.label if split_screen_selection.label else split_screen_selection.name)
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
elif self.props.brick_create_relations_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "new_brick_relation_namespace", text="")
|
||||
|
||||
elif self.props.brick_create_relations_toggled:
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.prop(data=self.props, property="new_brick_relation_object", text="")
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
|
||||
if self.props.brick_create_relations_toggled and self.props.add_relation_failed:
|
||||
if self.props.brick_create_relations_toggled and self.props.add_brick_relation_failed:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Failed to find this entity!", icon="ERROR")
|
||||
|
||||
|
||||
for relation in BrickschemaData.data["active_relations"]:
|
||||
row = self.layout.row(align=True)
|
||||
split = self.layout.split(factor=0.85, align=True)
|
||||
row = split.row(align=True)
|
||||
row.label(text=relation["predicate_name"])
|
||||
row.separator()
|
||||
row.label(text=relation["object_name"])
|
||||
if self.props.brick_edit_relations_toggled and relation["predicate_name"] != "type":
|
||||
row = split.row(align=True)
|
||||
row.column().alignment = "RIGHT"
|
||||
if self.props.brick_edit_relations_toggled and relation["predicate_uri"] and relation["predicate_name"] != "type":
|
||||
op = row.operator("bim.remove_brick_relation", text="", icon="UNLINKED")
|
||||
op.predicate = relation["predicate"]
|
||||
op.object = relation["object"]
|
||||
if relation["is_uri"] and relation["predicate_name"] != "type":
|
||||
op.predicate = relation["predicate_uri"]
|
||||
op.object = relation["object_uri"]
|
||||
if relation["is_uri"] and relation["object_uri"].toPython().split("#")[-1] != self.props.active_brick_class:
|
||||
op = row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT")
|
||||
op.item = relation["object_uri"]
|
||||
if relation["is_globalid"]:
|
||||
@@ -188,6 +246,23 @@ class BIM_PT_brickschema(Panel):
|
||||
op.global_id = relation["object_name"]
|
||||
|
||||
|
||||
class BIM_UL_bricks(UIList):
|
||||
split_screen = False
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
split = layout.split(factor=0.85, align=True)
|
||||
row = split.row()
|
||||
label = item.label if item.label else item.name
|
||||
if item.total_items:
|
||||
op = row.operator("bim.view_brick_class", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
|
||||
op.brick_class = item.name
|
||||
op.split_screen = self.split_screen
|
||||
row.label(text=label)
|
||||
if item.total_items:
|
||||
row = split.row()
|
||||
row.label(text=str(item.total_items))
|
||||
|
||||
class BIM_PT_ifc_brickschema_references(Panel):
|
||||
bl_label = "Brickschema References"
|
||||
bl_idname = "BIM_PT_ifc_brickschema_references"
|
||||
@@ -211,15 +286,24 @@ class BIM_PT_ifc_brickschema_references(Panel):
|
||||
row.label(text="No Brickschema Project Loaded")
|
||||
return
|
||||
|
||||
if not BrickschemaReferencesData.data["libraries"]:
|
||||
if not BrickschemaReferencesData.data["libraries"] and BrickStore.path:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No IFC Libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
return
|
||||
|
||||
elif not BrickschemaReferencesData.data["libraries"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No IFC Libraries: save the Brick project to create a new library", icon="ERROR")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Libraries must have location pointing to a \".ttl\" file", icon="INFO")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
if BrickStore.path:
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.assign_brick_reference", icon="ADD")
|
||||
@@ -234,19 +318,4 @@ class BIM_PT_ifc_brickschema_references(Panel):
|
||||
row.label(text=reference["identification"], icon="ASSET_MANAGER")
|
||||
row.label(text=reference["name"])
|
||||
row.operator("bim.unassign_library_reference", text="", icon="X").reference = reference["id"]
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
|
||||
|
||||
class BIM_UL_bricks(UIList):
|
||||
split_screen = False
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
label = item.label if item.label else item.name
|
||||
if item.total_items:
|
||||
op = row.operator("bim.view_brick_class", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
|
||||
op.brick_class = item.name
|
||||
op.split_screen = self.split_screen
|
||||
label = label + " (" + str(item.total_items) + ")"
|
||||
row.label(text=label)
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
@@ -0,0 +1,42 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.GetBSDDClassificationProperties,
|
||||
operator.LoadBSDDDomains,
|
||||
operator.SearchBSDDClass,
|
||||
operator.SetActiveBSDDDictionary,
|
||||
prop.BSDDDomain,
|
||||
prop.BSDDClassification,
|
||||
prop.BSDDPset,
|
||||
prop.BIMBSDDProperties,
|
||||
ui.BIM_UL_bsdd_domains,
|
||||
ui.BIM_UL_bsdd_classifications,
|
||||
ui.BIM_PT_bsdd,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMBSDDProperties = bpy.props.PointerProperty(type=prop.BIMBSDDProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMBSDDProperties
|
||||
@@ -0,0 +1,65 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bsdd
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.core import bsdd as core
|
||||
|
||||
|
||||
class LoadBSDDDomains(bpy.types.Operator):
|
||||
bl_idname = "bim.load_bsdd_domains"
|
||||
bl_label = "Load bSDD Dictionaries"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.load_bsdd(bsdd.Client(), tool.Bsdd)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetActiveBSDDDictionary(bpy.types.Operator):
|
||||
bl_idname = "bim.set_active_bsdd_domain"
|
||||
bl_label = "Load bSDD Dictionary"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.StringProperty()
|
||||
uri: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.set_active_bsdd_dictionary(self.name, self.uri, tool.Bsdd)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SearchBSDDClass(bpy.types.Operator):
|
||||
bl_idname = "bim.search_bsdd_classifications"
|
||||
bl_label = "Search bSDD Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
keyword = context.scene.BIMBSDDProperties.keyword
|
||||
core.search_class(keyword, bsdd.Client(), tool.Bsdd)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GetBSDDClassificationProperties(bpy.types.Operator):
|
||||
bl_idname = "bim.get_bsdd_classification_properties"
|
||||
bl_label = "Search bSDD Classifications"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.get_class_properties(bsdd.Client(), tool.Bsdd)
|
||||
return {"FINISHED"}
|
||||
@@ -0,0 +1,67 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from blenderbim.bim.prop import Attribute, StrProperty
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class BSDDDomain(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
uri: StringProperty(name="URI")
|
||||
default_language_code: StringProperty(name="Language")
|
||||
organization_name_owner: StringProperty(name="Organization")
|
||||
status: StringProperty(name="Status")
|
||||
version: StringProperty(name="Version")
|
||||
|
||||
|
||||
class BSDDClassification(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
reference_code: StringProperty(name="Reference Code")
|
||||
description: StringProperty(name="Description")
|
||||
uri: StringProperty(name="Namespace URI")
|
||||
domain_name: StringProperty(name="Domain Name")
|
||||
domain_namespace_uri: StringProperty(name="Domain Namespace URI")
|
||||
|
||||
|
||||
class BSDDPset(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
|
||||
|
||||
class BIMBSDDProperties(PropertyGroup):
|
||||
active_domain: StringProperty(name="Active Domain")
|
||||
active_uri: StringProperty(name="Active URI")
|
||||
domains: CollectionProperty(name="Domains", type=BSDDDomain)
|
||||
active_domain_index: IntProperty(name="Active Domain Index")
|
||||
classifications: CollectionProperty(name="Classifications", type=BSDDClassification)
|
||||
active_classification_index: IntProperty(name="Active Classification Index")
|
||||
keyword: StringProperty(name="Keyword")
|
||||
should_filter_ifc_class: BoolProperty(name="Filter Active IFC Class", default=True)
|
||||
load_preview_domains: BoolProperty(name="Load Preview Domains", default=False)
|
||||
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
|
||||
@@ -0,0 +1,79 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_bsdd(Panel):
|
||||
bl_label = "buildingSMART Data Dictionary"
|
||||
bl_idname = "BIM_PT_bsdd"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "load_preview_domains")
|
||||
if len(props.domains):
|
||||
row.operator("bim.load_bsdd_domains", text="", icon="FILE_REFRESH")
|
||||
|
||||
if props.active_domain:
|
||||
row = self.layout.row()
|
||||
row.label(text="Active: " + props.active_domain, icon="URL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Active bSDD Domain", icon="ERROR")
|
||||
|
||||
if len(props.domains):
|
||||
self.layout.template_list(
|
||||
"BIM_UL_bsdd_domains",
|
||||
"",
|
||||
props,
|
||||
"domains",
|
||||
props,
|
||||
"active_domain_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_bsdd_domains")
|
||||
|
||||
|
||||
class BIM_UL_bsdd_domains(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
if item.status != "Active":
|
||||
row.label(text=f"{item.name} ({item.organization_name_owner}) - {item.status}", icon="ERROR")
|
||||
else:
|
||||
row.label(text=f"{item.name} ({item.organization_name_owner})")
|
||||
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.name = item.name
|
||||
op.uri = item.uri
|
||||
|
||||
|
||||
class BIM_UL_bsdd_classifications(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.reference_code)
|
||||
row.label(text=item.name)
|
||||
@@ -24,6 +24,8 @@ import bpy_extras
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector, Matrix
|
||||
from math import pi, radians, sin, cos, sqrt
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
messages = {
|
||||
"SHARED_VERTEX": "Shared Vertex, no intersection possible",
|
||||
@@ -140,6 +142,9 @@ class CadFillet(bpy.types.Operator):
|
||||
layout.prop(self, prop)
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
@@ -361,6 +366,10 @@ class CadOffset(bpy.types.Operator):
|
||||
# dialog that Mesh Tools has. Sverchok is 2D based, but has a weird side
|
||||
# effect of converting an unclosed loop into a closed loop which is also
|
||||
# not what users expect.
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.distance = self.distance * si_conversion
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
@@ -498,6 +507,8 @@ class CadOffset(bpy.types.Operator):
|
||||
local_direction = (rotation_i @ direction).normalized()
|
||||
normals.append(local_direction)
|
||||
|
||||
|
||||
|
||||
if len(normals) == 2:
|
||||
# https://stackoverflow.com/a/54042831/9627415
|
||||
new_normal = (normals[0].lerp(normals[1], 0.5)).normalized()
|
||||
@@ -531,7 +542,7 @@ class CadOffset(bpy.types.Operator):
|
||||
class AddIfcCircle(bpy.types.Operator):
|
||||
bl_idname = "bim.add_ifccircle"
|
||||
bl_label = "Add IfcCircle"
|
||||
radius: bpy.props.FloatProperty(name="Radius", default=0.1)
|
||||
radius: bpy.props.FloatProperty(name="Radius", default=0.5)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -539,6 +550,9 @@ class AddIfcCircle(bpy.types.Operator):
|
||||
return bool(obj) and obj.type == "MESH"
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
if self.has_selected_existing_circle(context):
|
||||
self.change_radius(context)
|
||||
else:
|
||||
@@ -622,6 +636,9 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
layout.prop(self, prop)
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
if self.has_selected_existing_arc(context):
|
||||
self.change_radius(context)
|
||||
else:
|
||||
@@ -786,8 +803,8 @@ class AlignViewToProfile(bpy.types.Operator):
|
||||
class AddRectangle(bpy.types.Operator):
|
||||
bl_idname = "bim.add_rectangle"
|
||||
bl_label = "Add Rectangle"
|
||||
x: bpy.props.FloatProperty(name="X", default=0.1)
|
||||
y: bpy.props.FloatProperty(name="Y", default=0.1)
|
||||
x: bpy.props.FloatProperty(name="X", default=1)
|
||||
y: bpy.props.FloatProperty(name="Y", default=1)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -795,6 +812,10 @@ class AddRectangle(bpy.types.Operator):
|
||||
return bool(obj) and obj.type == "MESH"
|
||||
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.x = self.x * si_conversion
|
||||
self.y = self.y * si_conversion
|
||||
|
||||
obj = context.active_object
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
cursor = obj.matrix_world.inverted() @ context.scene.cursor.location
|
||||
|
||||
@@ -20,26 +20,33 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddClashSet,
|
||||
operator.AddClashSource,
|
||||
operator.ExecuteIfcClash,
|
||||
operator.ExportClashSets,
|
||||
operator.ImportClashSets,
|
||||
operator.AddClashSet,
|
||||
operator.LoadIfcClashes,
|
||||
operator.SaveIfcClashes,
|
||||
operator.LoadSmartGroupsForActiveClashSet,
|
||||
operator.RemoveClashSet,
|
||||
operator.AddClashSource,
|
||||
operator.RemoveClashSource,
|
||||
operator.SelectClashSource,
|
||||
operator.ExecuteIfcClash,
|
||||
operator.SelectIfcClashResults,
|
||||
operator.SelectClash,
|
||||
operator.SelectClashResults,
|
||||
operator.SelectClashSource,
|
||||
operator.SelectIfcClashResults,
|
||||
operator.SelectSmartGroup,
|
||||
operator.SelectSmartGroupedClashesPath,
|
||||
operator.SmartClashGroup,
|
||||
operator.SelectSmartGroup,
|
||||
operator.LoadSmartGroupsForActiveClashSet,
|
||||
prop.Clash,
|
||||
prop.ClashSource,
|
||||
prop.ClashSet,
|
||||
prop.SmartClashGroup,
|
||||
prop.BIMClashProperties,
|
||||
ui.BIM_PT_ifcclash,
|
||||
ui.BIM_PT_clash_manager,
|
||||
ui.BIM_PT_dumb_clash_manager,
|
||||
ui.BIM_PT_smart_clash_manager,
|
||||
ui.BIM_UL_clashes,
|
||||
ui.BIM_UL_clash_sets,
|
||||
ui.BIM_UL_smart_groups,
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ import ifcopenshell
|
||||
from mathutils import Matrix, Vector
|
||||
from math import radians
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class ExportClashSets(bpy.types.Operator):
|
||||
@@ -245,12 +246,14 @@ class ExecuteIfcClash(bpy.types.Operator):
|
||||
y = Vector([y.x, y.y, y.z])
|
||||
x = y.cross(z)
|
||||
|
||||
mat = Matrix([
|
||||
[x[0], y[0], z[0], p.x],
|
||||
[x[1], y[1], z[1], p.y],
|
||||
[x[2], y[2], z[2], p.z],
|
||||
[0, 0, 0, 0],
|
||||
])
|
||||
mat = Matrix(
|
||||
[
|
||||
[x[0], y[0], z[0], p.x],
|
||||
[x[1], y[1], z[1], p.y],
|
||||
[x[2], y[2], z[2], p.z],
|
||||
[0, 0, 0, 0],
|
||||
]
|
||||
)
|
||||
|
||||
camera.matrix_world = mat
|
||||
context.scene.camera = camera
|
||||
@@ -336,12 +339,132 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
else:
|
||||
element_file = self.file
|
||||
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if element.GlobalId in global_ids:
|
||||
try:
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
except:
|
||||
continue
|
||||
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if not global_id:
|
||||
continue
|
||||
if global_id in global_ids:
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadIfcClashes(bpy.types.Operator):
|
||||
bl_idname = "bim.load_ifc_clashes"
|
||||
bl_label = "Load IFC Clashes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Load the clashing IFC geometry stored in a file"
|
||||
filename_ext = ".json"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
with open(self.filepath) as f:
|
||||
clash_sets_json = json.load(f)
|
||||
active_clash_set = context.scene.BIMClashProperties.active_clash_set
|
||||
|
||||
for clash_set in clash_sets_json:
|
||||
if not "clashes" in clash_set.keys():
|
||||
self.report({"WARNING"}, "No clashes found for the selected Clash Set.")
|
||||
return {"CANCELLED"}
|
||||
active_clash_set.clashes.clear()
|
||||
for clash in clash_set["clashes"].values():
|
||||
blender_clash = active_clash_set.clashes.add()
|
||||
blender_clash.a_global_id = clash["a_global_id"]
|
||||
blender_clash.b_global_id = clash["b_global_id"]
|
||||
blender_clash.a_name = "{}/{}".format(clash["a_ifc_class"], clash["a_name"])
|
||||
blender_clash.b_name = "{}/{}".format(clash["b_ifc_class"], clash["b_name"])
|
||||
blender_clash.status = False if not "status" in clash.keys() else clash["status"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SaveIfcClashes(bpy.types.Operator):
|
||||
bl_idname = "bim.save_ifc_clashes"
|
||||
bl_label = "Save IFC Clashes"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Save the clashing IFC geometry stored in a file"
|
||||
filename_ext = ".json"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
|
||||
WindowManager = context.window_manager
|
||||
WindowManager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
clash_sets_json = self.load_json()
|
||||
active_clash_set = context.scene.BIMClashProperties.active_clash_set
|
||||
self.update_clash_sets(clash_sets_json, active_clash_set)
|
||||
self.save_json(clash_sets_json)
|
||||
return {"FINISHED"}
|
||||
|
||||
def load_json(self):
|
||||
with open(self.filepath) as f:
|
||||
return json.load(f)
|
||||
|
||||
def update_clash_sets(self, clash_sets_json, active_clash_set):
|
||||
for clash_set in clash_sets_json:
|
||||
if clash_set["name"] != active_clash_set.name or "clashes" not in clash_set.keys():
|
||||
continue
|
||||
for clash in active_clash_set.clashes:
|
||||
clash_key = clash.a_global_id + "-" + clash.b_global_id
|
||||
if clash_set.get("clashes", {}).get(clash_key, None) is not None:
|
||||
clash_set["clashes"][clash_key]["status"] = clash.status
|
||||
|
||||
def save_json(self, clash_sets_json):
|
||||
with open(self.filepath, "w") as destination:
|
||||
json.dump(clash_sets_json, destination, indent=4)
|
||||
|
||||
|
||||
class SelectClash(bpy.types.Operator):
|
||||
bl_idname = "bim.select_clash"
|
||||
bl_label = "Select Clash"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Select the clashing IFC geometry stored in a file"
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
clash = context.scene.BIMClashProperties.active_clash
|
||||
|
||||
products = []
|
||||
linked_objects = []
|
||||
try:
|
||||
products.append(tool.Ifc.get().by_guid(clash.a_global_id))
|
||||
except:
|
||||
linked_objects.append(clash.a_name)
|
||||
try:
|
||||
products.append(tool.Ifc.get().by_guid(clash.b_global_id))
|
||||
except:
|
||||
linked_objects.append(clash.b_name)
|
||||
|
||||
tool.Spatial.select_products(products, unhide=True)
|
||||
print(linked_objects)
|
||||
for obj_name in linked_objects:
|
||||
obj = bpy.data.objects.get(obj_name)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
else:
|
||||
print("Object not found:", obj_name)
|
||||
if context.scene.BIMClashProperties.sould_focus_on_clash:
|
||||
context_override = tool.Blender.get_viewport_context()
|
||||
with bpy.context.temp_override(**context_override):
|
||||
bpy.ops.view3d.view_selected()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SmartClashGroup(bpy.types.Operator):
|
||||
bl_idname = "bim.smart_clash_group"
|
||||
bl_label = "Smart Group Clashes"
|
||||
@@ -426,9 +549,9 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
||||
new_group = context.scene.BIMClashProperties.smart_clash_groups.add()
|
||||
new_group.number = f"{smart_group}"
|
||||
for pair in global_id_pairs:
|
||||
for id in pair:
|
||||
for guid in pair:
|
||||
new_global_id = new_group.global_ids.add()
|
||||
new_global_id.name = id
|
||||
new_global_id.guid = guid
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -443,20 +566,16 @@ class SelectSmartGroup(bpy.types.Operator):
|
||||
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
# Select smart group in view
|
||||
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
|
||||
# print(selected_smart_group.number)
|
||||
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
products = []
|
||||
for global_id in selected_smart_group.global_ids:
|
||||
try:
|
||||
products.append(tool.Ifc.get().by_guid(global_id.guid))
|
||||
except:
|
||||
continue
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
for id in selected_smart_group.global_ids:
|
||||
# print("Id: ", id)
|
||||
# print("Global id: ", element.GlobalId)
|
||||
if element.GlobalId in id.name:
|
||||
# print("object match: ", global_id)
|
||||
obj.select_set(True)
|
||||
|
||||
tool.Spatial.select_products(products, unhide=True)
|
||||
context_override = tool.Blender.get_viewport_context()
|
||||
with bpy.context.temp_override(**context_override):
|
||||
bpy.ops.view3d.view_selected()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -43,11 +43,20 @@ class ClashSource(PropertyGroup):
|
||||
)
|
||||
|
||||
|
||||
class Clash(PropertyGroup):
|
||||
a_global_id: StringProperty(name="A")
|
||||
b_global_id: StringProperty(name="B")
|
||||
a_name: StringProperty(name="A Name")
|
||||
b_name: StringProperty(name="B Name")
|
||||
status: BoolProperty(name="Status", default=False)
|
||||
|
||||
|
||||
class ClashSet(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
tolerance: FloatProperty(name="Tolerance")
|
||||
a: CollectionProperty(name="Group A", type=ClashSource)
|
||||
b: CollectionProperty(name="Group B", type=ClashSource)
|
||||
clashes: CollectionProperty(name="Clashes", type=Clash)
|
||||
|
||||
|
||||
class SmartClashGroup(PropertyGroup):
|
||||
@@ -63,11 +72,13 @@ class BIMClashProperties(PropertyGroup):
|
||||
clash_results_path: StringProperty(name="Clash Results Path")
|
||||
smart_grouped_clashes_path: StringProperty(name="Smart Grouped Clashes Path")
|
||||
active_clash_set_index: IntProperty(name="Active Clash Set Index")
|
||||
active_clash_index: IntProperty(name="Active Clash Index")
|
||||
smart_clash_groups: CollectionProperty(name="Smart Clash Groups", type=SmartClashGroup)
|
||||
active_smart_group_index: IntProperty(name="Active Smart Group Index")
|
||||
smart_clash_grouping_max_distance: IntProperty(
|
||||
name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10
|
||||
)
|
||||
sould_focus_on_clash: BoolProperty(name="Show Focus Clash", default=False)
|
||||
|
||||
@property
|
||||
def active_clash_set(self):
|
||||
@@ -80,3 +91,9 @@ class BIMClashProperties(PropertyGroup):
|
||||
if not self.smart_clash_groups:
|
||||
return None
|
||||
return self.smart_clash_groups[self.active_smart_group_index]
|
||||
|
||||
@property
|
||||
def active_clash(self):
|
||||
if not self.active_clash_set:
|
||||
return None
|
||||
return self.active_clash_set.clashes[self.active_clash_index]
|
||||
|
||||
@@ -27,7 +27,7 @@ class BIM_PT_ifcclash(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_quality_control"
|
||||
bl_parent_id = "BIM_PT_tab_clash_detection"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -102,9 +102,46 @@ class BIM_PT_clash_manager(Panel):
|
||||
bl_idname = "BIM_PT_clash_manager"
|
||||
bl_label = "Clash Manager"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_clash_detection"
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_dumb_clash_manager(Panel):
|
||||
bl_idname = "BIM_PT_dumb_clash_manager"
|
||||
bl_label = "Dumb Manager"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_clash_manager"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMClashProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.load_ifc_clashes", text="LOAD IFC CLASHES", icon="IMPORT")
|
||||
row.operator("bim.save_ifc_clashes", text="SAVE CLASH RESULTS", icon="EXPORT")
|
||||
layout.template_list("BIM_UL_clashes", "", props.active_clash_set, "clashes", props, "active_clash_index")
|
||||
# bim.select_clash
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.select_clash", text="SELECT CLASH", icon="IMPORT")
|
||||
row.prop(props, "sould_focus_on_clash", icon="RESTRICT_VIEW_OFF")
|
||||
|
||||
|
||||
class BIM_PT_smart_clash_manager(Panel):
|
||||
bl_idname = "BIM_PT_smart_clash_manager"
|
||||
bl_label = "Smart Clash Manager"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_clash_manager"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -155,3 +192,14 @@ class BIM_UL_smart_groups(bpy.types.UIList):
|
||||
layout.label(text=str(item.number), translate=False, icon="NONE", icon_value=0)
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
|
||||
|
||||
class BIM_UL_clashes(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=str(item.a_name), translate=False, icon="NONE", icon_value=0)
|
||||
row.label(text=str(item.b_name), translate=False, icon="NONE", icon_value=0)
|
||||
row.prop(item, "status", text="")
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
|
||||
@@ -21,13 +21,21 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddClassification,
|
||||
operator.AddClassificationFromBSDD,
|
||||
operator.AddClassificationReference,
|
||||
operator.AddClassificationReferenceFromBSDD,
|
||||
operator.AddManualClassification,
|
||||
operator.AddManualClassificationReference,
|
||||
operator.ChangeClassificationLevel,
|
||||
operator.DisableAddingManualClassification,
|
||||
operator.DisableAddingManualClassificationReference,
|
||||
operator.DisableEditingClassification,
|
||||
operator.DisableEditingClassificationReference,
|
||||
operator.DisableEditingClassificationReferences,
|
||||
operator.EditClassification,
|
||||
operator.EditClassificationReference,
|
||||
operator.EnableAddingManualClassification,
|
||||
operator.EnableAddingManualClassificationReference,
|
||||
operator.EnableEditingClassification,
|
||||
operator.EnableEditingClassificationReference,
|
||||
operator.LoadClassificationLibrary,
|
||||
|
||||
@@ -73,6 +73,10 @@ class ReferencesData:
|
||||
if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]:
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def classifications(cls):
|
||||
return [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type("IfcClassification")]
|
||||
|
||||
|
||||
class ClassificationReferencesData(ReferencesData):
|
||||
data = {}
|
||||
@@ -83,6 +87,8 @@ class ClassificationReferencesData(ReferencesData):
|
||||
cls.is_loaded = True
|
||||
cls.data["references"] = cls.references()
|
||||
cls.data["active_classification_library"] = cls.active_classification_library()
|
||||
cls.data["classifications"] = cls.classifications()
|
||||
cls.data["object_type"] = "OBJECT"
|
||||
|
||||
@classmethod
|
||||
def references(cls):
|
||||
@@ -105,6 +111,8 @@ class MaterialClassificationsData(ReferencesData):
|
||||
cls.is_loaded = True
|
||||
cls.data["references"] = cls.references()
|
||||
cls.data["active_classification_library"] = cls.active_classification_library()
|
||||
cls.data["classifications"] = cls.classifications()
|
||||
cls.data["object_type"] = "MATERIAL"
|
||||
|
||||
@classmethod
|
||||
def references(cls):
|
||||
@@ -127,6 +135,8 @@ class CostClassificationsData(ReferencesData):
|
||||
cls.is_loaded = True
|
||||
cls.data["references"] = cls.references()
|
||||
cls.data["active_classification_library"] = cls.active_classification_library()
|
||||
cls.data["classifications"] = cls.classifications()
|
||||
cls.data["object_type"] = "COST"
|
||||
|
||||
@classmethod
|
||||
def references(cls):
|
||||
|
||||
@@ -54,6 +54,120 @@ class AddClassification(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
|
||||
|
||||
class AddManualClassification(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_manual_classification"
|
||||
bl_label = "Add Manual Classification"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.classification_attributes)
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification="Unnamed"
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"classification.edit_classification", tool.Ifc.get(), classification=classification, attributes=attributes
|
||||
)
|
||||
props.is_adding = False
|
||||
|
||||
|
||||
class AddManualClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_manual_classification_reference"
|
||||
bl_label = "Add Manual Classification Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMClassificationReferenceProperties
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.reference_attributes)
|
||||
product = tool.Ifc.get_entity(obj)
|
||||
# TODO: refactor and support material and cost item classifications
|
||||
classification = tool.Ifc.get().by_id(int(props.classifications))
|
||||
reference = ifcopenshell.api.run(
|
||||
"classification.add_reference",
|
||||
tool.Ifc.get(),
|
||||
product=product,
|
||||
classification=classification,
|
||||
identification="X",
|
||||
name="Unnamed",
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"classification.edit_reference", tool.Ifc.get(), reference=reference, attributes=attributes
|
||||
)
|
||||
props.is_adding = False
|
||||
|
||||
|
||||
class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_classification_from_bsdd"
|
||||
bl_label = "Add Classification From bSDD"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
domain = [d for d in props.domains if d.name == props.active_domain][0]
|
||||
for element in tool.Ifc.get().by_type("IfcClassification"):
|
||||
if element.Name == props.active_domain or element.Location == domain.uri:
|
||||
return
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification=props.active_domain
|
||||
)
|
||||
classification.Source = domain.organization_name_owner
|
||||
classification.Location = domain.uri
|
||||
classification.Edition = domain.version
|
||||
|
||||
|
||||
class EnableAddingManualClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_adding_manual_classification"
|
||||
bl_label = "Enable Adding Manual Classification"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
props.is_adding = True
|
||||
props.active_classification_id = 0
|
||||
props.classification_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2("IfcClassification", props.classification_attributes)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableAddingManualClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_adding_manual_classification"
|
||||
bl_label = "Disable Adding Manual Classification"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMClassificationProperties
|
||||
props.is_adding = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableAddingManualClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_adding_manual_classification_reference"
|
||||
bl_label = "Enable Adding Manual Classification Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMClassificationReferenceProperties
|
||||
props.is_adding = True
|
||||
props.reference_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2("IfcClassificationReference", props.reference_attributes)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableAddingManualClassificationReference(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_adding_manual_classification_reference"
|
||||
bl_label = "Disable Adding Manual Classification Reference"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMClassificationReferenceProperties
|
||||
props.is_adding = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingClassification(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_classification"
|
||||
bl_label = "Enable Editing Classification"
|
||||
@@ -175,21 +289,23 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
objects = [self.obj]
|
||||
|
||||
identification = tool.Ifc.get().by_id(self.reference)[0]
|
||||
active_reference = tool.Ifc.get().by_id(self.reference)
|
||||
identification = active_reference[1]
|
||||
|
||||
for obj in objects:
|
||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
references = ifcopenshell.util.classification.get_references(element, should_inherit=False)
|
||||
for reference in references:
|
||||
if reference[0] == identification:
|
||||
if (identification and reference[1] == identification) or (
|
||||
not identification and reference == active_reference
|
||||
):
|
||||
ifcopenshell.api.run(
|
||||
"classification.remove_reference",
|
||||
tool.Ifc.get(),
|
||||
reference=reference,
|
||||
product=element,
|
||||
)
|
||||
break
|
||||
|
||||
|
||||
class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -254,6 +370,85 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
|
||||
|
||||
class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_classification_reference_from_bsdd"
|
||||
bl_label = "Add Classification Reference From bSDD"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
if self.obj_type == "Object":
|
||||
if context.selected_objects:
|
||||
objects = [o.name for o in context.selected_objects]
|
||||
else:
|
||||
objects = [context.active_object.name]
|
||||
else:
|
||||
objects = [self.obj]
|
||||
props = context.scene.BIMClassificationProperties
|
||||
bprops = context.scene.BIMBSDDProperties
|
||||
|
||||
bsdd_classification = bprops.classifications[bprops.active_classification_index]
|
||||
|
||||
classification = None
|
||||
for element in tool.Ifc.get().by_type("IfcClassification"):
|
||||
if (
|
||||
element.Name == bsdd_classification.domain_name
|
||||
or element.Location == bsdd_classification.domain_namespace_uri
|
||||
):
|
||||
classification = element
|
||||
break
|
||||
|
||||
if not classification:
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification=bsdd_classification.domain_name
|
||||
)
|
||||
classification.Location = bsdd_classification.domain_namespace_uri
|
||||
|
||||
for obj in objects:
|
||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
||||
if not ifc_definition_id:
|
||||
continue
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
reference = ifcopenshell.api.run(
|
||||
"classification.add_reference",
|
||||
tool.Ifc.get(),
|
||||
product=element,
|
||||
classification=classification,
|
||||
identification=bsdd_classification.reference_code,
|
||||
name=bsdd_classification.name,
|
||||
)
|
||||
reference.Location = bsdd_classification.uri
|
||||
|
||||
for classification_pset in bprops.classification_psets:
|
||||
is_pset = not classification_pset.name.startswith("Qto_")
|
||||
|
||||
if is_pset:
|
||||
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name, psets_only=True)
|
||||
else:
|
||||
pset = ifcopenshell.util.element.get_pset(element, classification_pset.name, qtos_only=True)
|
||||
|
||||
if pset:
|
||||
pset = tool.Ifc.get().by_id(pset["id"])
|
||||
elif is_pset:
|
||||
pset = ifcopenshell.api.run(
|
||||
"pset.add_pset", tool.Ifc.get(), product=element, name=classification_pset.name
|
||||
)
|
||||
else:
|
||||
pset = ifcopenshell.api.run(
|
||||
"pset.add_qto", tool.Ifc.get(), product=element, name=classification_pset.name
|
||||
)
|
||||
|
||||
properties = {}
|
||||
for prop in classification_pset.properties:
|
||||
properties[prop.name] = prop.get_value()
|
||||
|
||||
if is_pset:
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=properties)
|
||||
else:
|
||||
ifcopenshell.api.run("pset.edit_qto", tool.Ifc.get(), qto=pset, properties=properties)
|
||||
|
||||
|
||||
class ChangeClassificationLevel(bpy.types.Operator):
|
||||
bl_idname = "bim.change_classification_level"
|
||||
bl_label = "Change Classification Level"
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim.helper
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.classification.data import ClassificationsData
|
||||
from blenderbim.bim.module.classification.data import ClassificationsData, ClassificationReferencesData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -39,6 +40,12 @@ def get_available_classifications(self, context):
|
||||
return ClassificationsData.data["available_classifications"]
|
||||
|
||||
|
||||
def get_classifications(self, context):
|
||||
if not ClassificationReferencesData.is_loaded:
|
||||
ClassificationReferencesData.load()
|
||||
return ClassificationReferencesData.data["classifications"]
|
||||
|
||||
|
||||
class ClassificationReference(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
identification: StringProperty(name="Identification")
|
||||
@@ -48,6 +55,16 @@ class ClassificationReference(PropertyGroup):
|
||||
|
||||
|
||||
class BIMClassificationProperties(PropertyGroup):
|
||||
is_adding: BoolProperty(name="Is Adding", default=False)
|
||||
classification_source: EnumProperty(
|
||||
items=[
|
||||
("FILE", "IFC File", ""),
|
||||
("BSDD", "buildingSMART Data Dictionary", ""),
|
||||
("MANUAL", "Manual Entry", ""),
|
||||
],
|
||||
name="Classification Source",
|
||||
default="FILE",
|
||||
)
|
||||
available_classifications: EnumProperty(items=get_available_classifications, name="Available Classifications")
|
||||
classification_attributes: CollectionProperty(name="Classification Attributes", type=Attribute)
|
||||
active_classification_id: IntProperty(name="Active Classification Id")
|
||||
@@ -57,5 +74,7 @@ class BIMClassificationProperties(PropertyGroup):
|
||||
|
||||
|
||||
class BIMClassificationReferenceProperties(PropertyGroup):
|
||||
is_adding: BoolProperty(name="Is Adding", default=False)
|
||||
classifications: EnumProperty(items=get_classifications, name="Classifications")
|
||||
reference_attributes: CollectionProperty(name="Reference Attributes", type=Attribute)
|
||||
active_reference_id: IntProperty(name="Active Reference Id")
|
||||
|
||||
@@ -36,7 +36,7 @@ class BIM_PT_classifications(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -48,6 +48,48 @@ class BIM_PT_classifications(Panel):
|
||||
|
||||
self.props = context.scene.BIMClassificationProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Source", icon="OUTLINER")
|
||||
row.prop(self.props, "classification_source", text="")
|
||||
|
||||
if self.props.classification_source == "FILE":
|
||||
self.draw_add_file_ui(context)
|
||||
elif self.props.classification_source == "BSDD":
|
||||
self.draw_add_bsdd_ui(context)
|
||||
elif self.props.classification_source == "MANUAL":
|
||||
self.draw_add_manual_ui(context)
|
||||
|
||||
for classification in ClassificationsData.data["classifications"]:
|
||||
if self.props.active_classification_id == classification["id"]:
|
||||
self.draw_editable_ui()
|
||||
else:
|
||||
self.draw_ui(classification)
|
||||
|
||||
def draw_add_manual_ui(self, context):
|
||||
if self.props.is_adding:
|
||||
blenderbim.bim.helper.draw_attributes(self.props.classification_attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_manual_classification", text="Save", icon="CHECKMARK")
|
||||
row.operator("bim.disable_adding_manual_classification", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.enable_adding_manual_classification", text="Add Classification", icon="ADD")
|
||||
|
||||
def draw_add_bsdd_ui(self, context):
|
||||
self.bprops = context.scene.BIMBSDDProperties
|
||||
|
||||
if not self.bprops.active_domain:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Active bSDD Domain", icon="ERROR")
|
||||
return
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Active: " + self.bprops.active_domain, icon="URL")
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.add_classification_from_bsdd", icon="ADD")
|
||||
|
||||
def draw_add_file_ui(self, context):
|
||||
if ClassificationsData.data["has_classification_file"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "available_classifications", text="")
|
||||
@@ -58,12 +100,6 @@ class BIM_PT_classifications(Panel):
|
||||
row.label(text="No Active Classification Library")
|
||||
row.operator("bim.load_classification_library", text="", icon="IMPORT")
|
||||
|
||||
for classification in ClassificationsData.data["classifications"]:
|
||||
if self.props.active_classification_id == classification["id"]:
|
||||
self.draw_editable_ui()
|
||||
else:
|
||||
self.draw_ui(classification)
|
||||
|
||||
def draw_editable_ui(self):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_classification", text="Save changes", icon="CHECKMARK")
|
||||
@@ -84,6 +120,7 @@ class ReferenceUI:
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMClassificationProperties
|
||||
self.bprops = context.scene.BIMBSDDProperties
|
||||
self.props = obj.BIMClassificationReferenceProperties
|
||||
self.file = IfcStore.get_file()
|
||||
|
||||
@@ -100,14 +137,85 @@ class ReferenceUI:
|
||||
self.draw_reference_ui(reference)
|
||||
|
||||
def draw_add_ui(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Source", icon="OUTLINER")
|
||||
row.prop(self.sprops, "classification_source", text="")
|
||||
|
||||
if self.sprops.classification_source == "FILE":
|
||||
self.draw_add_file_ui(context)
|
||||
elif self.sprops.classification_source == "BSDD":
|
||||
self.draw_add_bsdd_ui(context)
|
||||
elif self.sprops.classification_source == "MANUAL":
|
||||
self.draw_add_manual_ui(context)
|
||||
|
||||
def draw_add_manual_ui(self, context):
|
||||
row = self.layout.row()
|
||||
row.prop(self.props, "classifications", text="")
|
||||
if self.props.is_adding:
|
||||
blenderbim.bim.helper.draw_attributes(self.props.reference_attributes, self.layout)
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator("bim.add_manual_classification_reference", text="Save", icon="CHECKMARK")
|
||||
op.type = self.data.data["object_type"]
|
||||
row.operator("bim.disable_adding_manual_classification_reference", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.enable_adding_manual_classification_reference", text="Add Reference", icon="ADD")
|
||||
|
||||
def draw_add_bsdd_ui(self, context):
|
||||
if not self.bprops.active_domain:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Active bSDD Domain", icon="ERROR")
|
||||
return
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Active: " + self.bprops.active_domain, icon="URL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.bprops, "keyword", text="")
|
||||
row.operator("bim.search_bsdd_classifications", text="", icon="VIEWZOOM")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(self.bprops, "should_filter_ifc_class")
|
||||
|
||||
if len(self.bprops.classifications):
|
||||
self.layout.template_list(
|
||||
"BIM_UL_bsdd_classifications",
|
||||
"",
|
||||
self.bprops,
|
||||
"classifications",
|
||||
self.bprops,
|
||||
"active_classification_index",
|
||||
)
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Search Results")
|
||||
|
||||
if self.bprops.active_classification_index < len(self.bprops.classifications):
|
||||
row = self.layout.row(align=True)
|
||||
op = row.operator(
|
||||
"bim.add_classification_reference_from_bsdd", text="Add Classification Reference", icon="ADD"
|
||||
)
|
||||
op.obj = self.obj
|
||||
op.obj_type = self.obj_type
|
||||
row.operator("bim.get_bsdd_classification_properties", text="", icon="COPY_ID")
|
||||
|
||||
if len(self.bprops.classification_psets):
|
||||
for pset in self.bprops.classification_psets:
|
||||
box = self.layout.box()
|
||||
row = box.row()
|
||||
row.label(text=pset.name, icon="COPY_ID")
|
||||
blenderbim.bim.helper.draw_attributes(pset.properties, box)
|
||||
|
||||
def draw_add_file_ui(self, context):
|
||||
if not self.data.data["active_classification_library"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Active Classification Library")
|
||||
row.label(text="No Active Classification Library", icon="ERROR")
|
||||
row.operator("bim.load_classification_library", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Active Classification Library: {self.data.data['active_classification_library']}")
|
||||
#row.prop(self.sprops, "available_classifications", text="")
|
||||
# row.prop(self.sprops, "available_classifications", text="")
|
||||
if not self.sprops.available_library_references:
|
||||
op = row.operator("bim.change_classification_level", text="", icon="GREASEPENCIL")
|
||||
op.parent_id = int(self.sprops.available_classifications)
|
||||
@@ -146,6 +254,8 @@ class ReferenceUI:
|
||||
name = reference["Identification"] or "No Identification"
|
||||
row.label(text=name, icon="ASSET_MANAGER")
|
||||
row.label(text=reference["Name"] or "")
|
||||
if reference["Location"]:
|
||||
row.operator("bim.open_uri", icon="URL", text="").uri = reference["Location"]
|
||||
if not self.props.active_reference_id:
|
||||
op = row.operator("bim.enable_editing_classification_reference", text="", icon="GREASEPENCIL")
|
||||
op.reference = reference["id"]
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import os
|
||||
import logging
|
||||
import ifcopenshell
|
||||
import json
|
||||
import webbrowser
|
||||
import tempfile
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class SelectCobieIfcFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_cobie_ifc_file"
|
||||
bl_label = "Select COBie IFC File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.COBieProperties.cobie_ifc_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class SelectCobieJsonFile(bpy.types.Operator):
|
||||
bl_idname = "bim.select_cobie_json_file"
|
||||
bl_label = "Select COBie JSON File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.COBieProperties.cobie_json_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class ExecuteIfcCobie(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_ifc_cobie"
|
||||
bl_label = "Execute IFCCOBie"
|
||||
file_format: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.COBieProperties
|
||||
return props.should_load_from_memory or props.cobie_ifc_file
|
||||
|
||||
def execute(self, context):
|
||||
from cobie import IfcCobieParser
|
||||
|
||||
props = context.scene.COBieProperties
|
||||
|
||||
if props.should_load_from_memory:
|
||||
output_dir = tempfile.gettempdir()
|
||||
else:
|
||||
output_dir = os.path.dirname(props.cobie_ifc_file)
|
||||
|
||||
output = os.path.join(output_dir, "output")
|
||||
logger = logging.getLogger("IFCtoCOBie")
|
||||
fh = logging.FileHandler(os.path.join(output_dir, "cobie.log"))
|
||||
fh.setLevel(logging.DEBUG)
|
||||
fh.setFormatter(logging.Formatter("%(asctime)s : %(levelname)s : %(message)s"))
|
||||
logger = logging.getLogger("IFCtoCOBie")
|
||||
logger.addHandler(fh)
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
if props.cobie_json_file:
|
||||
with open(props.cobie_json_file, "r") as f:
|
||||
custom_data = json.load(f)
|
||||
else:
|
||||
custom_data = {}
|
||||
parser = IfcCobieParser(logger, selector)
|
||||
|
||||
ifc_file = IfcStore.get_file()
|
||||
|
||||
if not (ifc_file and props.should_load_from_memory):
|
||||
ifc_file = props.cobie_ifc_file
|
||||
|
||||
parser.parse(
|
||||
ifc_file,
|
||||
props.cobie_types,
|
||||
props.cobie_components,
|
||||
custom_data,
|
||||
)
|
||||
if self.file_format == "xlsx":
|
||||
from cobie import CobieXlsWriter
|
||||
|
||||
writer = CobieXlsWriter(parser, output)
|
||||
writer.write()
|
||||
webbrowser.open("file://" + output + "." + self.file_format)
|
||||
elif self.file_format == "ods":
|
||||
from cobie import CobieOdsWriter
|
||||
|
||||
writer = CobieOdsWriter(parser, output)
|
||||
writer.write()
|
||||
webbrowser.open("file://" + output + "." + self.file_format)
|
||||
else:
|
||||
from cobie import CobieCsvWriter
|
||||
|
||||
writer = CobieCsvWriter(parser, output_dir)
|
||||
writer.write()
|
||||
webbrowser.open("file://" + output_dir)
|
||||
webbrowser.open("file://" + output_dir + "/cobie.log")
|
||||
return {"FINISHED"}
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_constraints(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_context(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_geometry"
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -20,64 +20,65 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddCostColumn,
|
||||
operator.AddCostItem,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.AddCostSchedule,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.EditCostSchedule,
|
||||
operator.AddCostValue,
|
||||
operator.AddCurrency,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.AssignCostItemType,
|
||||
operator.AssignCostValue,
|
||||
operator.CalculateCostItemResourceValue,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.ClearCostItemAssignments,
|
||||
operator.ContractCostItem,
|
||||
operator.ContractCostItemRate,
|
||||
operator.ContractCostItems,
|
||||
operator.CopyCostItem,
|
||||
operator.CopyCostItemValues,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.EditCostItem,
|
||||
operator.EditCostItemQuantity,
|
||||
operator.EditCostItemValue,
|
||||
operator.EditCostItemValueFormula,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EditCostSchedule,
|
||||
operator.EnableEditingCostItem,
|
||||
operator.ExportCostSchedules,
|
||||
operator.ExpandCostItems,
|
||||
operator.EnableEditingCostItemQuantities,
|
||||
operator.EnableEditingCostItemQuantity,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostItems,
|
||||
operator.EnableEditingCostItemValue,
|
||||
operator.EnableEditingCostItemValueFormula,
|
||||
operator.DisableEditingCostItem,
|
||||
operator.DisableEditingCostSchedule,
|
||||
operator.DisableEditingCostItemQuantity,
|
||||
operator.DisableEditingCostItemValue,
|
||||
operator.AddCostColumn,
|
||||
operator.RemoveCostColumn,
|
||||
operator.AddCostItem,
|
||||
operator.AddSummaryCostItem,
|
||||
operator.EnableEditingCostItemValues,
|
||||
operator.EnableEditingCostSchedule,
|
||||
operator.ExpandCostItem,
|
||||
operator.ContractCostItem,
|
||||
operator.ExpandCostItemRate,
|
||||
operator.ExpandCostItems,
|
||||
operator.ExportCostSchedules,
|
||||
operator.HighlightProductCostItem,
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemElementQuantities,
|
||||
operator.LoadCostItemQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemTypes,
|
||||
operator.LoadProductCostItems,
|
||||
operator.LoadScheduleOfRates,
|
||||
operator.RemoveCostColumn,
|
||||
operator.RemoveCostItem,
|
||||
operator.AssignCostItemType,
|
||||
operator.UnassignCostItemType,
|
||||
operator.AssignCostItemQuantity,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.AddCostItemQuantity,
|
||||
operator.RemoveCostItemQuantity,
|
||||
operator.AddCostValue,
|
||||
operator.RemoveCostItemValue,
|
||||
operator.CopyCostItemValues,
|
||||
operator.RemoveCostSchedule,
|
||||
operator.ReorderCostItem,
|
||||
operator.SelectCostItemProducts,
|
||||
operator.SelectCostScheduleProducts,
|
||||
operator.ImportCostScheduleCsv,
|
||||
operator.LoadCostItemQuantities,
|
||||
operator.LoadCostItemTypes,
|
||||
operator.AssignCostValue,
|
||||
operator.LoadScheduleOfRates,
|
||||
operator.ExpandCostItemRate,
|
||||
operator.ContractCostItemRate,
|
||||
operator.CalculateCostItemResourceValue,
|
||||
operator.ClearCostItemAssignments,
|
||||
operator.HighlightProductCostItem,
|
||||
operator.LoadProductCostItems,
|
||||
operator.ReorderCostItem,
|
||||
operator.SelectUnassignedProducts,
|
||||
operator.LoadCostItemElementQuantities,
|
||||
operator.LoadCostItemTaskQuantities,
|
||||
operator.LoadCostItemResourceQuantities,
|
||||
operator.ChangeParentCostItem,
|
||||
operator.CopyCostItem,
|
||||
operator.AddCurrency,
|
||||
operator.UnassignCostItemQuantity,
|
||||
operator.UnassignCostItemType,
|
||||
prop.CostItem,
|
||||
prop.CostItemQuantity,
|
||||
prop.CostItemType,
|
||||
|
||||
@@ -117,6 +117,7 @@ class CostSchedulesData:
|
||||
data["TotalAppliedValue"] = 0.0
|
||||
data["TotalCost"] = 0.0
|
||||
has_unit_basis = False
|
||||
is_sum = False
|
||||
if root_element.is_a("IfcCostItem"):
|
||||
values = root_element.CostValues
|
||||
elif root_element.is_a("IfcConstructionResource"):
|
||||
@@ -130,14 +131,18 @@ class CostSchedulesData:
|
||||
data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"]
|
||||
data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"]
|
||||
has_unit_basis = True
|
||||
if has_unit_basis:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] / data["UnitBasisValueComponent"]
|
||||
else:
|
||||
if data["TotalCostQuantity"] is not None:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * data["TotalCostQuantity"]
|
||||
else:
|
||||
data["TotalCost"] = data["TotalAppliedValue"]
|
||||
data["TotalAppliedValue"] = None
|
||||
data["UnitBasisValueComponent"] = 1
|
||||
data["UnitBasisUnitSymbol"] = "U"
|
||||
if cost_value.Category == "*":
|
||||
is_sum = True
|
||||
cost_quantity = data["TotalCostQuantity"] or 1
|
||||
if has_unit_basis:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity / data["UnitBasisValueComponent"]
|
||||
else:
|
||||
data["TotalCost"] = data["TotalAppliedValue"] * cost_quantity
|
||||
if is_sum:
|
||||
data["TotalAppliedValue"] = None
|
||||
|
||||
@classmethod
|
||||
def _load_cost_item_quantities(cls, cost_item, data):
|
||||
@@ -153,8 +158,10 @@ class CostSchedulesData:
|
||||
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
|
||||
if unit:
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
else:
|
||||
data["UnitSymbol"] = None
|
||||
if quantity.is_a("IfcPhysicalSimpleQuantity"):
|
||||
measure_class = quantity.wrapped_data.declaration().as_entity().attribute_by_index(3).type_of_attribute().declared_type().name()
|
||||
if "Count" in measure_class:
|
||||
data["UnitSymbol"] = "U"
|
||||
|
||||
# same_unit_nested_cost_item = set()
|
||||
# data["DerivedTotalCostQuantity"] = None
|
||||
@@ -249,7 +256,7 @@ class CostSchedulesData:
|
||||
for cost_value in cost_item.CostValues or []:
|
||||
label = "{0:.2f}".format(ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value))
|
||||
label += " = {}".format(ifcopenshell.util.cost.serialise_cost_value(cost_value))
|
||||
results.append({"id": cost_value.id(), "label": label})
|
||||
results.append({"id": cost_value.id(), "label": label, "name": cost_value.Name})
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -33,9 +33,24 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Add Cost Schedule"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Add a new cost schedule"
|
||||
name: bpy.props.StringProperty()
|
||||
object_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_cost_schedule(tool.Ifc, predefined_type=context.scene.BIMCostProperties.cost_schedule_predefined_types)
|
||||
predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types
|
||||
if predefined_type == "USERDEFINED":
|
||||
predefined_type = self.object_type
|
||||
core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "name", text="Name")
|
||||
layout.prop(context.scene.BIMCostProperties, "cost_schedule_predefined_types", text="Type")
|
||||
if context.scene.BIMCostProperties.cost_schedule_predefined_types == "USERDEFINED":
|
||||
layout.prop(self, "object_type", text="Object type")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -156,6 +171,17 @@ class ContractCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.contract_cost_item(tool.Cost, cost_item=tool.Ifc.get().by_id(self.cost_item))
|
||||
|
||||
|
||||
class ContractCostItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.contract_cost_items"
|
||||
bl_label = "Contract Cost Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Collapse cost item tree"
|
||||
cost_item: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.contract_cost_items(tool.Cost)
|
||||
|
||||
|
||||
class RemoveCostItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_cost_item"
|
||||
bl_label = "Remove Cost Item"
|
||||
@@ -635,7 +661,9 @@ class ExportCostSchedules(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
cost_schedule = tool.Ifc.get().by_id(self.cost_schedule) if self.cost_schedule else None
|
||||
r = core.export_cost_schedules(tool.Cost, filepath=self.filepath, format=self.format, cost_schedule=cost_schedule)
|
||||
r = core.export_cost_schedules(
|
||||
tool.Cost, filepath=self.filepath, format=self.format, cost_schedule=cost_schedule
|
||||
)
|
||||
if isinstance(r, str):
|
||||
self.report({"ERROR"}, r)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -21,7 +21,6 @@ import blenderbim.bim.module.cost.prop as CostProp
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.cost.data import CostSchedulesData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_cost_schedules(Panel):
|
||||
@@ -30,27 +29,31 @@ class BIM_PT_cost_schedules(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_cost"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get() and tool.Ifc.get().schema != "IFC2X3"
|
||||
file = IfcStore.get_file()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not CostSchedulesData.is_loaded:
|
||||
CostSchedulesData.load()
|
||||
|
||||
self.props = context.scene.BIMCostProperties
|
||||
row = self.layout.row()
|
||||
if not self.props.active_cost_schedule_id:
|
||||
row = self.layout.row(align=True)
|
||||
if CostSchedulesData.data["total_cost_schedules"]:
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.export_cost_schedules",icon="EXPORT",text="Export All Schedules")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
|
||||
else:
|
||||
row.label(text="No Cost Schedules found.", icon="COMMUNITY")
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.prop(self.props, "cost_schedule_predefined_types")
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="Add")
|
||||
row.label(text="No Cost Schedules found.", icon="TEXT")
|
||||
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="")
|
||||
row.operator("import_cost_schedule_csv.bim",icon="IMPORT",text="")
|
||||
|
||||
for schedule in CostSchedulesData.data["schedules"]:
|
||||
self.draw_cost_schedule_ui(schedule)
|
||||
@@ -138,8 +141,8 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.add_summary_cost_item", text="Add Summary Cost", icon="ADD")
|
||||
row.operator("bim.expand_all_tasks", text="Expand All")
|
||||
row.operator("bim.contract_all_tasks", text="Contract All")
|
||||
row.operator("bim.expand_cost_items", text="Expand All")
|
||||
row.operator("bim.contract_cost_items", text="Contract All")
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
||||
@@ -235,6 +238,8 @@ class BIM_PT_cost_schedules(Panel):
|
||||
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, self.layout.box())
|
||||
|
||||
def draw_readonly_cost_value_ui(self, layout, cost_value):
|
||||
if cost_value["name"]:
|
||||
layout.label(text=f"{cost_value['name']}: ")
|
||||
if self.props.active_cost_value_id == cost_value["id"] and self.props.cost_value_editing_type == "FORMULA":
|
||||
layout.prop(self.props, "cost_value_formula", text="")
|
||||
else:
|
||||
@@ -619,18 +624,22 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
row.label(text="", icon="DOT")
|
||||
|
||||
def draw_total_cost_column(self, layout, cost_item):
|
||||
format_numbers = "{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " ")
|
||||
currency = CostSchedulesData.data["currency"]
|
||||
text = "{} {}".format(format_numbers, currency["name"]) if currency else format_numbers
|
||||
layout.label(text=text)
|
||||
|
||||
def draw_quantity_column(self, layout, cost_item):
|
||||
if CostSchedulesData.data["is_editing_rates"]:
|
||||
self.draw_uom_column(layout, cost_item)
|
||||
else:
|
||||
self.draw_total_quantity_column(layout, cost_item)
|
||||
|
||||
def draw_uom_column(self, layout, cost_item):
|
||||
layout.label(text=cost_item["UnitBasisUnitSymbol"])
|
||||
|
||||
def draw_total_quantity_column(self, layout, cost_item):
|
||||
if cost_item["TotalCostQuantity"]:
|
||||
label = "{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}"
|
||||
layout.label(text=label)
|
||||
else:
|
||||
layout.label(text="-")
|
||||
|
||||
def draw_value_column(self, layout, cost_item):
|
||||
if cost_item["TotalAppliedValue"]:
|
||||
text = "{0:,.2f}".format(cost_item["TotalAppliedValue"]).replace(",", " ")
|
||||
@@ -642,8 +651,11 @@ class BIM_UL_cost_items_trait:
|
||||
else:
|
||||
layout.label(text="-")
|
||||
|
||||
def draw_uom_column(self, layout, cost_item):
|
||||
layout.label(text=cost_item["UnitBasisUnitSymbol"] or "-" if cost_item["UnitBasisValueComponent"] else "-")
|
||||
def draw_total_cost_column(self, layout, cost_item):
|
||||
format_numbers = "{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " ")
|
||||
currency = CostSchedulesData.data["currency"]
|
||||
text = "{} {}".format(format_numbers, currency["name"]) if currency else format_numbers
|
||||
layout.label(text=text)
|
||||
|
||||
def draw_order_operator(self, row, ifc_definition_id, cost_item):
|
||||
if cost_item["NestingIndex"] is not None:
|
||||
@@ -656,20 +668,6 @@ class BIM_UL_cost_items_trait:
|
||||
op.cost_item = ifc_definition_id
|
||||
op.new_index = cost_item["NestingIndex"] - 1
|
||||
|
||||
def draw_total_quantity_column(self, layout, cost_item):
|
||||
if cost_item["TotalCostQuantity"]:
|
||||
label = "{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}"
|
||||
layout.label(text=label)
|
||||
else:
|
||||
layout.label(text="-")
|
||||
# if cost_item["DerivedTotalCostQuantity"] not in [None, 0]:
|
||||
# layout.label(text="{0:.2f}".format(cost_item["DerivedTotalCostQuantity"]) + f" {cost_item['DerivedUnitSymbol'] or '-'}")
|
||||
# else:
|
||||
# if cost_item["TotalCostQuantity"] == 0:
|
||||
# layout.label(text="-")
|
||||
# else:
|
||||
# layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" {cost_item['UnitSymbol'] or '-'}")
|
||||
|
||||
|
||||
class BIM_UL_cost_items(BIM_UL_cost_items_trait, UIList):
|
||||
def __init__(self):
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from . import prop, workspace
|
||||
|
||||
classes = (
|
||||
prop.BIMCoveringProperties,
|
||||
workspace.Hotkey,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
if not bpy.app.background:
|
||||
bpy.utils.register_tool(workspace.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False)
|
||||
bpy.types.Scene.BIMCoveringProperties = bpy.props.PointerProperty(type=prop.BIMCoveringProperties)
|
||||
# bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
|
||||
# bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
if not bpy.app.background:
|
||||
bpy.utils.unregister_tool(workspace.CoveringTool)
|
||||
del bpy.types.Scene.BIMCoveringProperties
|
||||
# del bpy.types.Object.BIMObjectSpatialProperties
|
||||
# del bpy.types.Scene.BIMSpatialManagerProperties
|
||||
Binary file not shown.
+6
-11
@@ -17,8 +17,8 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.module.lca.data import LcaData
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
#from blenderbim.bim.module.spatial.data import SpatialData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -30,15 +30,10 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
#import blenderbim.tool as tool
|
||||
#import blenderbim.core.geometry
|
||||
#import ifcopenshell
|
||||
|
||||
|
||||
def get_product_systems(self, context):
|
||||
if not LcaData.is_loaded:
|
||||
LcaData.load()
|
||||
return LcaData.data["product_systems"]
|
||||
|
||||
|
||||
class BIMLCAProperties(PropertyGroup):
|
||||
is_connected: BoolProperty(name="Is Connected")
|
||||
amount: FloatProperty(name="Amount")
|
||||
product_systems: EnumProperty(items=get_product_systems, name="Product Systems")
|
||||
class BIMCoveringProperties(PropertyGroup):
|
||||
ceiling_height: bpy.props.FloatProperty(name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height")
|
||||
@@ -0,0 +1,272 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 @Andrej730
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.bim.handler
|
||||
|
||||
|
||||
# declaring it here to avoid circular import problems
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
class CoveringTool(WorkSpaceTool):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_context_mode = "OBJECT"
|
||||
bl_idname = "bim.covering_tool"
|
||||
bl_label = "Covering Tool"
|
||||
bl_description = "Create and edit coverings"
|
||||
bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.covering")
|
||||
bl_widget = None
|
||||
ifc_element_type = "IfcCoveringType"
|
||||
bl_keymap = tool.Blender.get_default_selection_keypmap() + (
|
||||
("bim.covering_hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
|
||||
("bim.covering_hotkey", {"type": "G", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_G")]}),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def draw_settings(cls, context, layout, ws_tool):
|
||||
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
|
||||
def add_layout_hotkey(layout, text, hotkey, description):
|
||||
args = ["covering", layout, text, hotkey, description]
|
||||
tool.Blender.add_layout_hotkey_operator(*args)
|
||||
|
||||
class CoveringToolUI:
|
||||
@classmethod
|
||||
def draw(cls, context, layout, ifc_element_type = None):
|
||||
cls.layout = layout
|
||||
cls.props = context.scene.BIMModelProperties
|
||||
cls.covering_props = context.scene.BIMCoveringProperties
|
||||
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
if not tool.Ifc.get():
|
||||
row.label(text="No IFC Project", icon="ERROR")
|
||||
return
|
||||
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load(ifc_element_type)
|
||||
elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
|
||||
AuthoringData.load(ifc_element_type)
|
||||
|
||||
if context.region.type == "TOOL_HEADER":
|
||||
cls.draw_header_interface()
|
||||
elif context.region.type in ("UI", "WINDOW"):
|
||||
cls.draw_basic_bim_tool_interface()
|
||||
|
||||
cls.draw_default_interface()
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def draw_header_interface(cls):
|
||||
cls.draw_type_selection_interface()
|
||||
|
||||
@classmethod
|
||||
def draw_default_interface(cls):
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(data=cls.props, property="rl3", text="RL")
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(data=cls.covering_props, property="ceiling_height", text="Ceiling Height")
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
relating_type_id = int(cls.props.relating_type_id)
|
||||
type_material_usage = ifcopenshell.util.element.get_material(tool.Ifc.get().by_id(relating_type_id)).is_a()
|
||||
|
||||
# PLEASE KEEP COMMENTS AS A REMINDER
|
||||
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||
|
||||
if (type_material_usage == "IfcMaterialLayerSet" and
|
||||
not bpy.context.selected_objects):
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
if tool.Ifc.get_entity(collection_obj):
|
||||
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
op = row.operator("bim.add_instance_flooring_covering_from_cursor")
|
||||
elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
op = row.operator("bim.add_instance_ceiling_covering_from_cursor")
|
||||
else:
|
||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
op.from_invoke = True
|
||||
if cls.props.relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(cls.props.relating_type_id)
|
||||
|
||||
else:
|
||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
op.from_invoke = True
|
||||
if cls.props.relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(cls.props.relating_type_id)
|
||||
|
||||
elif (AuthoringData.data["predefined_type"] == "FLOORING" and
|
||||
type_material_usage == "IfcMaterialLayerSet" and
|
||||
element and
|
||||
bpy.context.selected_objects and
|
||||
element.is_a("IfcWall")):
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
op = row.operator("bim.add_instance_flooring_coverings_from_walls")
|
||||
|
||||
elif (AuthoringData.data["predefined_type"] == "CEILING" and
|
||||
type_material_usage == "IfcMaterialLayerSet" and
|
||||
element and
|
||||
bpy.context.selected_objects and
|
||||
element.is_a("IfcWall")):
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
op = row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||
|
||||
elif (element and
|
||||
bpy.context.selected_objects and
|
||||
element.is_a("IfcCovering") and
|
||||
# AuthoringData.data["predefined_type"] == "FLOORING" and
|
||||
AuthoringData.data["active_material_usage"] == "LAYER3"):
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_G")
|
||||
op = row.operator("bim.regen_selected_covering_object")
|
||||
|
||||
|
||||
|
||||
# elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
# row = cls.layout.row(align=True)
|
||||
# row.prop(data=cls.props, property="ceiling_height", text="ceiling height")
|
||||
# if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
# op = row.operator("bim.add_instance_ceiling_coverings_from_walls")
|
||||
# elif element and bpy.context.selected_objects and element.is_a("IfcSpace"):
|
||||
# op. = row.operator("bim.add_istance_flooring_from_spaces"):
|
||||
# else:
|
||||
# op = row.operator("bim.add_instance_ceiling_from_cursor")
|
||||
# op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
# op.from_invoke = True
|
||||
# if cls.props.relating_type_id.isnumeric():
|
||||
# op.relating_type_id = int(cls.props.relating_type_id)
|
||||
else:
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
op = row.operator("bim.add_constr_type_instance", text="Add")
|
||||
op.from_invoke = True
|
||||
if cls.props.relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(cls.props.relating_type_id)
|
||||
|
||||
@classmethod
|
||||
def draw_type_selection_interface(cls):
|
||||
# shared by both sidebar and header
|
||||
row = cls.layout.row(align=True)
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
row = cls.layout.row(align=True)
|
||||
row.label(text="", icon="FILE_3D")
|
||||
prop_with_search(row, cls.props, "relating_type_id", text="")
|
||||
row.operator("bim.launch_type_manager", icon="LIGHTPROBE_GRID", text="")
|
||||
else:
|
||||
row.label(text=f"No {AuthoringData.data['ifc_element_type']} Found", icon="ERROR")
|
||||
row = cls.layout.row()
|
||||
row.operator("bim.launch_type_manager", icon="LIGHTPROBE_GRID", text="Launch Type Manager")
|
||||
|
||||
@classmethod
|
||||
def draw_basic_bim_tool_interface(cls):
|
||||
cls.draw_type_selection_interface()
|
||||
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
if cls.props.ifc_class:
|
||||
box = cls.layout.box()
|
||||
if AuthoringData.data["type_thumbnail"]:
|
||||
box.template_icon(icon_value=AuthoringData.data["type_thumbnail"], scale=5)
|
||||
else:
|
||||
op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
|
||||
op.ifc_class = cls.props.ifc_class
|
||||
|
||||
|
||||
class Hotkey(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.covering_hotkey"
|
||||
bl_label = "Hotkey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
hotkey: bpy.props.StringProperty()
|
||||
description: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get()
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
# self.props = context.scene.BIMCoveringProperties
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
|
||||
def invoke(self, context, event):
|
||||
# https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey
|
||||
# self.props = context.scene.BIMSpatialProperties
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
def hotkey_S_A(self):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
collection = bpy.context.view_layer.active_layer_collection.collection
|
||||
collection_obj = collection.BIMCollectionProperties.obj
|
||||
|
||||
if AuthoringData.data["predefined_type"] == "FLOORING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.add_instance_flooring_coverings_from_walls()
|
||||
elif tool.Ifc.get_entity(collection_obj):
|
||||
bpy.ops.bim.add_instance_flooring_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
elif AuthoringData.data["predefined_type"] == "CEILING":
|
||||
if element and bpy.context.selected_objects and element.is_a("IfcWall"):
|
||||
bpy.ops.bim.add_instance_ceiling_coverings_from_walls()
|
||||
elif tool.Ifc.get_entity(collection_obj):
|
||||
bpy.ops.bim.add_instance_ceiling_covering_from_cursor()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
else:
|
||||
bpy.ops.bim.add_constr_type_instance()
|
||||
|
||||
def hotkey_S_G(self):
|
||||
active_obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
if (element and
|
||||
bpy.context.selected_objects and
|
||||
element.is_a("IfcCovering") and
|
||||
AuthoringData.data["active_material_usage"] == "LAYER3"):
|
||||
bpy.ops.bim.regen_selected_covering_object()
|
||||
|
||||
@@ -21,14 +21,15 @@ from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddCsvAttribute,
|
||||
operator.RemoveCsvAttribute,
|
||||
operator.RemoveAllCsvAttributes,
|
||||
operator.ExportIfcCsv,
|
||||
operator.ImportIfcCsv,
|
||||
operator.EyedropIfcCsv,
|
||||
operator.SelectCsvIfcFile,
|
||||
operator.ImportCsvAttributes,
|
||||
operator.ExportCsvAttributes,
|
||||
operator.ExportIfcCsv,
|
||||
operator.ImportCsvAttributes,
|
||||
operator.ImportIfcCsv,
|
||||
operator.RemoveAllCsvAttributes,
|
||||
operator.RemoveCsvAttribute,
|
||||
operator.ReorderCsvAttribute,
|
||||
operator.SelectCsvIfcFile,
|
||||
prop.CsvAttribute,
|
||||
prop.CsvProperties,
|
||||
ui.BIM_PT_ifccsv,
|
||||
)
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifccsv
|
||||
import ifcopenshell
|
||||
import os
|
||||
import logging
|
||||
import bpy
|
||||
import json
|
||||
import webbrowser
|
||||
import ifccsv
|
||||
import logging
|
||||
import tempfile
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.handler import purge_module_data
|
||||
from blenderbim.bim.handler import refresh_ui_data
|
||||
|
||||
|
||||
class AddCsvAttribute(bpy.types.Operator):
|
||||
@@ -61,28 +62,57 @@ class RemoveAllCsvAttributes(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ReorderCsvAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.reorder_csv_attribute"
|
||||
bl_label = "Reorder CSV Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
old_index: bpy.props.IntProperty()
|
||||
new_index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
old = context.scene.CsvProperties.csv_attributes[self.old_index]
|
||||
new = context.scene.CsvProperties.csv_attributes[self.new_index]
|
||||
props = ["name", "header", "sort", "group", "varies_value", "summary"]
|
||||
for prop in props:
|
||||
value = getattr(new, prop)
|
||||
setattr(new, prop, getattr(old, prop))
|
||||
setattr(old, prop, value)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportCsvAttributes(bpy.types.Operator):
|
||||
bl_idname = "bim.import_csv_attributes"
|
||||
bl_label = "Import CSV Template"
|
||||
bl_label = "Load CSV Settings"
|
||||
bl_description = "Import a json template for CSV export"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
csv_props = context.scene.CsvProperties
|
||||
csv_json = json.load(open(self.filepath))
|
||||
props = context.scene.CsvProperties
|
||||
data = json.load(open(self.filepath))
|
||||
tool.Search.import_filter_query(data["query"], props.filter_groups)
|
||||
|
||||
expression = csv_json.get("expression", "")
|
||||
if expression:
|
||||
csv_props.ifc_selector = expression
|
||||
|
||||
attributes = csv_json.get("attributes", [])
|
||||
if attributes:
|
||||
csv_props.csv_attributes.clear()
|
||||
for attribute in attributes:
|
||||
csv_props.csv_attributes.add().name = attribute
|
||||
for prop in [
|
||||
"should_generate_svg",
|
||||
"should_preserve_existing",
|
||||
"include_global_id",
|
||||
"null_value",
|
||||
"empty_value",
|
||||
"true_value",
|
||||
"false_value",
|
||||
"concat_value",
|
||||
"csv_delimiter",
|
||||
"format",
|
||||
"csv_custom_delimiter",
|
||||
]:
|
||||
setattr(props, prop, data["settings"][prop])
|
||||
|
||||
props.csv_attributes.clear()
|
||||
for attribute in data["attributes"]:
|
||||
new = props.csv_attributes.add()
|
||||
for prop in ["name", "header", "sort", "group", "summary", "formatting"]:
|
||||
setattr(new, prop, attribute[prop])
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -92,7 +122,7 @@ class ImportCsvAttributes(bpy.types.Operator):
|
||||
|
||||
class ExportCsvAttributes(bpy.types.Operator):
|
||||
bl_idname = "bim.export_csv_attributes"
|
||||
bl_label = "Export CSV Template"
|
||||
bl_label = "Save CSV Settings"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Save a json template for CSV export"
|
||||
filename_ext = ".json"
|
||||
@@ -100,22 +130,42 @@ class ExportCsvAttributes(bpy.types.Operator):
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
csv_props = context.scene.CsvProperties
|
||||
props = context.scene.CsvProperties
|
||||
|
||||
csv_template = {}
|
||||
expression = csv_props.ifc_selector
|
||||
if expression:
|
||||
csv_template["expression"] = expression
|
||||
settings = {}
|
||||
for prop in [
|
||||
"should_generate_svg",
|
||||
"should_preserve_existing",
|
||||
"include_global_id",
|
||||
"null_value",
|
||||
"empty_value",
|
||||
"true_value",
|
||||
"false_value",
|
||||
"concat_value",
|
||||
"csv_delimiter",
|
||||
"format",
|
||||
"csv_custom_delimiter",
|
||||
]:
|
||||
settings[prop] = getattr(props, prop)
|
||||
|
||||
csv_attributes = []
|
||||
for attribute in csv_props.csv_attributes:
|
||||
attribute_name = attribute.name
|
||||
csv_attributes.append(attribute_name)
|
||||
if csv_attributes:
|
||||
csv_template["attributes"] = csv_attributes
|
||||
data = {
|
||||
"query": tool.Search.export_filter_query(props.filter_groups),
|
||||
"attributes": [
|
||||
{
|
||||
"name": a.name,
|
||||
"header": a.header,
|
||||
"sort": a.sort,
|
||||
"group": a.group,
|
||||
"summary": a.summary,
|
||||
"formatting": a.formatting,
|
||||
}
|
||||
for a in props.csv_attributes
|
||||
],
|
||||
"settings": settings,
|
||||
}
|
||||
|
||||
with open(self.filepath, "w") as outfile:
|
||||
json.dump(csv_template, outfile)
|
||||
json.dump(data, outfile)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -129,9 +179,8 @@ class ExportCsvAttributes(bpy.types.Operator):
|
||||
class ExportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.export_ifccsv"
|
||||
bl_label = "Export IFC"
|
||||
#filename_ext = ".csv"
|
||||
filename_ext = ".csv"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
props = context.scene.CsvProperties
|
||||
@@ -149,19 +198,61 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
ifc_file = IfcStore.get_file()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
results = selector.parse(ifc_file, props.ifc_selector)
|
||||
results = ifcopenshell.util.selector.filter_elements(
|
||||
ifc_file, tool.Search.export_filter_query(props.filter_groups)
|
||||
)
|
||||
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
headers = [a.header for a in props.csv_attributes]
|
||||
|
||||
sort = []
|
||||
groups = []
|
||||
summaries = []
|
||||
formatting = []
|
||||
for attribute in props.csv_attributes:
|
||||
if attribute.sort != "NONE":
|
||||
sort.append({"name": attribute.name, "order": attribute.sort})
|
||||
if attribute.group != "NONE":
|
||||
groups.append({"name": attribute.name, "type": attribute.group, "varies_value": attribute.varies_value})
|
||||
if attribute.summary != "NONE":
|
||||
summaries.append({"name": attribute.name, "type": attribute.summary})
|
||||
|
||||
if attribute.formatting != "{{value}}" and "{{value}}" in attribute.formatting:
|
||||
formatting.append({"name": attribute.name, "format": attribute.formatting})
|
||||
|
||||
sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter
|
||||
ifc_csv.export(ifc_file, results, attributes, output=self.filepath, format=props.format, delimiter=sep)
|
||||
ifc_csv.export(
|
||||
ifc_file,
|
||||
results,
|
||||
attributes,
|
||||
headers=headers,
|
||||
output=self.filepath,
|
||||
format=props.format,
|
||||
should_preserve_existing=props.should_preserve_existing,
|
||||
delimiter=sep,
|
||||
include_global_id=props.include_global_id,
|
||||
null=props.null_value,
|
||||
empty=props.empty_value,
|
||||
bool_true=props.true_value,
|
||||
bool_false=props.false_value,
|
||||
concat=props.concat_value,
|
||||
sort=sort,
|
||||
groups=groups,
|
||||
summaries=summaries,
|
||||
formatting=formatting,
|
||||
)
|
||||
|
||||
if props.format != "csv" and props.should_generate_svg:
|
||||
schedule_creator = scheduler.Scheduler()
|
||||
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.import_ifccsv"
|
||||
bl_label = "Import CSV to IFC"
|
||||
filename_ext = ".csv"
|
||||
bl_label = "Import to IFC"
|
||||
filter_glob: bpy.props.StringProperty(default="*.csv;*.ods;*.xlsx", options={"HIDDEN"})
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -180,25 +271,20 @@ class ImportIfcCsv(bpy.types.Operator):
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter
|
||||
ifc_csv.Import(ifc_file, self.filepath, delimiter=sep)
|
||||
attributes = [a.name for a in props.csv_attributes]
|
||||
ifc_csv.Import(
|
||||
ifc_file,
|
||||
self.filepath,
|
||||
attributes=attributes,
|
||||
delimiter=sep,
|
||||
null=props.null_value,
|
||||
empty=props.empty_value,
|
||||
bool_true=props.true_value,
|
||||
bool_false=props.false_value,
|
||||
)
|
||||
if not props.should_load_from_memory:
|
||||
ifc_file.write(props.csv_ifc_file)
|
||||
purge_module_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EyedropIfcCsv(bpy.types.Operator):
|
||||
bl_idname = "bim.eyedrop_ifccsv"
|
||||
bl_label = "Query Selected Items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
global_ids = []
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in context.selected_objects:
|
||||
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.ifc_definition_id:
|
||||
global_ids.append("#" + self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId)
|
||||
context.scene.CsvProperties.ifc_selector = "|".join(global_ids)
|
||||
refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty
|
||||
from blenderbim.bim.prop import StrProperty, BIMFilterGroup
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -31,10 +31,48 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
class CsvAttribute(PropertyGroup):
|
||||
name: StringProperty(name="Query", default="class")
|
||||
header: StringProperty(name="Header Value", default="IFC Class")
|
||||
sort: EnumProperty(items=[("NONE", "None", ""), ("ASC", "Ascending", ""), ("DESC", "Descending", "")])
|
||||
group: EnumProperty(
|
||||
items=[
|
||||
("NONE", "None", "Don't group any rows"),
|
||||
("GROUP", "Group", "All rows where this value is identical will be merged"),
|
||||
("CONCAT", "Concatenation", "Concatenate values if values vary within a group"),
|
||||
("VARIES", "Varies", "Show a custom value if values vary within a group"),
|
||||
("SUM", "Sum", "Sums the total value of rows in a group"),
|
||||
("AVERAGE", "Average", "Averages the total value of rows in a group"),
|
||||
("MIN", "Min", "Gets the minimum value of rows in a group"),
|
||||
("MAX", "Max", "Gets the maximum value of rows in a group"),
|
||||
]
|
||||
)
|
||||
varies_value: StringProperty(default="Varies", name="Varies Value")
|
||||
summary: EnumProperty(
|
||||
items=[
|
||||
("NONE", "None", "Don't provide a summary row"),
|
||||
("SUM", "Sum", "Sums the total value of all rows"),
|
||||
("AVERAGE", "Average", "Averages the total value of all rows"),
|
||||
("MIN", "Min", "Gets the minimum value of all rows"),
|
||||
("MAX", "Max", "Gets the maximum value of all rows"),
|
||||
]
|
||||
)
|
||||
formatting: StringProperty(default="{{value}}", name="Formatting")
|
||||
|
||||
|
||||
class CsvProperties(PropertyGroup):
|
||||
csv_ifc_file: StringProperty(default="", name="IFC File")
|
||||
ifc_selector: StringProperty(default="", name="IFC Selector")
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty)
|
||||
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
|
||||
csv_attributes: CollectionProperty(name="CSV Attributes", type=CsvAttribute)
|
||||
should_generate_svg: BoolProperty(default=False, name="Generate SVG")
|
||||
should_preserve_existing: BoolProperty(default=False, name="Preserve Existing")
|
||||
include_global_id: BoolProperty(default=True, name="Include GlobalId")
|
||||
null_value: StringProperty(default="N/A", name="Null Value")
|
||||
empty_value: StringProperty(default="-", name="Empty String Value")
|
||||
true_value: StringProperty(default="YES", name="True Value")
|
||||
false_value: StringProperty(default="NO", name="False Value")
|
||||
concat_value: StringProperty(default=", ", name="Concat Value")
|
||||
csv_delimiter: EnumProperty(
|
||||
items=[
|
||||
(";", ";", ""),
|
||||
@@ -55,4 +93,9 @@ class CsvProperties(PropertyGroup):
|
||||
default="csv",
|
||||
)
|
||||
csv_custom_delimiter: StringProperty(default="", name="Custom Delimiter")
|
||||
should_show_settings: BoolProperty(default=False, name="Show Settings")
|
||||
should_show_sort: BoolProperty(default=False, name="Show Sorting")
|
||||
should_show_group: BoolProperty(default=False, name="Show Grouping")
|
||||
should_show_summary: BoolProperty(default=False, name="Show Summary")
|
||||
should_show_formatting: BoolProperty(default=False, name="Show Formatting")
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.search.data import SearchData
|
||||
|
||||
|
||||
class BIM_PT_ifccsv(Panel):
|
||||
bl_label = "CSV Import/Export"
|
||||
bl_label = "Spreadsheet Import/Export"
|
||||
bl_idname = "BIM_PT_ifccsv"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
@@ -36,45 +38,88 @@ class BIM_PT_ifccsv(Panel):
|
||||
props = scene.CsvProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
row = layout.row()
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "should_load_from_memory")
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT", text="")
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT", text="")
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT", text="")
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT", text="")
|
||||
row.prop(props, "should_show_settings", icon="PREFERENCES", text="")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_ifc_file")
|
||||
row.operator("bim.select_csv_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "ifc_selector")
|
||||
row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="")
|
||||
layout.separator()
|
||||
if props.should_show_settings:
|
||||
layout.use_property_split = True
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "format")
|
||||
|
||||
if props.format == "csv":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_delimiter")
|
||||
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_custom_delimiter")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "should_generate_svg")
|
||||
row = layout.row()
|
||||
row.prop(props, "should_preserve_existing")
|
||||
row = layout.row()
|
||||
row.prop(props, "include_global_id")
|
||||
row = layout.row()
|
||||
row.prop(props, "null_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "empty_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "true_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "false_value")
|
||||
row = layout.row()
|
||||
row.prop(props, "concat_value")
|
||||
layout.use_property_split = False
|
||||
|
||||
blenderbim.bim.helper.draw_filter(self.layout, props.filter_groups, SearchData, "csv")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.add_csv_attribute", icon="ADD")
|
||||
row.operator("bim.remove_all_csv_attributes", icon="CANCEL")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT")
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT")
|
||||
row.prop(props, "should_show_sort", icon="SORTSIZE", text="")
|
||||
row.prop(props, "should_show_group", icon="OUTLINER_COLLECTION", text="")
|
||||
row.prop(props, "should_show_summary", icon="SYNTAX_ON", text="")
|
||||
row.prop(props, "should_show_formatting", icon="CON_TRANSLIKE", text="")
|
||||
|
||||
total = len(props.csv_attributes)
|
||||
for index, attribute in enumerate(props.csv_attributes):
|
||||
row = layout.row(align=True)
|
||||
row.prop(attribute, "name", text="")
|
||||
row.prop(attribute, "header", text="")
|
||||
if props.should_show_sort:
|
||||
row.prop(attribute, "sort", text="")
|
||||
if props.should_show_group:
|
||||
row.prop(attribute, "group", text="")
|
||||
if attribute.group == "VARIES":
|
||||
row.prop(attribute, "varies_value", text="")
|
||||
if props.should_show_summary:
|
||||
row.prop(attribute, "summary", text="")
|
||||
if props.should_show_formatting:
|
||||
row.prop(attribute, "formatting", text="")
|
||||
if total > 1:
|
||||
if index != 0:
|
||||
op = row.operator(f"bim.reorder_csv_attribute", icon="TRIA_UP", text="")
|
||||
op.old_index = index
|
||||
op.new_index = index - 1
|
||||
if index + 1 != total:
|
||||
op = row.operator(f"bim.reorder_csv_attribute", icon="TRIA_DOWN", text="")
|
||||
op.old_index = index
|
||||
op.new_index = index + 1
|
||||
row.operator("bim.remove_csv_attribute", icon="X", text="").index = index
|
||||
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "format")
|
||||
|
||||
if props.format == 'csv':
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_delimiter")
|
||||
|
||||
if props.csv_delimiter == "CUSTOM":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_custom_delimiter")
|
||||
|
||||
row = layout.row()
|
||||
split = row.split(factor=0.5)
|
||||
c = split.column()
|
||||
c.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper())
|
||||
c = split.column()
|
||||
c.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
row.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper())
|
||||
row.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
|
||||
@@ -20,23 +20,26 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.ConvertToBlender,
|
||||
operator.CopyDebugInformation,
|
||||
operator.CreateAllShapes,
|
||||
operator.CreateShapeFromStepId,
|
||||
operator.InspectFromObject,
|
||||
operator.InspectFromStepId,
|
||||
operator.OverrideDisplayType,
|
||||
operator.ParseExpress,
|
||||
operator.PrintIfcFile,
|
||||
operator.PrintObjectPlacement,
|
||||
operator.ProfileImportIFC,
|
||||
operator.PurgeHdf5Cache,
|
||||
operator.PurgeIfcLinks,
|
||||
operator.ConvertToBlender,
|
||||
operator.RewindInspector,
|
||||
operator.SelectExpressFile,
|
||||
operator.SelectHighPolygonMeshes,
|
||||
operator.SelectHighestPolygonMeshes,
|
||||
operator.ValidateIfcFile,
|
||||
operator.PrintUnusedElementStats,
|
||||
operator.PurgeUnusedElementsByClass,
|
||||
prop.BIMDebugProperties,
|
||||
ui.BIM_PT_debug,
|
||||
)
|
||||
|
||||
@@ -116,7 +116,7 @@ class PurgeIfcLinks(bpy.types.Operator):
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
IfcStore.purge()
|
||||
blenderbim.bim.handler.purge_module_data()
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
m.BIMMaterialProperties.ifc_style_id = False
|
||||
bpy.context.scene.BIMProperties.ifc_file = ""
|
||||
IfcStore.purge()
|
||||
blenderbim.bim.handler.purge_module_data()
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -146,14 +146,33 @@ class ValidateIfcFile(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
import ifcopenshell.validate
|
||||
|
||||
class LogDetectionHandler(logging.Handler):
|
||||
message_logged = False
|
||||
|
||||
def emit(self, record):
|
||||
if not self.message_logged:
|
||||
self.message_logged = True
|
||||
|
||||
logger = logging.getLogger("validate")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
ifcopenshell.validate.validate(IfcStore.get_file(), logger, express_rules=True)
|
||||
|
||||
# use LogDetectionHandler to check whether there were any validation errors
|
||||
# during `ifcopenshell.validate.validate`
|
||||
handler = LogDetectionHandler()
|
||||
logger.addHandler(handler)
|
||||
ifcopenshell.validate.validate(tool.Ifc.get(), logger, express_rules=True)
|
||||
logger.removeHandler(handler)
|
||||
|
||||
if handler.message_logged:
|
||||
self.report({"INFO"}, "Check validation results in the system console.")
|
||||
else:
|
||||
self.report({"INFO"}, "No validation issues found.")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -194,7 +213,7 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
|
||||
failures = []
|
||||
excludes = () # For the developer to debug with
|
||||
for i, element in enumerate(elements):
|
||||
for i, element in enumerate(elements, 1):
|
||||
if element.GlobalId in excludes:
|
||||
continue
|
||||
print(f"{i}/{total}:", element)
|
||||
@@ -216,7 +235,7 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
len(shape.geometry.edges),
|
||||
len(shape.geometry.faces),
|
||||
)
|
||||
print(f"Failures: {len(failures)}")
|
||||
self.report({"INFO"}, f"Failed shapes: {len(failures)}, check the system console for details.")
|
||||
for failure in failures:
|
||||
print(failure)
|
||||
return {"FINISHED"}
|
||||
@@ -441,3 +460,166 @@ class PurgeHdf5Cache(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
core.purge_hdf5_cache(tool.Debug)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OverrideDisplayType(bpy.types.Operator):
|
||||
bl_idname = "bim.override_display_type"
|
||||
bl_label = "Override Display Type"
|
||||
display: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
obj.display_type = self.display
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class PrintUnusedElementStats(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.print_unused_elements_stats"
|
||||
bl_label = "Print Unused Elements Stats"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Print all unused elements in current IFC project in system console, not limited to the selected class"
|
||||
)
|
||||
|
||||
ignore_contexts: bpy.props.BoolProperty(name="Ignore Contexts", default=True)
|
||||
ignore_relationships: bpy.props.BoolProperty(name="Ignore Relationships", default=True)
|
||||
ignore_types: bpy.props.BoolProperty(name="Ignore Types", default=True)
|
||||
ignore_styled_items: bpy.props.BoolProperty(name="Ignoer Styled Items", default=True)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
# ignore some classes that could have zero 0 inverse references by their nature
|
||||
ignore_classes = []
|
||||
if self.ignore_contexts:
|
||||
ignore_classes += ["IfcRepresentationContext"]
|
||||
if self.ignore_relationships:
|
||||
ignore_classes += ["IfcRelationship"]
|
||||
if self.ignore_types:
|
||||
ignore_classes += ["IfcTypeProduct"]
|
||||
if self.ignore_styled_items:
|
||||
ignore_classes += ["IfcStyledItem"]
|
||||
|
||||
unused_elements = tool.Debug.print_unused_elements_stats(props.ifc_class_purge, ignore_classes)
|
||||
self.report({"INFO"}, f"{unused_elements} unused elements found, check the system console for the details.")
|
||||
|
||||
|
||||
class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.purge_unused_elements_by_class"
|
||||
bl_label = "Purge Unused Elements By Class"
|
||||
bl_description = (
|
||||
"Will find all elements of class that have no inverse refernces and will remove them, use very carefully"
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
if props.ifc_class_purge:
|
||||
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
|
||||
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
|
||||
tool.Ifc.get().write(self.filepath)
|
||||
return
|
||||
|
||||
# A whitelisted class is a class that only contains simple data that
|
||||
# has no meaning by itself (only has meaning when combined with other
|
||||
# data). E.g. a colour by itself has no meaning so is whitelisted, but
|
||||
# a material by itself may be part of your materials library.
|
||||
|
||||
# There are classes I don't know enough about to decide. Not whitelisting in case.
|
||||
# IfcAlignmentParameterSegment
|
||||
# IfcBoundaryCondition
|
||||
# IfcStructuralConnectionCondition
|
||||
# IfcStructuralLoad
|
||||
|
||||
whitelisted_classes = [
|
||||
"IfcActorRole",
|
||||
"IfcAddress",
|
||||
"IfcApplication",
|
||||
"IfcAppliedValue",
|
||||
"IfcConnectionGeometry",
|
||||
"IfcCoordinateReferenceSystem",
|
||||
"IfcDerivedUnit",
|
||||
"IfcDerivedUnitElement",
|
||||
"IfcDimensionalExponents",
|
||||
"IfcExternalReference",
|
||||
"IfcGridAxis",
|
||||
"IfcIrregularTimeSeriesValue",
|
||||
"IfcLightDistributionData",
|
||||
"IfcLightIntensityDistribution",
|
||||
"IfcMeasureWithUnit",
|
||||
"IfcMonetaryUnit",
|
||||
"IfcNamedUnit",
|
||||
"IfcObjectPlacement",
|
||||
"IfcOrganization", # Should be referenced as part of an actor
|
||||
"IfcOwnerHistory",
|
||||
"IfcPerson", # Should be referenced as part of an actor
|
||||
"IfcPersonAndOrganization", # Should be referenced as part of an actor
|
||||
"IfcPhysicalQuantity",
|
||||
"IfcPresentationItem",
|
||||
"IfcProductDefinitionShape",
|
||||
"IfcPropertyAbstraction",
|
||||
"IfcRecurrencePattern",
|
||||
"IfcReference",
|
||||
"IfcRepresentation",
|
||||
"IfcRepresentationContext",
|
||||
"IfcRepresentationItem",
|
||||
"IfcRepresentationMap",
|
||||
"IfcPropertyDefinition", # A bit of a questionable one, and the odd one out from IfcRoot.
|
||||
"IfcSchedulingTime",
|
||||
"IfcShapeAspect",
|
||||
"IfcTable",
|
||||
"IfcTableColumn",
|
||||
"IfcTableRow",
|
||||
"IfcTextureCoordinateIndices",
|
||||
"IfcTimePeriod",
|
||||
"IfcTimeSeries",
|
||||
"IfcTimeSeriesValue",
|
||||
"IfcUnitAssignment",
|
||||
"IfcVirtualGridIntersection",
|
||||
]
|
||||
|
||||
total_purged = 0
|
||||
while True:
|
||||
total_batches = 0
|
||||
print("*" * 100)
|
||||
total_batch_purged = 0
|
||||
for ifc_class in whitelisted_classes:
|
||||
total_class_purged = 0
|
||||
try:
|
||||
elements = tool.Ifc.get().by_type(ifc_class)
|
||||
except:
|
||||
continue # Probably not in this schema?
|
||||
to_purge = set()
|
||||
for element in elements:
|
||||
try:
|
||||
if ifc_class == "IfcRepresentationItem" and element.is_a("IfcStyledItem") and element.Item:
|
||||
continue
|
||||
except:
|
||||
to_purge.add(element.id()) # It's invalid, definitely purge it.
|
||||
if tool.Ifc.get().get_total_inverses(element) == 0:
|
||||
to_purge.add(element.id())
|
||||
for element_id in to_purge:
|
||||
try:
|
||||
element = tool.Ifc.get().by_id(element_id)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), element)
|
||||
total_class_purged += 1
|
||||
except:
|
||||
continue
|
||||
if total_class_purged > 0:
|
||||
total_batch_purged += total_class_purged
|
||||
print(f"Auto purged {total_class_purged} {ifc_class}")
|
||||
if total_batch_purged > 0:
|
||||
total_purged += total_batch_purged
|
||||
print(f"Auto purged in batch: {total_batch_purged}")
|
||||
total_batches += 1
|
||||
if total_batch_purged == 0:
|
||||
break
|
||||
elif total_batches > 20:
|
||||
print("Finished 20 batches. Manually stopping in case of infinite loop.")
|
||||
self.report({"INFO"}, f"Auto purged {total_purged} orphaned elements")
|
||||
tool.Ifc.get().write(self.filepath)
|
||||
|
||||
@@ -40,3 +40,14 @@ class BIMDebugProperties(PropertyGroup):
|
||||
inverse_attributes: CollectionProperty(name="Inverse Attributes", type=Attribute)
|
||||
inverse_references: CollectionProperty(name="Inverse References", type=Attribute)
|
||||
express_file: StringProperty(name="Express File")
|
||||
display_type: EnumProperty(
|
||||
items=[
|
||||
("BOUNDS", "Bounds", ""),
|
||||
("WIRE", "Wire", ""),
|
||||
("SOLID", "Solid", ""),
|
||||
("TEXTURED", "Textured", ""),
|
||||
],
|
||||
name="Display Type",
|
||||
default="BOUNDS",
|
||||
)
|
||||
ifc_class_purge: StringProperty(name="Unused Elements IFC Class", default="")
|
||||
|
||||
@@ -83,6 +83,17 @@ class BIM_PT_debug(Panel):
|
||||
).percentile = context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
row.prop(props, "percentile_of_polygons", text="")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
row.prop(props, "display_type", text="")
|
||||
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
|
||||
|
||||
layout.operator("bim.purge_unused_representations")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
|
||||
row.operator("bim.purge_unused_elements_by_class", text="Purge Orphaned", icon="TRASH")
|
||||
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
row = layout.row()
|
||||
|
||||
@@ -20,11 +20,12 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.SelectDiffJsonFile,
|
||||
operator.VisualiseDiff,
|
||||
operator.SelectDiffOldFile,
|
||||
operator.SelectDiffNewFile,
|
||||
operator.ExecuteIfcDiff,
|
||||
operator.SelectDiffJsonFile,
|
||||
operator.SelectDiffNewFile,
|
||||
operator.SelectDiffObjects,
|
||||
operator.SelectDiffOldFile,
|
||||
operator.VisualiseDiff,
|
||||
prop.Relationships,
|
||||
prop.DiffProperties,
|
||||
ui.BIM_PT_diff,
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
|
||||
import bpy
|
||||
import json
|
||||
import ifccsv
|
||||
import logging
|
||||
import ifcopenshell
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class SelectDiffJsonFile(bpy.types.Operator):
|
||||
@@ -51,8 +52,37 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
diff = json.load(file)
|
||||
for obj in context.visible_objects:
|
||||
obj.color = (1.0, 1.0, 1.0, 1.0)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
|
||||
if "IfcDiff Deleted Elements" in obj.users_collection[0].name:
|
||||
obj.color = (1.0, 0.0, 0.0, 1.0)
|
||||
continue
|
||||
elif "IfcDiff Added Elements" in obj.users_collection[0].name:
|
||||
obj.color = (0.0, 1.0, 0.0, 1.0)
|
||||
continue
|
||||
elif "IfcDiff Changed Elements" in obj.users_collection[0].name:
|
||||
obj.color = (0.0, 0.0, 0.7, 1.0)
|
||||
continue
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
if ifc_file:
|
||||
if ifc_file not in IfcStore.session_files:
|
||||
IfcStore.session_files[ifc_file] = ifcopenshell.open(ifc_file)
|
||||
element_file = IfcStore.session_files[ifc_file]
|
||||
else:
|
||||
element_file = ifc_file
|
||||
|
||||
try:
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
except:
|
||||
continue
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if not global_id:
|
||||
@@ -115,14 +145,140 @@ class ExecuteIfcDiff(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
import ifcdiff
|
||||
|
||||
old = ifcopenshell.open(context.scene.DiffProperties.old_file)
|
||||
new = ifcopenshell.open(context.scene.DiffProperties.new_file)
|
||||
relationships = [r.relationship for r in context.scene.DiffProperties.diff_relationships]
|
||||
query = context.scene.DiffProperties.diff_filter_elements
|
||||
self.props = context.scene.DiffProperties
|
||||
|
||||
if tool.Ifc.get():
|
||||
if self.props.active_file == "NONE":
|
||||
old = ifcopenshell.open(self.props.old_file)
|
||||
new = ifcopenshell.open(self.props.new_file)
|
||||
elif self.props.active_file == "NEW":
|
||||
old = ifcopenshell.open(self.props.old_file)
|
||||
new = tool.Ifc.get()
|
||||
elif self.props.active_file == "OLD":
|
||||
old = tool.Ifc.get()
|
||||
new = ifcopenshell.open(self.props.new_file)
|
||||
else:
|
||||
old = ifcopenshell.open(self.props.old_file)
|
||||
new = ifcopenshell.open(self.props.new_file)
|
||||
|
||||
relationships = [r.relationship for r in self.props.diff_relationships]
|
||||
query = self.props.diff_filter_elements
|
||||
|
||||
ifc_diff = ifcdiff.IfcDiff(old, new, relationships=relationships, filter_elements=query)
|
||||
ifc_diff.diff()
|
||||
ifc_diff.export(self.filepath)
|
||||
context.scene.DiffProperties.diff_json_file = self.filepath
|
||||
self.props.diff_json_file = self.filepath
|
||||
|
||||
self.load_changed_elements(ifc_diff)
|
||||
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
def load_changed_elements(self, ifc_diff):
|
||||
if not tool.Ifc.get() or self.props.active_file == "NONE" or not self.props.should_load_changed_elements:
|
||||
return
|
||||
|
||||
active_ifc = tool.Ifc.get()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = blenderbim.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
|
||||
ifc_importer = blenderbim.bim.import_ifc.IfcImporter(ifc_import_settings)
|
||||
|
||||
if self.props.active_file == "NEW":
|
||||
tool.Ifc.set(ifc_diff.old)
|
||||
ifc_importer.file = ifc_diff.old
|
||||
ifc_importer.process_context_filter()
|
||||
ifc_importer.create_materials()
|
||||
ifc_importer.create_styles()
|
||||
|
||||
elements = {ifc_diff.old.by_guid(guid) for guid in ifc_diff.deleted_elements}
|
||||
ifc_importer.create_generic_elements(elements)
|
||||
self.place_objs_in_collection(ifc_importer.added_data.values(), "IfcDiff Deleted Elements")
|
||||
|
||||
ifc_importer.added_data = {}
|
||||
|
||||
elements = {ifc_diff.old.by_guid(guid) for guid in ifc_diff.change_register.keys()}
|
||||
ifc_importer.create_generic_elements(elements)
|
||||
self.place_objs_in_collection(ifc_importer.added_data.values(), "IfcDiff Changed Elements")
|
||||
elif self.props.active_file == "OLD":
|
||||
tool.Ifc.set(ifc_diff.new)
|
||||
ifc_importer.file = ifc_diff.new
|
||||
ifc_importer.process_context_filter()
|
||||
ifc_importer.create_materials()
|
||||
ifc_importer.create_styles()
|
||||
|
||||
elements = {ifc_diff.new.by_guid(guid) for guid in ifc_diff.added_elements}
|
||||
ifc_importer.create_generic_elements(elements)
|
||||
self.place_objs_in_collection(ifc_importer.added_data.values(), "IfcDiff Added Elements")
|
||||
|
||||
ifc_importer.added_data = {}
|
||||
|
||||
elements = {ifc_diff.new.by_guid(guid) for guid in ifc_diff.change_register.keys()}
|
||||
ifc_importer.create_generic_elements(elements)
|
||||
self.place_objs_in_collection(ifc_importer.added_data.values(), "IfcDiff Changed Elements")
|
||||
tool.Ifc.set(active_ifc)
|
||||
|
||||
def place_objs_in_collection(self, objs, name):
|
||||
collection = bpy.data.collections.get(name)
|
||||
if not collection:
|
||||
collection = bpy.data.collections.new(name)
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
|
||||
for obj in objs:
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
collection.objects.link(obj)
|
||||
|
||||
|
||||
class SelectDiffObjects(bpy.types.Operator):
|
||||
bl_idname = "bim.select_diff_objects"
|
||||
bl_label = "Select Diff Objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
mode: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
with open(context.scene.DiffProperties.diff_json_file, "r") as file:
|
||||
diff = json.load(file)
|
||||
for obj in context.visible_objects:
|
||||
obj.select_set(False)
|
||||
|
||||
if self.mode == "DELETED" and "IfcDiff Deleted Elements" in obj.users_collection[0].name:
|
||||
obj.select_set(True)
|
||||
continue
|
||||
elif self.mode == "ADDED" and "IfcDiff Added Elements" in obj.users_collection[0].name:
|
||||
obj.select_set(True)
|
||||
continue
|
||||
elif self.mode == "CHANGED" and "IfcDiff Changed Elements" in obj.users_collection[0].name:
|
||||
obj.select_set(True)
|
||||
continue
|
||||
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
if ifc_file:
|
||||
if ifc_file not in IfcStore.session_files:
|
||||
IfcStore.session_files[ifc_file] = ifcopenshell.open(ifc_file)
|
||||
element_file = IfcStore.session_files[ifc_file]
|
||||
else:
|
||||
element_file = ifc_file
|
||||
|
||||
try:
|
||||
element = element_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
except:
|
||||
continue
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if not global_id:
|
||||
continue
|
||||
if self.mode == "DELETED" and global_id in diff["deleted"]:
|
||||
obj.select_set(True)
|
||||
elif self.mode == "ADDED" and global_id in diff["added"]:
|
||||
obj.select_set(True)
|
||||
elif self.mode == "CHANGED" and global_id in diff["changed"]:
|
||||
obj.select_set(True)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -49,3 +49,13 @@ class DiffProperties(PropertyGroup):
|
||||
new_file: StringProperty(default="", name="New IFC File")
|
||||
diff_relationships: CollectionProperty(type=Relationships, name="Relationships")
|
||||
diff_filter_elements: StringProperty(default="", name="Filter")
|
||||
should_load_changed_elements: BoolProperty(name="Load Changed Elements", default=True)
|
||||
active_file: EnumProperty(
|
||||
items=[
|
||||
("NONE", "N/A", ""),
|
||||
("OLD", "Old", ""),
|
||||
("NEW", "New", ""),
|
||||
],
|
||||
name="Active File",
|
||||
default="NEW",
|
||||
)
|
||||
|
||||
@@ -44,13 +44,38 @@ class BIM_PT_diff(Panel):
|
||||
|
||||
layout.label(text="IFC Diff Setup:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "old_file")
|
||||
row.operator("bim.select_diff_old_file", icon="FILE_FOLDER", text="")
|
||||
if tool.Ifc.get():
|
||||
row = layout.row()
|
||||
row.prop(props, "active_file")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "new_file")
|
||||
row.operator("bim.select_diff_new_file", icon="FILE_FOLDER", text="")
|
||||
if props.active_file == "NONE":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "old_file")
|
||||
row.operator("bim.select_diff_old_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "new_file")
|
||||
row.operator("bim.select_diff_new_file", icon="FILE_FOLDER", text="")
|
||||
elif props.active_file == "NEW":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "old_file")
|
||||
row.operator("bim.select_diff_old_file", icon="FILE_FOLDER", text="")
|
||||
elif props.active_file == "OLD":
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "new_file")
|
||||
row.operator("bim.select_diff_new_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
if props.active_file != "NONE":
|
||||
row = layout.row()
|
||||
row.prop(props, "should_load_changed_elements")
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "old_file")
|
||||
row.operator("bim.select_diff_old_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "new_file")
|
||||
row.operator("bim.select_diff_new_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "diff_relationships")
|
||||
@@ -82,10 +107,22 @@ class BIM_PT_diff(Panel):
|
||||
|
||||
if DiffData.data["diff_json"]:
|
||||
row = layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text=f"{DiffData.data['total_added']} added")
|
||||
row.label(text=f"{DiffData.data['total_deleted']} deleted")
|
||||
row.label(text=f"{DiffData.data['total_changed']} changed")
|
||||
col = row.column(align=True)
|
||||
col.operator(
|
||||
"bim.select_diff_objects",
|
||||
icon="RESTRICT_SELECT_OFF",
|
||||
text=f"Select {DiffData.data['total_added']} Added",
|
||||
).mode = "ADDED"
|
||||
col.operator(
|
||||
"bim.select_diff_objects",
|
||||
icon="RESTRICT_SELECT_OFF",
|
||||
text=f"Select {DiffData.data['total_deleted']} Deleted",
|
||||
).mode = "DELETED"
|
||||
col.operator(
|
||||
"bim.select_diff_objects",
|
||||
icon="RESTRICT_SELECT_OFF",
|
||||
text=f"Select {DiffData.data['total_changed']} Changed",
|
||||
).mode = "CHANGED"
|
||||
|
||||
if DiffData.data["changes"]:
|
||||
box = layout.box()
|
||||
|
||||
@@ -29,7 +29,7 @@ class BIM_PT_documents(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_project_setup"
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -30,11 +30,13 @@ classes = (
|
||||
operator.AddDrawingStyleAttribute,
|
||||
operator.AddDrawingToSheet,
|
||||
operator.AddReference,
|
||||
operator.AddReferenceImage,
|
||||
operator.AddReferenceToSheet,
|
||||
operator.AddSchedule,
|
||||
operator.AddScheduleToSheet,
|
||||
operator.AddSheet,
|
||||
operator.AddTextLiteral,
|
||||
operator.AssignSelectedObjectAsProduct,
|
||||
operator.BuildSchedule,
|
||||
operator.CleanWireframes,
|
||||
operator.ContractSheet,
|
||||
@@ -50,11 +52,13 @@ classes = (
|
||||
operator.DisableEditingText,
|
||||
operator.DuplicateDrawing,
|
||||
operator.EditAssignedProduct,
|
||||
operator.EditElementFilter,
|
||||
operator.EditSheet,
|
||||
operator.EditText,
|
||||
operator.EditTextPopup,
|
||||
operator.EnableAddAnnotationType,
|
||||
operator.EnableEditingAssignedProduct,
|
||||
operator.EnableEditingElementFilter,
|
||||
operator.EnableEditingText,
|
||||
operator.ExpandSheet,
|
||||
operator.ExpandTargetView,
|
||||
@@ -92,10 +96,11 @@ classes = (
|
||||
prop.BIMTextProperties,
|
||||
prop.BIMAssignedProductProperties,
|
||||
prop.BIMAnnotationProperties,
|
||||
ui.BIM_PT_camera,
|
||||
ui.BIM_PT_drawing_underlay,
|
||||
ui.BIM_PT_sheets,
|
||||
ui.BIM_PT_drawings,
|
||||
ui.BIM_PT_camera,
|
||||
ui.BIM_PT_element_filters,
|
||||
ui.BIM_PT_drawing_underlay,
|
||||
ui.BIM_PT_schedules,
|
||||
ui.BIM_PT_references,
|
||||
ui.BIM_PT_product_assignments,
|
||||
@@ -123,6 +128,7 @@ def register():
|
||||
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
|
||||
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
|
||||
bpy.app.handlers.depsgraph_update_pre.append(handler.depsgraph_update_pre_handler)
|
||||
bpy.types.VIEW3D_MT_image_add.append(ui.add_object_button)
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -136,3 +142,4 @@ def unregister():
|
||||
del bpy.types.TextCurve.BIMTextProperties
|
||||
bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad)
|
||||
bpy.app.handlers.depsgraph_update_pre.remove(handler.depsgraph_update_pre_handler)
|
||||
bpy.types.VIEW3D_MT_image_add.remove(ui.add_object_button)
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import os
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
import bpy
|
||||
import math
|
||||
import bmesh
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
|
||||
class Annotator:
|
||||
@@ -90,6 +92,15 @@ class Annotator:
|
||||
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_vertex_to_annotation(obj):
|
||||
verts_world_space = Annotator.get_placeholder_coords()
|
||||
vert_local = obj.matrix_world.inverted() @ verts_world_space[0]
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
|
||||
bm.verts.new(vert_local)
|
||||
tool.Blender.apply_bmesh(obj.data, bm, obj)
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_plane_to_annotation(obj, remove_face=False):
|
||||
# default order = bot left, top left, bot right, top right
|
||||
@@ -111,7 +122,7 @@ class Annotator:
|
||||
def get_annotation_obj(drawing, object_type, data_type):
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
|
||||
matrix_world = camera.matrix_world.copy()
|
||||
matrix_world = tool.Drawing.get_camera_matrix(camera)
|
||||
matrix_world.translation = co1
|
||||
collection = camera.BIMObjectProperties.collection
|
||||
|
||||
@@ -142,6 +153,8 @@ class Annotator:
|
||||
data = bpy.data.curves.new(object_type, type="CURVE")
|
||||
data.dimensions = "3D"
|
||||
data.resolution_u = 2
|
||||
elif data_type == "empty":
|
||||
data = None
|
||||
|
||||
obj = bpy.data.objects.new(object_type, data)
|
||||
obj.matrix_world = matrix_world
|
||||
@@ -154,6 +167,13 @@ class Annotator:
|
||||
camera = bpy.context.scene.camera
|
||||
|
||||
z_offset = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
|
||||
|
||||
if (
|
||||
ifcopenshell.util.element.get_pset(tool.Ifc.get_entity(camera), "EPset_Drawing", "TargetView")
|
||||
== "REFLECTED_PLAN_VIEW"
|
||||
):
|
||||
z_offset *= -1
|
||||
|
||||
y = camera.data.ortho_scale / 4
|
||||
|
||||
res_x = bpy.context.scene.render.resolution_x
|
||||
@@ -166,6 +186,7 @@ class Annotator:
|
||||
|
||||
center = camera.matrix_world.inverted() @ bpy.context.scene.cursor.location
|
||||
center.z = 0
|
||||
center = camera.matrix_world @ center
|
||||
|
||||
return (
|
||||
center + z_offset,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
@@ -29,6 +30,7 @@ def refresh():
|
||||
SheetsData.is_loaded = False
|
||||
DocumentsData.is_loaded = False
|
||||
DrawingsData.is_loaded = False
|
||||
ElementFiltersData.is_loaded = False
|
||||
AnnotationData.is_loaded = False
|
||||
DecoratorData.data = {}
|
||||
DecoratorData.cut_cache = {}
|
||||
@@ -123,6 +125,8 @@ class DrawingsData:
|
||||
[(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcBuildingStorey")]
|
||||
)
|
||||
return results
|
||||
elif bpy.context.scene.DocProperties.target_view in ["MODEL_VIEW"]:
|
||||
return [(h.upper(), h, "") for h in ["Orthographic", "Perspective"]]
|
||||
return [(h.upper(), h, "") for h in ["North", "South", "East", "West"]]
|
||||
|
||||
@classmethod
|
||||
@@ -135,6 +139,55 @@ class DrawingsData:
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing")
|
||||
|
||||
|
||||
class ElementFiltersData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"saved_searches": cls.saved_searches(),
|
||||
"has_include_filter": cls.has_include_filter(),
|
||||
"has_exclude_filter": cls.has_exclude_filter(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def saved_searches(cls):
|
||||
if not tool.Ifc.get():
|
||||
return []
|
||||
groups = tool.Ifc.get().by_type("IfcGroup")
|
||||
results = []
|
||||
for group in groups:
|
||||
try:
|
||||
data = json.loads(group.Description)
|
||||
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search" and data.get("query", None):
|
||||
results.append(group)
|
||||
except:
|
||||
pass
|
||||
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
|
||||
|
||||
@classmethod
|
||||
def has_include_filter(cls):
|
||||
obj = bpy.context.scene.camera
|
||||
if not obj:
|
||||
return
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
return bool(ifcopenshell.util.element.get_pset(element, "EPset_Drawing", "Include"))
|
||||
|
||||
@classmethod
|
||||
def has_exclude_filter(cls):
|
||||
obj = bpy.context.scene.camera
|
||||
if not obj:
|
||||
return
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
return bool(ifcopenshell.util.element.get_pset(element, "EPset_Drawing", "Exclude"))
|
||||
|
||||
|
||||
class DocumentsData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@@ -52,23 +52,23 @@ class profile_consequential:
|
||||
cls.test_name = test_name
|
||||
|
||||
@classmethod
|
||||
def log(cls):
|
||||
def log(cls, args=[]):
|
||||
if cls.start_time is not None:
|
||||
cls.lines.append(f"{cls.test_name}\t{timer() - cls.start_time:.10f}")
|
||||
args = "" if not args else "\t" + "\t".join(args)
|
||||
cls.lines.append(f"{cls.test_name}\t{timer() - cls.start_time:.10f}{args}")
|
||||
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
cls.log()
|
||||
cls.start_time = None
|
||||
print("\n".join(cls.lines))
|
||||
lines = "\n".join(cls.lines)
|
||||
print(lines)
|
||||
import pyperclip
|
||||
|
||||
pyperclip.copy(lines)
|
||||
cls.lines = []
|
||||
|
||||
|
||||
def ccw(A, B, C):
|
||||
"""whether a-b-c located in counter-clockwise order in 2d space"""
|
||||
return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)
|
||||
|
||||
|
||||
def worldspace_to_winspace(verts, context):
|
||||
"""Convert world space verts to window space"""
|
||||
region = context.region
|
||||
@@ -161,9 +161,9 @@ class BaseDecorator:
|
||||
os.path.join(bpy.context.scene.BIMProperties.data_dir, "fonts", "OpenGost Type B TT.ttf")
|
||||
)
|
||||
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
def get_camera_width_mm(self):
|
||||
# Horrific prototype code to ensure bgl draws at drawing scales
|
||||
@@ -248,8 +248,13 @@ class BaseDecorator:
|
||||
if check_mode and obj.data.is_editmode:
|
||||
return self.get_editmesh_geom(obj)
|
||||
|
||||
vertices = [obj.matrix_world @ v.co for v in obj.data.vertices]
|
||||
indices = [e.vertices for e in obj.data.edges]
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
vertices = [obj.matrix_world @ v.co for v in bm.verts]
|
||||
# In object mode, it's nicer to not show "internal edges". Most will be dissolved anyway.
|
||||
indices = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces) != 2]
|
||||
bm.free()
|
||||
|
||||
return vertices, indices
|
||||
|
||||
def get_editmesh_geom(self, obj):
|
||||
@@ -385,8 +390,6 @@ class BaseDecorator:
|
||||
# 0 is the default font, but we're fancier than that
|
||||
font_id = self.font_id
|
||||
|
||||
dpi = context.preferences.system.dpi
|
||||
|
||||
color = context.preferences.addons["blenderbim"].preferences.decorations_colour
|
||||
|
||||
ang = -Vector((1, 0)).angle_signed(text_dir)
|
||||
@@ -406,7 +409,7 @@ class BaseDecorator:
|
||||
font_size_px = int(0.004118616 * mm_to_px) * font_size_mm / 2.5
|
||||
pos = pos - line_no * font_size_px * rotation_matrix[1]
|
||||
|
||||
blf.size(font_id, font_size_px, dpi)
|
||||
blf.size(font_id, font_size_px)
|
||||
|
||||
if box_alignment or center or vcenter:
|
||||
w, h = blf.dimensions(font_id, text)
|
||||
@@ -520,7 +523,10 @@ class BaseDecorator:
|
||||
return matrix.inverted()[i].to_3d().normalized()
|
||||
|
||||
text_dir_world_x_axis = get_basis_vector(obj.matrix_world)
|
||||
text_dir = (camera.matrix_world.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
camera_matrix = tool.Drawing.get_camera_matrix(camera)
|
||||
|
||||
text_dir = (camera_matrix.inverted().to_quaternion() @ text_dir_world_x_axis).to_2d().normalized()
|
||||
|
||||
pos = location_3d_to_region_2d(region, region3d, text_world_position)
|
||||
props = obj.BIMTextProperties
|
||||
@@ -742,7 +748,7 @@ class AngleDecorator(BaseDecorator):
|
||||
except ZeroDivisionError:
|
||||
continue
|
||||
circle_angle = acos(cos_a)
|
||||
counter_clockwise = ccw(v2, v1, v0)
|
||||
counter_clockwise = tool.Cad.is_counter_clockwise_order(v2, v1, v0)
|
||||
angle_circle = get_angle_circle(circle_start, circle_angle, counter_clockwise)
|
||||
add_verts_sequence([v1 + v for v in angle_circle], start_i_arcs, **out_kwargs_arcs)
|
||||
|
||||
@@ -774,7 +780,7 @@ class AngleDecorator(BaseDecorator):
|
||||
region3d = context.region_data
|
||||
|
||||
viewportDrawingScale = self.get_viewport_drawing_scale(context)
|
||||
ANGLE_LABEL_OFFSET = 25 * viewportDrawingScale
|
||||
ANGLE_LABEL_OFFSET = 20 * viewportDrawingScale
|
||||
last_segment_i = len(indices) - 1
|
||||
|
||||
for edge_i, edge_vertices in enumerate(indices):
|
||||
@@ -792,24 +798,24 @@ class AngleDecorator(BaseDecorator):
|
||||
edge1_ws = v2 - v1
|
||||
try:
|
||||
cos_a = edge0_ws.dot(edge1_ws) / (edge0_ws.length * edge1_ws.length)
|
||||
angle_rad = acos(cos_a)
|
||||
angle = angle_rad / pi * 180
|
||||
except ZeroDivisionError:
|
||||
continue
|
||||
circle_angle_rad = acos(cos_a)
|
||||
circle_angle = circle_angle_rad / pi * 180
|
||||
angle_rad, angle = 0, 0
|
||||
|
||||
# calculate angle position
|
||||
p0 = location_3d_to_region_2d(region, region3d, v0)
|
||||
p1 = location_3d_to_region_2d(region, region3d, v1)
|
||||
p2 = location_3d_to_region_2d(region, region3d, v2)
|
||||
p0, p1, p2 = [location_3d_to_region_2d(region, region3d, p) for p in vertices[i0 : i1 + 2]]
|
||||
edge0 = p0 - p1
|
||||
edge1 = p2 - p1
|
||||
base_edge = edge0 if ccw(p0, p1, p2) else edge1
|
||||
text_offset = (Matrix.Rotation(-circle_angle_rad / 2, 2) @ base_edge).normalized() * ANGLE_LABEL_OFFSET
|
||||
radius = min(edge0.length_squared, edge1.length_squared) ** 0.5
|
||||
# TODO: can be helpful for svg positioning
|
||||
base_edge = edge0 if tool.Cad.is_counter_clockwise_order(p0, p1, p2) else edge1
|
||||
text_offset = (Matrix.Rotation(-angle_rad / 2, 2) @ base_edge).normalized() * (radius + ANGLE_LABEL_OFFSET)
|
||||
label_position = p1 + text_offset
|
||||
|
||||
text = f"{int(circle_angle)}d"
|
||||
text = f"{int(angle)}deg"
|
||||
label_dir = Vector((1, 0))
|
||||
self.draw_label(context, text, label_position, label_dir)
|
||||
self.draw_label(context, text, label_position, label_dir, box_alignment="center")
|
||||
|
||||
|
||||
class DiameterDecorator(DimensionDecorator):
|
||||
@@ -1555,15 +1561,15 @@ class CutDecorator:
|
||||
gpu.state.point_size_set(1)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
|
||||
# general shader
|
||||
self.shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
self.shader.bind()
|
||||
|
||||
black = (0, 0, 0, 1)
|
||||
@@ -1923,7 +1929,7 @@ class DecorationsHandler:
|
||||
if cls.installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
# NOTE: we USE POST_PIXEL here so that we can draw use both 3D_POLYLINE_UNIFORM_COLOR
|
||||
# NOTE: we USE POST_PIXEL here so that we can use both POLYLINE_UNIFORM_COLOR
|
||||
# and drawing text in the same handler. BUT this means that we supply coordinates in WINSPACE
|
||||
cls.installed = SpaceView3D.draw_handler_add(handler, (context,), "WINDOW", "POST_PIXEL")
|
||||
|
||||
@@ -1941,10 +1947,12 @@ class DecorationsHandler:
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
|
||||
def get_objects_and_decorators(self, collection):
|
||||
# TODO: do it in data instead of the handler for performance?
|
||||
results = []
|
||||
viewport = bpy.context.space_data
|
||||
|
||||
for obj in collection.all_objects:
|
||||
if obj.hide_get():
|
||||
if not obj.visible_get(viewport=viewport):
|
||||
continue
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import blenderbim.bim.module.drawing.decoration as decoration
|
||||
import blenderbim.tool as tool
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
@@ -37,10 +38,28 @@ def depsgraph_update_pre_handler(scene):
|
||||
def set_active_camera_resolution(scene):
|
||||
if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings:
|
||||
return
|
||||
props = scene.camera.data.BIMCameraProperties
|
||||
ortho_scale = max((props.width, props.height))
|
||||
aspect_ratio = props.width / props.height
|
||||
if (
|
||||
scene.render.resolution_x != scene.camera.data.BIMCameraProperties.raster_x
|
||||
or scene.render.resolution_y != scene.camera.data.BIMCameraProperties.raster_y
|
||||
(scene.camera.data.ortho_scale != ortho_scale)
|
||||
or (scene.render.resolution_x / scene.render.resolution_y != aspect_ratio)
|
||||
):
|
||||
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x
|
||||
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y
|
||||
scene.camera.data.ortho_scale = ortho_scale
|
||||
|
||||
diagram_scale = tool.Drawing.get_diagram_scale(scene.camera)
|
||||
scale_ratio = tool.Drawing.get_scale_ratio(diagram_scale["Scale"])
|
||||
|
||||
if props.width > props.height:
|
||||
aspect_ratio = props.height / props.width
|
||||
raster_x = ortho_scale * scale_ratio * props.dpi / 0.0254
|
||||
raster_y = ortho_scale * aspect_ratio * scale_ratio * props.dpi / 0.0254
|
||||
else:
|
||||
aspect_ratio = props.width / props.height
|
||||
raster_x = ortho_scale * aspect_ratio * scale_ratio * props.dpi / 0.0254
|
||||
raster_y = ortho_scale * scale_ratio * props.dpi / 0.0254
|
||||
|
||||
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x = int(raster_x)
|
||||
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y = int(raster_y)
|
||||
|
||||
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
|
||||
|
||||
@@ -182,8 +182,12 @@ def format_distance(
|
||||
|
||||
# Check values and compose string
|
||||
if inches == 12:
|
||||
feet += 1
|
||||
inches = 0
|
||||
if unit_length == "INCHES":
|
||||
inches = 12
|
||||
if unit_length == "FEET":
|
||||
feet += 1
|
||||
inches = 0
|
||||
|
||||
|
||||
if not isArea:
|
||||
add_inches = bool(inches) or not suppress_zero_inches
|
||||
|
||||
@@ -26,7 +26,6 @@ import shutil
|
||||
import hashlib
|
||||
import shapely
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
@@ -42,15 +41,16 @@ import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
import blenderbim.bim.export_ifc
|
||||
from blenderbim.bim.module.drawing.decoration import CutDecorator
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
|
||||
import blenderbim.bim.export_ifc
|
||||
from lxml import etree
|
||||
from mathutils import Vector, Color
|
||||
from mathutils import Vector, Color, Matrix
|
||||
from timeit import default_timer as timer
|
||||
from blenderbim.bim.module.drawing.prop import RasterStyleProperty, Literal, RASTER_STYLE_PROPERTIES_EXCLUDE
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from pathlib import Path
|
||||
from bpy_extras.image_utils import load_image
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
@@ -70,15 +70,6 @@ class profile:
|
||||
print(self.task, timer() - self.start)
|
||||
|
||||
|
||||
def open_with_user_command(user_command, path):
|
||||
if user_command:
|
||||
commands = eval(user_command)
|
||||
for command in commands:
|
||||
subprocess.Popen(command)
|
||||
else:
|
||||
webbrowser.open("file://" + path)
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
@@ -113,6 +104,9 @@ class AddAnnotationType(bpy.types.Operator, Operator):
|
||||
)
|
||||
element.ApplicableOccurrence = f"IfcAnnotation/{object_type}"
|
||||
|
||||
if props.create_representation_for_type and object_type == "IMAGE":
|
||||
bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", use_existing_object_by_name=obj.name)
|
||||
|
||||
|
||||
class EnableAddAnnotationType(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.enable_add_annotation_type"
|
||||
@@ -269,7 +263,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
with profile("Combine SVG layers"):
|
||||
svg_path = self.combine_svgs(context, underlay_svg, linework_svg, annotation_svg)
|
||||
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
|
||||
tool.Drawing.open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command, svg_path
|
||||
)
|
||||
|
||||
if self.print_all:
|
||||
bpy.ops.bim.activate_drawing(drawing=original_drawing_id, camera_view_point=False)
|
||||
@@ -485,7 +481,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
||||
|
||||
self.setup_serialiser(ifc)
|
||||
self.setup_serialiser(ifc, target_view)
|
||||
cache = IfcStore.get_cache()
|
||||
[cache.remove(guid) for guid in invalidated_guids]
|
||||
tree = ifcopenshell.geom.tree()
|
||||
@@ -544,7 +540,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
root = etree.fromstring(results)
|
||||
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
if not group:
|
||||
if group is None:
|
||||
with open(svg_path, "wb") as svg:
|
||||
svg.write(etree.tostring(root))
|
||||
|
||||
@@ -555,7 +551,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
if self.camera.data.BIMCameraProperties.calculate_shapely_surfaces:
|
||||
# shapely variant
|
||||
group = root.findall(".//{http://www.w3.org/2000/svg}g")[0]
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
nm = group.attrib["{http://www.ifcopenshell.org/ns}name"]
|
||||
m4 = np.array(json.loads(group.attrib["{http://www.ifcopenshell.org/ns}plane"]))
|
||||
m3 = np.array(json.loads(group.attrib["{http://www.ifcopenshell.org/ns}matrix3"]))
|
||||
@@ -566,10 +562,21 @@ class CreateDrawing(bpy.types.Operator):
|
||||
m44[1][3] = m3[1][2]
|
||||
m44 = np.linalg.inv(m44)
|
||||
|
||||
projections = group.findall('.//{http://www.w3.org/2000/svg}g[@class="projection"]') or []
|
||||
elements_with_faces = set()
|
||||
for element in drawing_elements.copy():
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj and obj.type == "MESH" and len(obj.data.polygons):
|
||||
elements_with_faces.add(element.GlobalId)
|
||||
|
||||
projections = root.xpath(
|
||||
".//svg:g[contains(@class, 'projection')]", namespaces={"svg": "http://www.w3.org/2000/svg"}
|
||||
)
|
||||
|
||||
boundary_lines = []
|
||||
for projection in projections:
|
||||
boundary_lines = []
|
||||
global_id = projection.attrib["{http://www.ifcopenshell.org/ns}guid"]
|
||||
if global_id not in elements_with_faces:
|
||||
continue
|
||||
for path in projection.findall("./{http://www.w3.org/2000/svg}path"):
|
||||
# Rounding is necessary to ensure coincident points are coincident
|
||||
start, end = [[round(float(o), 1) for o in co[1:].split(",")] for co in path.attrib["d"].split()]
|
||||
@@ -578,50 +585,55 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# Extension by 0.5mm is necessary to ensure lines overlap with other diagonal lines
|
||||
start, end = tool.Drawing.extend_line(start, end, 0.5)
|
||||
boundary_lines.append(shapely.LineString([start, end]))
|
||||
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||
|
||||
for polygon in closed_polygons.geoms:
|
||||
# Less than 1mm2 is not worth styling on sheet
|
||||
if polygon.area < 1:
|
||||
continue
|
||||
centroid = polygon.centroid
|
||||
internal_point = centroid if polygon.contains(centroid) else polygon.representative_point()
|
||||
if internal_point:
|
||||
internal_point = [internal_point.x, internal_point.y]
|
||||
a, b = self.drawing_to_model_co(m44, m4, internal_point, 0.0), self.drawing_to_model_co(
|
||||
m44, m4, internal_point, -100.0
|
||||
)
|
||||
inside_elements = [e for e in tree.select(self.pythonize(a)) if not e.is_a("IfcAnnotation")]
|
||||
if not inside_elements:
|
||||
elements = [
|
||||
e
|
||||
for e in tree.select_ray(self.pythonize(a), self.pythonize(b - a))
|
||||
if not e.instance.is_a("IfcAnnotation")
|
||||
and tool.Cad.is_point_on_edge(
|
||||
Vector(list(e.position)), (Vector(self.pythonize(a)), Vector(self.pythonize(b)))
|
||||
)
|
||||
]
|
||||
if elements:
|
||||
path = etree.Element("path")
|
||||
d = (
|
||||
"M"
|
||||
+ " L".join(
|
||||
[",".join([str(o) for o in co]) for co in polygon.exterior.coords[0:-1]]
|
||||
)
|
||||
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||
|
||||
for polygon in closed_polygons.geoms:
|
||||
# Less than 1mm2 is not worth styling on sheet
|
||||
if polygon.area < 1:
|
||||
continue
|
||||
centroid = polygon.centroid
|
||||
internal_point = centroid if polygon.contains(centroid) else polygon.representative_point()
|
||||
if internal_point:
|
||||
internal_point = [internal_point.x, internal_point.y]
|
||||
a, b = self.drawing_to_model_co(m44, m4, internal_point, 0.0), self.drawing_to_model_co(
|
||||
m44, m4, internal_point, -100.0
|
||||
)
|
||||
inside_elements = [e for e in tree.select(self.pythonize(a)) if not e.is_a("IfcAnnotation")]
|
||||
if not inside_elements:
|
||||
elements = [
|
||||
e
|
||||
for e in tree.select_ray(self.pythonize(a), self.pythonize(b - a))
|
||||
if not e.instance.is_a("IfcAnnotation")
|
||||
and tool.Cad.is_point_on_edge(
|
||||
Vector(list(e.position)), (Vector(self.pythonize(a)), Vector(self.pythonize(b)))
|
||||
)
|
||||
]
|
||||
if elements:
|
||||
path = etree.Element("path")
|
||||
d = (
|
||||
"M"
|
||||
+ " L".join([",".join([str(o) for o in co]) for co in polygon.exterior.coords[0:-1]])
|
||||
+ " Z"
|
||||
)
|
||||
for interior in polygon.interiors:
|
||||
d += (
|
||||
" M"
|
||||
+ " L".join([",".join([str(o) for o in co]) for co in interior.coords[0:-1]])
|
||||
+ " Z"
|
||||
)
|
||||
for interior in polygon.interiors:
|
||||
d += (
|
||||
" M"
|
||||
+ " L".join([",".join([str(o) for o in co]) for co in interior.coords[0:-1]])
|
||||
+ " Z"
|
||||
)
|
||||
path.attrib["d"] = d
|
||||
classes = self.get_svg_classes(ifc.by_id(elements[0].instance.id()))
|
||||
classes.append("surface")
|
||||
path.set("class", " ".join(list(classes)))
|
||||
group.insert(0, path)
|
||||
path.attrib["d"] = d
|
||||
classes = self.get_svg_classes(ifc.by_id(elements[0].instance.id()))
|
||||
classes.append(f"intpoint-{internal_point}")
|
||||
classes.append(f"ab-{a}, {b}")
|
||||
for i, ray_result in enumerate(elements):
|
||||
classes.append(f"el{i}-{ray_result.instance.id()}")
|
||||
classes.append(f"el{i}-pos-{list(ray_result.position)}")
|
||||
classes.append(f"el{i}-dst-{ray_result.distance}")
|
||||
classes.append("surface")
|
||||
path.set("class", " ".join(list(classes)))
|
||||
group.insert(0, path)
|
||||
|
||||
if self.camera.data.BIMCameraProperties.calculate_svgfill_surfaces:
|
||||
results = etree.tostring(root).decode("utf8")
|
||||
@@ -636,7 +648,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
dom1 = parseString(svg_data_1)
|
||||
svg1 = dom1.childNodes[0]
|
||||
groups1 = [g for g in yield_groups(svg1) if g.getAttribute("class") == "projection"]
|
||||
groups1 = [g for g in yield_groups(svg1) if "projection" in g.getAttribute("class")]
|
||||
|
||||
ls_groups = ifcopenshell.ifcopenshell_wrapper.svg_to_line_segments(results, "projection")
|
||||
|
||||
@@ -829,7 +841,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
return svg_path
|
||||
|
||||
def setup_serialiser(self, ifc):
|
||||
def setup_serialiser(self, ifc, target_view):
|
||||
self.svg_settings = ifcopenshell.geom.settings(
|
||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||
)
|
||||
@@ -840,7 +852,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.serialiser.setPolygonal(True)
|
||||
self.serialiser.setUseHlrPoly(True)
|
||||
# Objects with more than these edges are rendered as wireframe instead of HLR for optimisation
|
||||
self.serialiser.setProfileThreshold(1000)
|
||||
self.serialiser.setProfileThreshold(10000)
|
||||
self.serialiser.setUseNamespace(True)
|
||||
self.serialiser.setAlwaysProject(True)
|
||||
self.serialiser.setAutoElevation(False)
|
||||
@@ -853,6 +865,10 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.serialiser.setScale(self.scale)
|
||||
self.serialiser.setSubtractionSettings(ifcopenshell.ifcopenshell_wrapper.ALWAYS)
|
||||
self.serialiser.setUsePrefiltering(True) # See #3359
|
||||
self.serialiser.setUnifyInputs(True)
|
||||
self.serialiser.setSegmentProjection(True)
|
||||
if target_view == "REFLECTED_PLAN_VIEW":
|
||||
self.serialiser.setMirrorY(True)
|
||||
# tree = ifcopenshell.geom.tree()
|
||||
# This instructs the tree to explode BReps into faces and return
|
||||
# the style of the face when running tree.select_ray()
|
||||
@@ -887,24 +903,50 @@ class CreateDrawing(bpy.types.Operator):
|
||||
)
|
||||
return classes
|
||||
|
||||
def is_manifold(self, obj):
|
||||
result = self.is_manifold_cache.get(obj.data.name, None)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
for edge in bm.edges:
|
||||
if not edge.is_manifold:
|
||||
bm.free()
|
||||
self.is_manifold_cache[obj.data.name] = False
|
||||
return False
|
||||
self.is_manifold_cache[obj.data.name] = True
|
||||
return True
|
||||
|
||||
def merge_linework_and_add_metadata(self, root):
|
||||
join_criteria = ifcopenshell.util.element.get_pset(self.camera_element, "EPset_Drawing", "JoinCriteria")
|
||||
if join_criteria:
|
||||
join_criteria = join_criteria.split(",")
|
||||
else:
|
||||
# Drawing convention states that same objects classes with the same material are merged when cut.
|
||||
join_criteria = ["class", "material.Name", 'r"Pset.*Common"."Status"']
|
||||
join_criteria = ["class", "material.Name", "/Pset_.*Common/.Status", "EPset_Status.Status"]
|
||||
|
||||
group = root.findall(".//{http://www.w3.org/2000/svg}g")[0]
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
joined_paths = {}
|
||||
self.is_manifold_cache = {}
|
||||
|
||||
ifc = tool.Ifc.get()
|
||||
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||
element = ifc.by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("cut")
|
||||
el.set("class", " ".join(classes))
|
||||
if "projection" in el.get("class", "").split():
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("projection")
|
||||
el.set("class", " ".join(classes))
|
||||
continue
|
||||
else:
|
||||
classes = self.get_svg_classes(element)
|
||||
classes.append("cut")
|
||||
el.set("class", " ".join(classes))
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not self.is_manifold(obj):
|
||||
continue
|
||||
|
||||
# An element group will contain a bunch of paths representing the
|
||||
# cut of that element. However IfcOpenShell may not correctly
|
||||
@@ -1022,9 +1064,12 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# IfcConvert puts the projection afterwards which is not correct since
|
||||
# projection should be drawn underneath the cut.
|
||||
group = root.find("{http://www.w3.org/2000/svg}g")
|
||||
projection = group.find("{http://www.w3.org/2000/svg}g[@class='projection']")
|
||||
projection.getparent().remove(projection)
|
||||
group.insert(0, projection)
|
||||
projections = root.xpath(
|
||||
".//svg:g[contains(@class, 'projection')]", namespaces={"svg": "http://www.w3.org/2000/svg"}
|
||||
)
|
||||
for projection in projections:
|
||||
projection.getparent().remove(projection)
|
||||
group.insert(0, projection)
|
||||
|
||||
def generate_annotation(self, context):
|
||||
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasAnnotation"):
|
||||
@@ -1035,7 +1080,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
elements = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(self.camera_element))
|
||||
filtered_drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
||||
elements = [e for e in elements if e in filtered_drawing_elements]
|
||||
filtered_drawing_annotations = {e for e in filtered_drawing_elements if e.is_a("IfcAnnotation")}
|
||||
elements = {e for e in elements if e in filtered_drawing_elements}
|
||||
elements = list(elements | filtered_drawing_annotations)
|
||||
|
||||
annotations = sorted(
|
||||
elements, key=lambda a: (tool.Drawing.get_annotation_z_index(a), 1 if a.ObjectType == "TEXT" else 0)
|
||||
@@ -1236,11 +1283,15 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
|
||||
# These variables will be made available to the evaluated commands
|
||||
svg = references["SHEET"]
|
||||
basename = os.path.basename(svg)
|
||||
path = os.path.dirname(svg)
|
||||
pdf = os.path.splitext(svg)[0] + ".pdf"
|
||||
eps = os.path.splitext(svg)[0] + ".eps"
|
||||
dxf = os.path.splitext(svg)[0] + ".dxf"
|
||||
replacements = {
|
||||
"svg": svg,
|
||||
"basename": os.path.basename(svg),
|
||||
"path": os.path.dirname(svg),
|
||||
"pdf": pdf,
|
||||
"eps": os.path.splitext(svg)[0] + ".eps",
|
||||
"dxf": os.path.splitext(svg)[0] + ".dxf",
|
||||
}
|
||||
|
||||
has_sheet_reference = False
|
||||
for reference in tool.Drawing.get_document_references(sheet):
|
||||
@@ -1273,22 +1324,23 @@ class CreateSheets(bpy.types.Operator, Operator):
|
||||
|
||||
if svg2pdf_command:
|
||||
# With great power comes great responsibility. Example:
|
||||
# [['inkscape', svg, '-o', pdf]]
|
||||
commands = eval(svg2pdf_command)
|
||||
# [["inkscape", "svg", "-o", "pdf"]]
|
||||
commands = json.loads(svg2pdf_command)
|
||||
for command in commands:
|
||||
subprocess.run(command)
|
||||
subprocess.run([replacements.get(c, c) for c in command])
|
||||
|
||||
if svg2dxf_command:
|
||||
# With great power comes great responsibility. Example:
|
||||
# [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]
|
||||
commands = eval(svg2dxf_command)
|
||||
# [["inkscape", "svg", "-o", "eps"], ["pstoedit", "-dt", "-f", "dxf:-polyaslines -mm", "eps", "dxf", "-psarg", "-dNOSAFER"]]
|
||||
commands = json.loads(svg2dxf_command)
|
||||
for command in commands:
|
||||
subprocess.run(command)
|
||||
command[0] = shutil.which(command[0]) or command[0]
|
||||
subprocess.run([replacements.get(c, c) for c in command])
|
||||
|
||||
if svg2pdf_command:
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
|
||||
else:
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
|
||||
tool.Drawing.open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, svg)
|
||||
|
||||
|
||||
class SelectAllDrawings(bpy.types.Operator):
|
||||
@@ -1355,7 +1407,9 @@ class OpenDrawing(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
for drawing_uri in drawing_uris:
|
||||
open_with_user_command(context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri)
|
||||
tool.Drawing.open_with_user_command(
|
||||
context.preferences.addons["blenderbim"].preferences.svg_command, drawing_uri
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1372,25 +1426,9 @@ class ActivateModel(bpy.types.Operator):
|
||||
CutDecorator.uninstall()
|
||||
|
||||
if not bpy.app.background:
|
||||
view3d_context = dict()
|
||||
|
||||
for window in bpy.context.window_manager.windows:
|
||||
screen = window.screen
|
||||
for area in screen.areas:
|
||||
if area.type == "VIEW_3D":
|
||||
view3d_context["window"] = window
|
||||
view3d_context["screen"] = screen
|
||||
view3d_context["area"] = area
|
||||
for region in area.regions:
|
||||
if region.type == "WINDOW":
|
||||
view3d_context["region"] = region
|
||||
break
|
||||
break
|
||||
if view3d_context:
|
||||
break
|
||||
|
||||
if view3d_context:
|
||||
bpy.ops.object.hide_view_clear(view3d_context)
|
||||
with context.temp_override(**tool.Blender.get_viewport_context()):
|
||||
bpy.ops.object.hide_view_clear()
|
||||
bpy.ops.bim.activate_status_filters()
|
||||
|
||||
subcontext = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@@ -1512,11 +1550,17 @@ class RemoveDrawing(bpy.types.Operator, Operator):
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in context.scene.DocProperties.drawings if d.is_selected
|
||||
]
|
||||
else:
|
||||
if not self.drawing:
|
||||
self.report({"ERROR"}, "No drawing selected")
|
||||
return {"CANCELLED"}
|
||||
drawings = [tool.Ifc.get().by_id(self.drawing)]
|
||||
|
||||
removed_drawings = [drawing.id() for drawing in drawings]
|
||||
|
||||
for drawing in drawings:
|
||||
sheet_references = tool.Drawing.get_sheet_references(drawing)
|
||||
for reference in sheet_references:
|
||||
bpy.ops.bim.remove_drawing_from_sheet(reference=reference.id())
|
||||
core.remove_drawing(tool.Ifc, tool.Drawing, drawing=drawing)
|
||||
|
||||
|
||||
@@ -1862,7 +1906,7 @@ class RemoveSchedule(bpy.types.Operator, Operator):
|
||||
schedule: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", schedule=tool.Ifc.get().by_id(self.schedule))
|
||||
core.remove_document(tool.Ifc, tool.Drawing, "SCHEDULE", document=tool.Ifc.get().by_id(self.schedule))
|
||||
|
||||
|
||||
class OpenSchedule(bpy.types.Operator, Operator):
|
||||
@@ -1952,11 +1996,11 @@ class AddReferenceToSheet(bpy.types.Operator, Operator):
|
||||
props = context.scene.DocProperties
|
||||
active_reference = props.references[props.active_reference_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
reference = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
|
||||
extref = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
reference_location = tool.Drawing.get_path_with_ext(reference.DocumentReferences[0].Location, "svg")
|
||||
extref_location = tool.Drawing.get_path_with_ext(extref.DocumentReferences[0].Location, "svg")
|
||||
else:
|
||||
reference_location = tool.Drawing.get_path_with_ext(reference.HasDocumentReferences[0].Location, "svg")
|
||||
extref_location = tool.Drawing.get_path_with_ext(extref.HasDocumentReferences[0].Location, "svg")
|
||||
|
||||
sheet = tool.Ifc.get().by_id(active_sheet.ifc_definition_id)
|
||||
if not sheet.is_a("IfcDocumentInformation"):
|
||||
@@ -1964,30 +2008,30 @@ class AddReferenceToSheet(bpy.types.Operator, Operator):
|
||||
|
||||
references = tool.Drawing.get_document_references(sheet)
|
||||
|
||||
has_reference = False
|
||||
has_extref = False
|
||||
for reference in references:
|
||||
if reference.Location == reference_location:
|
||||
has_reference = True
|
||||
if reference.Location == extref_location:
|
||||
has_extref = True
|
||||
break
|
||||
if has_reference:
|
||||
if has_extref:
|
||||
return
|
||||
|
||||
if not tool.Drawing.does_file_exist(tool.Ifc.resolve_uri(reference_location)):
|
||||
self.report({"ERROR"}, f"Cannot find reference svg by path {reference_location}.")
|
||||
if not tool.Drawing.does_file_exist(tool.Ifc.resolve_uri(extref_location)):
|
||||
self.report({"ERROR"}, f"Cannot find reference svg by path {extref_location}.")
|
||||
return
|
||||
|
||||
reference = tool.Ifc.run("document.add_reference", information=sheet)
|
||||
id_attr = "ItemReference" if tool.Ifc.get_schema() == "IFC2X3" else "Identification"
|
||||
attributes = {
|
||||
id_attr: str(len([r for r in references if r.Description in ("DRAWING", "REFERENCE")]) + 1),
|
||||
"Location": reference_location,
|
||||
"Location": extref_location,
|
||||
"Description": "REFERENCE",
|
||||
}
|
||||
tool.Ifc.run("document.edit_reference", reference=reference, attributes=attributes)
|
||||
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = context.scene.BIMProperties.data_dir
|
||||
sheet_builder.add_document(reference, reference, sheet)
|
||||
sheet_builder.add_document(reference, extref, sheet)
|
||||
|
||||
tool.Drawing.import_sheets()
|
||||
|
||||
@@ -2227,6 +2271,27 @@ class RemoveTextLiteral(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignSelectedObjectAsProduct(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_selected_as_product"
|
||||
bl_label = "Assign Selected Object As Product"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if len(context.selected_objects) != 2:
|
||||
cls.poll_message_set("2 objects need to be selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
objs = context.selected_objects[:]
|
||||
objs.remove(context.active_object)
|
||||
other_selected_object = next(obj for obj in context.selected_objects if obj != context.active_object)
|
||||
context.active_object.BIMAssignedProductProperties.relating_product = other_selected_object
|
||||
bpy.ops.bim.edit_assigned_product()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditAssignedProduct(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.edit_assigned_product"
|
||||
bl_label = "Edit Text Product"
|
||||
@@ -2466,3 +2531,156 @@ class SelectAssignedProduct(bpy.types.Operator, Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_assigned_product(tool.Drawing, context)
|
||||
|
||||
|
||||
class EnableEditingElementFilter(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.enable_editing_element_filter"
|
||||
bl_label = "Enable Editing Element Filter"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filter_mode: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.context.scene.camera
|
||||
if obj:
|
||||
obj.data.BIMCameraProperties.filter_mode = self.filter_mode
|
||||
|
||||
|
||||
class EditElementFilter(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.edit_element_filter"
|
||||
bl_label = "Edit Element Filter"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filter_mode: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.active_object.data.BIMCameraProperties
|
||||
obj = bpy.context.scene.camera
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
pset = tool.Pset.get_element_pset(element, "EPset_Drawing")
|
||||
if self.filter_mode == "INCLUDE":
|
||||
query = tool.Search.export_filter_query(props.include_filter_groups) or None
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Include": query})
|
||||
elif self.filter_mode == "EXCLUDE":
|
||||
query = tool.Search.export_filter_query(props.exclude_filter_groups) or None
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Exclude": query})
|
||||
obj.data.BIMCameraProperties.filter_mode = "NONE"
|
||||
bpy.ops.bim.activate_drawing(drawing=element.id(), camera_view_point=False)
|
||||
|
||||
|
||||
class AddReferenceImage(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.add_reference_image"
|
||||
bl_label = "Add Reference Image"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=True)
|
||||
filepath: bpy.props.StringProperty(
|
||||
name="File Path", description="Filepath used to import from", maxlen=1024, default="", subtype="FILE_PATH"
|
||||
)
|
||||
filter_image: bpy.props.BoolProperty(default=True, options={"HIDDEN", "SKIP_SAVE"})
|
||||
filter_folder: bpy.props.BoolProperty(default=True, options={"HIDDEN", "SKIP_SAVE"})
|
||||
|
||||
override_existing_image: bpy.props.BoolProperty(
|
||||
name="Override Existing Image",
|
||||
default=True,
|
||||
description=(
|
||||
"Override image if it was previously loaded to Blender. If disabled, will always create a new image"
|
||||
),
|
||||
)
|
||||
use_existing_object_by_name: bpy.props.StringProperty(
|
||||
name="Use Existing Object By Name",
|
||||
description="Existing object name to add a style with reference image to. If not provided will create a new object.",
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
if Path(tool.Ifc.get_path()).is_file():
|
||||
layout.prop(self, "use_relative_path")
|
||||
else:
|
||||
self.use_relative_path = False
|
||||
layout.label(text="Save the .ifc file first ")
|
||||
layout.label(text="to use relative paths.")
|
||||
layout.prop(self, "override_existing_image")
|
||||
layout.prop(self, "use_existing_object_by_name")
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def _execute(self, context):
|
||||
abs_path = Path(self.filepath)
|
||||
if self.use_relative_path:
|
||||
image_filepath = abs_path.relative_to(Path(tool.Ifc.get_path()).parent)
|
||||
else:
|
||||
image_filepath = abs_path
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
if self.override_existing_image:
|
||||
params = {"check_existing": True, "force_reload": True}
|
||||
else:
|
||||
params = {"check_existing": False}
|
||||
image = load_image(abs_path.name, abs_path.parent, **params)
|
||||
|
||||
def bm_add_image_plane(mesh):
|
||||
bm = tool.Blender.get_bmesh_for_mesh(mesh, clean=True)
|
||||
plane_scale = (Vector(image.size) / min(image.size)).to_3d()
|
||||
matrix = Matrix.LocRotScale(None, None, plane_scale)
|
||||
bmesh.ops.create_grid(bm, x_segments=1, y_segments=1, size=1, matrix=matrix, calc_uvs=False)
|
||||
tool.Blender.apply_bmesh(mesh, bm)
|
||||
|
||||
if self.use_existing_object_by_name:
|
||||
obj = bpy.data.objects[self.use_existing_object_by_name]
|
||||
bm_add_image_plane(obj.data)
|
||||
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="")
|
||||
else:
|
||||
temp_mesh = bpy.data.meshes.new("temp_mesh")
|
||||
bm_add_image_plane(temp_mesh)
|
||||
obj = bpy.data.objects.new(image_filepath.stem, temp_mesh)
|
||||
tool.Drawing.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="IMAGE",
|
||||
should_add_representation=True,
|
||||
context=ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW"),
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
tool.Blender.remove_data_block(temp_mesh)
|
||||
|
||||
tool.Blender.set_active_object(obj)
|
||||
|
||||
material = bpy.data.materials.new(name=image_filepath.stem)
|
||||
obj.data.materials.append(None) # new slot
|
||||
obj.material_slots[0].material = material
|
||||
bpy.ops.bim.add_style()
|
||||
|
||||
style = ifc_file.by_id(material.BIMMaterialProperties.ifc_style_id)
|
||||
tool.Style.assign_style_to_object(style, obj)
|
||||
|
||||
# TODO: IfcSurfaceStyleRendering is unnecessary here, added it only because
|
||||
# we don't support IfcSurfaceStyleWithTextures without Rendering yet
|
||||
shading_attributes = {
|
||||
"SurfaceColour": {
|
||||
"Red": 1.0,
|
||||
"Green": 1.0,
|
||||
"Blue": 1.0,
|
||||
},
|
||||
"Transparency": 0.0,
|
||||
"ReflectanceMethod": "NOTDEFINED",
|
||||
}
|
||||
ifcopenshell.api.run(
|
||||
"style.add_surface_style",
|
||||
tool.Ifc.get(),
|
||||
style=style,
|
||||
ifc_class="IfcSurfaceStyleRendering",
|
||||
attributes=shading_attributes,
|
||||
)
|
||||
texture = ifc_file.create_entity("IfcImageTexture", Mode="DIFFUSE", URLReference=image_filepath.as_posix())
|
||||
textures = [texture]
|
||||
ifc_file.create_entity("IfcTextureCoordinateGenerator", Maps=textures, Mode="COORD") # UV map
|
||||
tool.Ifc.run(
|
||||
"style.add_surface_style",
|
||||
style=style,
|
||||
ifc_class="IfcSurfaceStyleWithTextures",
|
||||
attributes={"Textures": textures},
|
||||
)
|
||||
tool.Style.reload_material_from_ifc(material)
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
|
||||
@@ -25,6 +25,7 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.drawing as core
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.decoration as decoration
|
||||
from blenderbim.bim.prop import BIMFilterGroup
|
||||
from blenderbim.bim.module.drawing.data import DrawingsData, DecoratorData, SheetsData, AnnotationData
|
||||
from blenderbim.bim.module.drawing.data import refresh as refresh_drawing_data
|
||||
from pathlib import Path
|
||||
@@ -312,7 +313,6 @@ class DocProperties(PropertyGroup):
|
||||
should_use_underlay_cache: BoolProperty(name="Use Underlay Cache", default=False)
|
||||
should_use_linework_cache: BoolProperty(name="Use Linework Cache", default=False)
|
||||
should_use_annotation_cache: BoolProperty(name="Use Annotation Cache", default=False)
|
||||
should_extract: BoolProperty(name="Should Extract", default=True)
|
||||
is_editing_drawings: BoolProperty(name="Is Editing Drawings", default=False)
|
||||
is_editing_schedules: BoolProperty(name="Is Editing Schedules", default=False)
|
||||
is_editing_references: BoolProperty(name="Is Editing References", default=False)
|
||||
@@ -375,8 +375,14 @@ class BIMCameraProperties(PropertyGroup):
|
||||
custom_scale_denominator: bpy.props.StringProperty(default="100", update=update_diagram_scale)
|
||||
raster_x: IntProperty(name="Raster X", default=1000)
|
||||
raster_y: IntProperty(name="Raster Y", default=1000)
|
||||
dpi: IntProperty(name="DPI", default=75)
|
||||
width: FloatProperty(name="Width", default=50, subtype="DISTANCE")
|
||||
height: FloatProperty(name="Height", default=50, subtype="DISTANCE")
|
||||
is_nts: BoolProperty(name="Is NTS", update=update_is_nts)
|
||||
active_drawing_style_index: IntProperty(name="Active Drawing Style Index")
|
||||
filter_mode: StringProperty(name="Filter Mode", default="NONE")
|
||||
include_filter_groups: CollectionProperty(type=BIMFilterGroup, name="Include Filter")
|
||||
exclude_filter_groups: CollectionProperty(type=BIMFilterGroup, name="Exclude Filter")
|
||||
|
||||
# For now, this JSON dump are all the parameters that determine a camera's "Block representation"
|
||||
# By checking this, you will know whether or not the camera IFC representation needs to be refreshed
|
||||
@@ -502,11 +508,14 @@ ANNOTATION_TYPES_DATA = {
|
||||
"PLAN_LEVEL": ("Level (Plan)", "", "SORTBYEXT", "curve"),
|
||||
"SECTION_LEVEL": ("Level (Section)", "", "TRIA_DOWN", "curve"),
|
||||
"BREAKLINE": ("Breakline", "", "FCURVE", "mesh"),
|
||||
"SYMBOL": ("Symbol", "", "KEYFRAME", "empty"),
|
||||
"MULTI_SYMBOL": ("Multi-Symbol", "", "OUTLINER_DATA_POINTCLOUD", "mesh"),
|
||||
"LINEWORK": ("Line", "", "SNAP_MIDPOINT", "mesh"),
|
||||
"BATTING": ("Batting", "Add batting annotation.\nThickness could be changed through Thickness property of BBIM_Batting property set", "FORCE_FORCE", "mesh"),
|
||||
"REVISION_CLOUD":("Revision Cloud", "Add revision cloud", "VOLUME_DATA", "mesh"),
|
||||
"FILL_AREA": ("Fill Area", "", "NODE_TEXTURE", "mesh"),
|
||||
"FALL": ("Fall", "", "SORT_ASC", "curve"),
|
||||
"IMAGE": ("Image", "Add reference image attached to the drawing", "TEXTURE", "mesh"),
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
@@ -538,7 +547,7 @@ class BIMAnnotationProperties(PropertyGroup):
|
||||
name="Create Representation For Type",
|
||||
default=False,
|
||||
description='Whether "Add type" should define a representation for the type \n'
|
||||
"or allow occurences to have their own",
|
||||
"or allow occurrences to have their own",
|
||||
)
|
||||
is_adding_type: bpy.props.BoolProperty(default=False)
|
||||
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
from blenderbim.bim.module.drawing.svgwriter import SvgWriter
|
||||
import svgwrite
|
||||
import openpyxl
|
||||
|
||||
from odf.opendocument import load as load_ods
|
||||
from odf.table import Table, TableRow, TableColumn, TableCell
|
||||
@@ -62,6 +63,156 @@ class Scheduler:
|
||||
)
|
||||
self.padding = 1
|
||||
self.margin = 1
|
||||
if infile.endswith("ods"):
|
||||
self.schedule_ods(infile, outfile)
|
||||
elif infile.endswith("xlsx"):
|
||||
self.schedule_xlsx(infile, outfile)
|
||||
|
||||
def schedule_xlsx(self, infile, outfile):
|
||||
workbook = openpyxl.open(infile, data_only=True)
|
||||
sheet = workbook.active
|
||||
|
||||
column_dimensions = {k: v.width for k, v in sheet.column_dimensions.items()}
|
||||
row_dimensions = {k: v.height for k, v in sheet.row_dimensions.items()}
|
||||
default_width = 8.43 # 8.43 characters wide
|
||||
default_height = 15 # 15pt seems to be the default height
|
||||
|
||||
merged_cells = []
|
||||
for cell_range in sheet.merged_cells.ranges:
|
||||
pass
|
||||
|
||||
y = self.margin
|
||||
rows = list(sheet.iter_rows())
|
||||
total_rows = len(rows)
|
||||
for i, row in enumerate(rows):
|
||||
# The last row may contain only null values
|
||||
if i == (total_rows - 1) and not [c for c in row if c.value is not None]:
|
||||
continue
|
||||
|
||||
x = self.margin
|
||||
for cell in row:
|
||||
if isinstance(cell, openpyxl.cell.cell.MergedCell):
|
||||
column_letter = openpyxl.utils.get_column_letter(cell.column)
|
||||
width = column_dimensions.get(column_letter, default_width)
|
||||
else:
|
||||
width = column_dimensions.get(cell.column_letter, default_width)
|
||||
width = self.convert_character_width_to_mm(width)
|
||||
height = row_dimensions.get(cell.row, default_height)
|
||||
height = self.convert_to_mm(f"{height}pt")
|
||||
|
||||
if isinstance(cell, openpyxl.cell.cell.MergedCell):
|
||||
x += width
|
||||
continue
|
||||
|
||||
unmerged_width = width
|
||||
unmerged_height = height
|
||||
|
||||
for cell_range in sheet.merged_cells.ranges:
|
||||
if cell.coordinate not in cell_range:
|
||||
continue
|
||||
merged_width = 0
|
||||
merged_height = 0
|
||||
for merged_column in cell_range.cols:
|
||||
merged_cell = sheet.cell(*merged_column[0])
|
||||
column_letter = openpyxl.utils.get_column_letter(merged_cell.column)
|
||||
width = column_dimensions.get(cell.column_letter, default_width)
|
||||
width = self.convert_character_width_to_mm(width)
|
||||
merged_width += width
|
||||
for merged_row in cell_range.rows:
|
||||
merged_cell = sheet.cell(*merged_row[0])
|
||||
height = row_dimensions.get(merged_cell.row, default_height)
|
||||
height = self.convert_to_mm(f"{height}pt")
|
||||
merged_height += height
|
||||
width = merged_width
|
||||
height = merged_height
|
||||
break
|
||||
|
||||
if cell.fill.patternType:
|
||||
background_color = "#" + str(cell.fill.bgColor.rgb).lower()[2:]
|
||||
else:
|
||||
background_color = "#ffffff"
|
||||
|
||||
self.svg.add(
|
||||
self.svg.rect(
|
||||
insert=(x, y),
|
||||
size=(width, height),
|
||||
style=f"fill: {background_color}; stroke-width:.125; stroke: #000000;",
|
||||
)
|
||||
)
|
||||
|
||||
if not cell.value:
|
||||
x += unmerged_width
|
||||
continue
|
||||
|
||||
font_size = cell.font.size or 11 # 11pt default
|
||||
font_size = font_size / FONT_SIZE_PT * FONT_SIZE # Magic?
|
||||
|
||||
text_position = [0.0, 0.0]
|
||||
if cell.alignment.horizontal == "left":
|
||||
text_position[0] = x + self.padding
|
||||
horizontal_align = "left"
|
||||
elif cell.alignment.horizontal == "general" and isinstance(cell.value, str):
|
||||
text_position[0] = x + self.padding
|
||||
horizontal_align = "left"
|
||||
elif cell.alignment.horizontal in ("center", "justify", "centerContinuous", "distributed"):
|
||||
text_position[0] = x + width / 2
|
||||
horizontal_align = "middle"
|
||||
elif cell.alignment.horizontal == "right":
|
||||
text_position[0] = x + width - self.padding
|
||||
horizontal_align = "right"
|
||||
elif cell.alignment.horizontal == "general" and not isinstance(cell.value, str):
|
||||
text_position[0] = x + width - self.padding
|
||||
horizontal_align = "right"
|
||||
else:
|
||||
text_position[0] = x + self.padding
|
||||
horizontal_align = "left"
|
||||
|
||||
if cell.alignment.vertical == "top":
|
||||
text_position[1] = y + self.padding
|
||||
vertical_align = "top"
|
||||
elif cell.alignment.vertical in ("center", "justify", "distributed"):
|
||||
text_position[1] = y + height / 2
|
||||
vertical_align = "middle"
|
||||
elif cell.alignment.vertical == "bottom":
|
||||
text_position[1] = y + height - self.padding
|
||||
vertical_align = "bottom"
|
||||
else:
|
||||
text_position[1] = y + height - self.padding
|
||||
vertical_align = "bottom"
|
||||
|
||||
if vertical_align == "middle" and horizontal_align == "middle":
|
||||
box_alignment = "center"
|
||||
else:
|
||||
box_alignment = f"{vertical_align}-{horizontal_align}"
|
||||
|
||||
if cell.font.color:
|
||||
text_color = "#" + str(cell.font.color.rgb).lower()[2:]
|
||||
else:
|
||||
text_color = "#000000"
|
||||
|
||||
self.add_text(
|
||||
[cell.value],
|
||||
*text_position,
|
||||
font_size=font_size,
|
||||
box_alignment=box_alignment,
|
||||
wrap_text=cell.alignment.wrapText or False,
|
||||
cell_width=width,
|
||||
bold=cell.font.b,
|
||||
italic=cell.font.i,
|
||||
text_color=text_color,
|
||||
)
|
||||
|
||||
x += unmerged_width
|
||||
y += unmerged_height
|
||||
|
||||
total_width = x + self.margin
|
||||
total_height = y + self.margin
|
||||
self.svg["width"] = "{}mm".format(total_width)
|
||||
self.svg["height"] = "{}mm".format(total_height)
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def schedule_ods(self, infile, outfile):
|
||||
doc = load_ods(infile)
|
||||
|
||||
# useful for debugging ods
|
||||
@@ -118,6 +269,25 @@ class Scheduler:
|
||||
if cell_style:
|
||||
related_styles.append((style_name, cell_style))
|
||||
|
||||
# sometimes there are no column styles (e.g. in ODS from IfcCSV)
|
||||
# and we just use some constant number for column widths
|
||||
if not column_widths:
|
||||
row_columns = []
|
||||
for tr in table.getElementsByType(TableRow):
|
||||
row_cols = 0
|
||||
for td in tr.getElementsByType(TableCell):
|
||||
column_span = td.getAttribute("numbercolumnsspanned")
|
||||
column_span = int(column_span) if column_span else 1
|
||||
|
||||
col_repeat = td.getAttribute("numbercolumnsrepeated")
|
||||
col_repeat = int(col_repeat) if col_repeat else 1
|
||||
row_cols += column_span * col_repeat
|
||||
row_columns.append(row_cols)
|
||||
|
||||
n_columns = max(row_columns)
|
||||
column_widths = [25] * n_columns # some constant width value 👀
|
||||
column_styles = [None] * n_columns
|
||||
|
||||
# collect rows height
|
||||
row_heights = []
|
||||
# TODO: never used yet because unsure about priority for row styles
|
||||
@@ -351,7 +521,7 @@ class Scheduler:
|
||||
wrap_text: if True, text will be wrapped to fit in cell
|
||||
cell_width: width of cell, used for wrapping text
|
||||
"""
|
||||
text_lines = [str(p).upper() for p in p_tags]
|
||||
text_lines = [str(p) for p in p_tags]
|
||||
box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment)
|
||||
text_params = {
|
||||
"font-size": font_size,
|
||||
@@ -367,6 +537,10 @@ class Scheduler:
|
||||
if len(text_lines) == 1 and not wrap_text:
|
||||
text_params.update(box_alignment_params)
|
||||
text_tag = self.svg.text(text_lines[0], insert=(x, y), **(text_params))
|
||||
if self.is_url(text_lines[0]):
|
||||
anchor = self.svg.a(text_lines[0].strip())
|
||||
anchor.add(text_tag)
|
||||
text_tag = anchor
|
||||
self.svg.add(text_tag)
|
||||
return
|
||||
|
||||
@@ -383,14 +557,44 @@ class Scheduler:
|
||||
else:
|
||||
wrapped_lines = text_lines
|
||||
|
||||
for line_number, text_line in enumerate(wrapped_lines[::-1]):
|
||||
if box_alignment.startswith("top"):
|
||||
dy_dir = 1
|
||||
line_number = 0
|
||||
elif box_alignment.startswith("bot"):
|
||||
dy_dir = -1
|
||||
line_number = 0
|
||||
else: # middle row
|
||||
# the idea is that the middle row should always stay in the center
|
||||
# e.g. dy offset for 3 lines is: 1, 0, -1
|
||||
# for 2 lines: 0.5, -0.5
|
||||
dy_dir = -1
|
||||
line_number = (len(wrapped_lines) - 1) / 2
|
||||
|
||||
for text_line in wrapped_lines[::dy_dir]:
|
||||
# position has to be inserted at tspan to avoid x offset between tspans
|
||||
tspan = self.svg.tspan(text_line, insert=(x, y), **text_params)
|
||||
# doing it here and not in tspan constructor because constructor adds unnecessary spaces
|
||||
tspan.update({"dy": f"-{line_number}em"})
|
||||
tspan.update({"dy": f"{line_number}em"})
|
||||
if self.is_url(text_line):
|
||||
anchor = self.svg.a(text_line.strip())
|
||||
anchor.add(tspan)
|
||||
tspan = anchor
|
||||
text_tag.add(tspan)
|
||||
line_number += dy_dir
|
||||
|
||||
self.svg.add(text_tag)
|
||||
|
||||
def is_url(self, value):
|
||||
return value.strip().startswith("http") and " " not in value.strip() and "://" in value # Wrong but fast
|
||||
|
||||
def convert_character_width_to_mm(self, value):
|
||||
# Excel measures widths in terms of "number of characters of the
|
||||
# default font", which is most commonly 11pt Calibri. The width of the
|
||||
# "0" character in Calibri is approximately 7px at 100% zoom.
|
||||
px_per_character = 7
|
||||
px_to_pt = 0.75
|
||||
return self.convert_to_mm(f"{value * px_per_character * px_to_pt}pt")
|
||||
|
||||
def convert_to_mm(self, value):
|
||||
# XSL is what defines the units of measurements in ODF
|
||||
# https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#datatype-positiveLength
|
||||
|
||||
@@ -288,9 +288,9 @@ class BaseShader:
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# 3D_POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("3D_POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.base_shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
def get_shader(self):
|
||||
"""Returns shader for this type"""
|
||||
|
||||
@@ -160,7 +160,8 @@ class SheetBuilder:
|
||||
x = float(image.attrib["x"])
|
||||
y = float(image.attrib["y"])
|
||||
if image.attrib["data-type"] == "view-title":
|
||||
image.attrib["x"] = str(x - readjust.x)
|
||||
image.attrib["x"] = str(x + readjust.x)
|
||||
# negate y offset because view-title comes AFTER foreground
|
||||
image.attrib["y"] = str(y - readjust.y)
|
||||
else:
|
||||
image.attrib["x"] = str(x + readjust.x)
|
||||
@@ -174,6 +175,8 @@ class SheetBuilder:
|
||||
ET.register_namespace("", "http://www.w3.org/2000/svg")
|
||||
|
||||
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
|
||||
if not os.path.exists(layout_path):
|
||||
return
|
||||
layout_tree = ET.parse(layout_path)
|
||||
layout_root = layout_tree.getroot()
|
||||
|
||||
@@ -184,11 +187,11 @@ class SheetBuilder:
|
||||
|
||||
layout_tree.write(layout_path)
|
||||
|
||||
def add_document(self, reference, schedule, sheet):
|
||||
view_path = tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(schedule), "svg")
|
||||
def add_document(self, reference, document, sheet):
|
||||
view_path = tool.Drawing.get_path_with_ext(tool.Drawing.get_document_uri(document), "svg")
|
||||
if not os.path.exists(view_path):
|
||||
tool.Drawing.create_svg_schedule(schedule)
|
||||
schedule_name = os.path.splitext(os.path.basename(view_path))[0]
|
||||
tool.Drawing.create_svg_document(document)
|
||||
document_name = os.path.splitext(os.path.basename(view_path))[0]
|
||||
layout_path = tool.Drawing.get_document_uri(sheet, "LAYOUT")
|
||||
layout_dir = os.path.dirname(layout_path)
|
||||
|
||||
@@ -204,12 +207,12 @@ class SheetBuilder:
|
||||
view_height = self.convert_to_mm(view_root.attrib.get("height"))
|
||||
|
||||
view = ET.SubElement(layout_root, "g")
|
||||
view.attrib["data-type"] = "schedule"
|
||||
view.attrib["data-id"] = str(reference.id())
|
||||
view.attrib["data-schedule"] = str(schedule.id())
|
||||
view.attrib["data-type"] = document.Scope.lower()
|
||||
view.attrib["data-document"] = str(document.id())
|
||||
|
||||
foreground = ET.SubElement(view, "image")
|
||||
foreground.attrib["data-type"] = "table"
|
||||
foreground.attrib["data-type"] = "content"
|
||||
foreground.attrib["xlink:href"] = os.path.relpath(view_path, layout_dir)
|
||||
foreground.attrib["x"] = str(DEFAULT_POSITION.x)
|
||||
foreground.attrib["y"] = str(DEFAULT_POSITION.y)
|
||||
@@ -260,7 +263,7 @@ class SheetBuilder:
|
||||
|
||||
self.build_titleblock(root, sheet)
|
||||
self.build_drawings(root, sheet)
|
||||
self.build_schedules(root, sheet)
|
||||
self.build_documents(root, sheet)
|
||||
|
||||
with open(sheet_path, "wb") as output:
|
||||
tree.write(output)
|
||||
@@ -343,8 +346,12 @@ class SheetBuilder:
|
||||
def build_drawings(self, root, sheet):
|
||||
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="drawing"]'):
|
||||
drawing_id = int(view.attrib["data-id"])
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
|
||||
try:
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
drawing = tool.Ifc.get().by_id(view.attrib["data-drawing"])
|
||||
except:
|
||||
# Perhaps the SVG has outdated content or is edited externally which we cannot control.
|
||||
continue
|
||||
|
||||
images = view.findall("{http://www.w3.org/2000/svg}image")
|
||||
|
||||
@@ -383,10 +390,17 @@ class SheetBuilder:
|
||||
for image in images:
|
||||
view.remove(image)
|
||||
|
||||
def build_schedules(self, root, sheet):
|
||||
for view in root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]'):
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
schedule = tool.Ifc.get().by_id(int(view.attrib["data-schedule"]))
|
||||
def build_documents(self, root, sheet):
|
||||
schedules = root.findall('{http://www.w3.org/2000/svg}g[@data-type="schedule"]')
|
||||
references = root.findall('{http://www.w3.org/2000/svg}g[@data-type="reference"]')
|
||||
documents = schedules + references
|
||||
for view in documents:
|
||||
try:
|
||||
reference = tool.Ifc.get().by_id(int(view.attrib["data-id"]))
|
||||
document = tool.Ifc.get().by_id(int(view.attrib["data-document"]))
|
||||
except:
|
||||
# Perhaps the SVG has outdated content or is edited externally which we cannot control.
|
||||
continue
|
||||
|
||||
images = view.findall("{http://www.w3.org/2000/svg}image")
|
||||
|
||||
@@ -394,7 +408,7 @@ class SheetBuilder:
|
||||
view_title = None
|
||||
|
||||
for image in images:
|
||||
if image.attrib["data-type"] == "table":
|
||||
if image.attrib["data-type"] == "content":
|
||||
table = image
|
||||
elif image.attrib["data-type"] == "view-title":
|
||||
view_title = image
|
||||
@@ -407,14 +421,14 @@ class SheetBuilder:
|
||||
data = reference.get_info()
|
||||
data.update({"Sheet" + k: v for k, v in sheet.get_info().items()})
|
||||
if not data["Name"]:
|
||||
data["Name"] = schedule.Name or "Unnamed"
|
||||
data["Name"] = document.Name or "Unnamed"
|
||||
view.append(self.parse_embedded_svg(view_title, data))
|
||||
|
||||
for image in images:
|
||||
view.remove(image)
|
||||
|
||||
def get_href(self, element):
|
||||
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href"))
|
||||
return urllib.parse.unquote(element.attrib.get("{http://www.w3.org/1999/xlink}href")).replace('\\','/')
|
||||
|
||||
def parse_embedded_svg(self, image, data):
|
||||
group = ET.Element("g")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user