Compare commits
1831 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6aeddcabc0 | |||
| 2703dce209 | |||
| dc529d4146 | |||
| 8648b16e00 | |||
| c0a62e8593 | |||
| ef5d672d4d | |||
| 01b1f4642f | |||
| e92a84f917 | |||
| 04a2949d75 | |||
| 031a0cec59 | |||
| 5f8e77c292 | |||
| 384d007770 | |||
| e14a3a99c0 | |||
| 96260cdb39 | |||
| d7f526eafd | |||
| 55b081edf7 | |||
| ffb8c016b6 | |||
| 11697fbd47 | |||
| 4b9520a1bb | |||
| 9717d6b1eb | |||
| 2f2b4e7dc2 | |||
| f6c2a18e63 | |||
| 08d959f66d | |||
| aa41f698c1 | |||
| c6a7dfdadb | |||
| adf5af947b | |||
| 55b63410bd | |||
| 012a2cca0f | |||
| 5a722a792a | |||
| c3f7410f76 | |||
| 7c1a23e5e3 | |||
| ac95fb1aa2 | |||
| 1f4df32d04 | |||
| b09d030e12 | |||
| ed9557539d | |||
| 500467b3a8 | |||
| 1cee5c3108 | |||
| 5e84d8da83 | |||
| 1317263536 | |||
| f4ef17eea6 | |||
| 5db0f78646 | |||
| b185f56dcc | |||
| b8ab478416 | |||
| 97d34a7061 | |||
| 3e6a737cdb | |||
| 4c87ea321d | |||
| 91c6fb402d | |||
| 47d4ea00ca | |||
| f55438e871 | |||
| b1232ecb02 | |||
| a37e124d8f | |||
| c472dc2f1b | |||
| c0ed4074d8 | |||
| c94cac29a5 | |||
| f90c56013e | |||
| 572efd7ea6 | |||
| b71febab0d | |||
| bbde897113 | |||
| 4527d3a96e | |||
| 447a7a0eb2 | |||
| 92cb138ca8 | |||
| fa7353c923 | |||
| 1023c8bc1c | |||
| 276dc0d894 | |||
| 8618befa0f | |||
| 20ab036016 | |||
| 911ff4484f | |||
| b4ecdfba69 | |||
| f9521eca54 | |||
| af3ed303d8 | |||
| 9d893d2c5a | |||
| 900aa20e10 | |||
| b68f490dbc | |||
| 65f381a377 | |||
| 8b7d96893f | |||
| aa3a855b4c | |||
| f19b962ab1 | |||
| 9b6366ddac | |||
| d0b2bf85cc | |||
| 86e3d1e548 | |||
| d1e2ac4c5a | |||
| 8c36217e26 | |||
| b7f574fa30 | |||
| 395d63279f | |||
| 55d24ba1ee | |||
| 923771f448 | |||
| 2f2611e0cb | |||
| 94737da1f5 | |||
| 6a6025302e | |||
| b92fac5c5e | |||
| 824f09a1e7 | |||
| 6a8d8139ed | |||
| 36f51e19b5 | |||
| 3bbdc3f277 | |||
| c67a99fb5d | |||
| 7522f8fb7a | |||
| 84959d1cf8 | |||
| 404a6c244c | |||
| 83c22ebce3 | |||
| 28717beff8 | |||
| 02fa8c8413 | |||
| 3202174903 | |||
| 37fe54d247 | |||
| 73439e2114 | |||
| 460cb85e4b | |||
| b938e4b3c3 | |||
| 37f35a7b2e | |||
| dc41ad5ab1 | |||
| 8411333f66 | |||
| 95ce82a79e | |||
| 1d8a568a0b | |||
| 70a45be904 | |||
| e3444c60af | |||
| 4cea2b7044 | |||
| 1142c5285c | |||
| ea524767a0 | |||
| 45bba127ff | |||
| b9ba138d8d | |||
| 2ea04f97f9 | |||
| d15c5c7e21 | |||
| 9574b7e08f | |||
| 19ba6f86d7 | |||
| c5c5bbd290 | |||
| bc9f7d214d | |||
| ee0b9004e3 | |||
| 3fe5c88910 | |||
| 25fb9be82e | |||
| fa4ba58cac | |||
| 5a11ac0545 | |||
| 894bae66fd | |||
| ccb538c756 | |||
| c87baa1a50 | |||
| 30fa59960d | |||
| bdd86366bf | |||
| 142a38d402 | |||
| 257f4b2bf0 | |||
| 4e8a827822 | |||
| b10ba45194 | |||
| a04ce8a24d | |||
| 1d0b13960f | |||
| 418574fe40 | |||
| 2e07fd1cde | |||
| 0123e2a69f | |||
| 843917a431 | |||
| 2fce21fd88 | |||
| c9e85c5d34 | |||
| 521a5a5981 | |||
| c6347e30be | |||
| 21d4203688 | |||
| aefd4e3387 | |||
| 3df2c89b0a | |||
| ebd53250e7 | |||
| 6444376ecd | |||
| f5796eaed3 | |||
| a4b2285e8a | |||
| 713e70ff63 | |||
| 540f793940 | |||
| 071c84ddf8 | |||
| f2b645b73f | |||
| 7554714fff | |||
| 2d04d988e7 | |||
| 91773e8fc6 | |||
| 6ee3c8fd98 | |||
| a54b84b4c3 | |||
| addf28c1e2 | |||
| aa82cb15d2 | |||
| 2e3c5c42bf | |||
| a1be2ef868 | |||
| ab8aa95479 | |||
| b78e710479 | |||
| 9400eab0ff | |||
| 1836136049 | |||
| d1a2a12e94 | |||
| 6502b2c18d | |||
| 1779fe88ef | |||
| 497f793a6b | |||
| a85d7c66a5 | |||
| e1b68dafa4 | |||
| df2f4b69e3 | |||
| e76d401d15 | |||
| 3182c75f14 | |||
| 68767c3656 | |||
| 6976144d80 | |||
| 2a54fee41a | |||
| 3d0c9378c1 | |||
| 8ef752f173 | |||
| 06e1ee7825 | |||
| 84de1f1b09 | |||
| 2e85a80fc8 | |||
| 8eab1ec4a5 | |||
| 7871dea3ca | |||
| bf41ee013c | |||
| ef3f9489ea | |||
| be2ea335f5 | |||
| 53de93e582 | |||
| 0dcdfcf46c | |||
| 2d061f63fb | |||
| afbdedaee8 | |||
| 30d9ad1fbc | |||
| d673e845f6 | |||
| fcc2b9ee13 | |||
| 02ac150a51 | |||
| d206dff9c0 | |||
| cac635107e | |||
| 8eb9973045 | |||
| c4aecd6e61 | |||
| cef3b9537a | |||
| 02b01b2a44 | |||
| db5418cbfe | |||
| 44d3ddbb7c | |||
| b2e5c904c3 | |||
| 19d07892f4 | |||
| 113efa1206 | |||
| f76d792a25 | |||
| d74b344a95 | |||
| 30b3090386 | |||
| d4a219e54f | |||
| 23a66b3a72 | |||
| d9dbda6b5a | |||
| cbcd04c81d | |||
| 1fe2bd8332 | |||
| 6387c03dbc | |||
| 516869ba5d | |||
| 0c0ffce908 | |||
| f120f0c77c | |||
| 3961ecd322 | |||
| 23c0e279be | |||
| 6e4bb95e82 | |||
| af5350bb9f | |||
| c5ec54ca7f | |||
| efea9d429e | |||
| b91dfb401d | |||
| 979f37d7c6 | |||
| 23abc604f9 | |||
| a997b2f999 | |||
| 0ecf66f7fa | |||
| 2c6686846a | |||
| 008014a89b | |||
| b016d39eca | |||
| c773b5741b | |||
| 9060b6ee40 | |||
| c3d4ce8f51 | |||
| a5adb01dff | |||
| de5ce28a78 | |||
| 9def261316 | |||
| c844076efb | |||
| ee222628f3 | |||
| cd6c258687 | |||
| 0799a78e9a | |||
| a073015a9a | |||
| 01d59a1bc2 | |||
| 453dbd65ac | |||
| 7ae685dbff | |||
| 0d457ba0ce | |||
| ef8b3d60b7 | |||
| c502d39aaf | |||
| 397a09846e | |||
| ceb7b0e13f | |||
| f49174cab4 | |||
| 17e805e144 | |||
| 5c92bd7348 | |||
| 7c45181a6e | |||
| 82468370a7 | |||
| 7f49ebea55 | |||
| c0381588f3 | |||
| 37e56c4011 | |||
| ea83238e7a | |||
| fa6df3fa30 | |||
| 424069893e | |||
| abb34d33e4 | |||
| 36a0119f40 | |||
| 9fc5d97488 | |||
| f277733ff5 | |||
| 510d756cc4 | |||
| bca9c82c3d | |||
| 3deba2e71e | |||
| d5566785be | |||
| 6cfa9f6e9b | |||
| 6a00f2f0d2 | |||
| b9583c562d | |||
| e51b7d3c85 | |||
| 5234c8086f | |||
| 7e306251c1 | |||
| 8d05ceaea9 | |||
| abaae7f06d | |||
| 3c2c591d6d | |||
| 315b32dd38 | |||
| 6699f2dccf | |||
| f235f5765b | |||
| 877bf3207a | |||
| 119a729319 | |||
| 1b9b005820 | |||
| 1bea18d3cf | |||
| 1b6857b8ba | |||
| 666641bb18 | |||
| 67f7e68fa7 | |||
| 7b2fa3e877 | |||
| 4d2be3e3c5 | |||
| 5a1f2ec4fe | |||
| b22055a588 | |||
| 27051cce8a | |||
| 4df8418e72 | |||
| 6dfeaf5509 | |||
| 30f8350156 | |||
| 7a7caffaae | |||
| a5530acfd7 | |||
| 8baf9b056d | |||
| 6f84bc6ce5 | |||
| 6a5d255c2f | |||
| ee42703f9f | |||
| 2226d5a565 | |||
| 7d138b8f72 | |||
| edd6e74813 | |||
| 2a6efaaaa4 | |||
| dafdd06b2e | |||
| 1a48472b19 | |||
| 8e78ed332f | |||
| de5dc40e63 | |||
| 60dd74c64d | |||
| ff0a7891be | |||
| a14c0f70a6 | |||
| babbd51e70 | |||
| ec02c8600b | |||
| 0c06639635 | |||
| 97913e8592 | |||
| 26d537cb4f | |||
| 3b856f446e | |||
| b8fe04b7a9 | |||
| 81326ba48f | |||
| 21a7599d45 | |||
| 1074c2431c | |||
| b77aa11776 | |||
| b6cf97a291 | |||
| eb0f2de197 | |||
| 1a48343bcb | |||
| b0bd027f84 | |||
| 8bf530d0d7 | |||
| a3bc159359 | |||
| 7d756138f2 | |||
| c16b63e5ae | |||
| 3eee5aba78 | |||
| 05d835410d | |||
| 03d8537a2d | |||
| 509ee78b67 | |||
| 8cc546a9a2 | |||
| 0ab82295f9 | |||
| 0f66f88420 | |||
| 5572faf8b4 | |||
| 322860cc51 | |||
| 1bff04d2c0 | |||
| de4f18e2af | |||
| 03dd4c39b7 | |||
| fc7d05c691 | |||
| 854a0fe863 | |||
| 9ffc2874da | |||
| e01b677845 | |||
| 85a78a6e48 | |||
| 3ae20637d7 | |||
| 7dd40149aa | |||
| d08da0a61d | |||
| 1b09bb37e8 | |||
| 90f225e93b | |||
| f00040c7d9 | |||
| b8286e8fb5 | |||
| dbafcc2fa1 | |||
| a869e88de0 | |||
| 5ad6abea1a | |||
| d00da09cdc | |||
| 17a8b33387 | |||
| 4f2bf3a36f | |||
| e208dd0381 | |||
| a62a32d414 | |||
| 6a06c292a1 | |||
| 9dcf44fbea | |||
| e39a997f5f | |||
| f3269b732f | |||
| 14c726008d | |||
| c0b4115202 | |||
| d5e6721ac4 | |||
| 48de2f77ab | |||
| f37283f61c | |||
| cc5f34a705 | |||
| 33bf7054bf | |||
| 14193fb486 | |||
| 34174dd3f3 | |||
| 479b937f81 | |||
| cada9c9e3b | |||
| 860d795ba1 | |||
| 2ac0b75dbc | |||
| fb7998980d | |||
| b9af4906aa | |||
| ce6350a52f | |||
| b65c1e94ad | |||
| 0387783e9d | |||
| b72b2aa7d7 | |||
| c1bbb1e721 | |||
| 0d28e741fc | |||
| 0d03b4e008 | |||
| 981f0c23c7 | |||
| 884e8a5663 | |||
| 06685f9768 | |||
| 9a20de914b | |||
| 26f6e875ed | |||
| cda131d6c1 | |||
| e1fa6fb306 | |||
| 679c2bef02 | |||
| 8bb157ce0a | |||
| bdaf8cd0f3 | |||
| 4e62c276ee | |||
| c70b41f21a | |||
| aaf9b10a9e | |||
| 9281c8fbce | |||
| 5f76062575 | |||
| efd2663f15 | |||
| 823b10e323 | |||
| 431de8d5a1 | |||
| dce730d9c2 | |||
| 50d7efa99b | |||
| 957c719224 | |||
| b407b815e2 | |||
| ac12a6785c | |||
| 8aae7c95e6 | |||
| bbde4e7522 | |||
| 803a4b6c12 | |||
| 5a06bd5a57 | |||
| 51dca4329c | |||
| 1921b4cecf | |||
| f7e05e011a | |||
| 2596f90015 | |||
| 24c542e33a | |||
| 7b7f756506 | |||
| 9ca124bf2f | |||
| c9666015bb | |||
| 8815b74ff8 | |||
| 98f45817a6 | |||
| e3f61510ad | |||
| 864dcffef8 | |||
| fae899823b | |||
| 26d35a7644 | |||
| 4a5fe83dab | |||
| 78666caa47 | |||
| 3ad2d0eaac | |||
| 7ad35249b8 | |||
| 259d467164 | |||
| cfa97be080 | |||
| fddea742b8 | |||
| 6ebb69f62e | |||
| b0097d35b4 | |||
| 15ea15d9ab | |||
| b88eb37bdf | |||
| 5ce334019c | |||
| 15199152ac | |||
| e346d5e3c6 | |||
| 957cc02157 | |||
| 32f99428c3 | |||
| acb518613b | |||
| 25a26db360 | |||
| 5cbc924c85 | |||
| 4bff3dc3dd | |||
| a5b00f1ac9 | |||
| a9a8213731 | |||
| 33b2661464 | |||
| 3085c88647 | |||
| 7ec91728be | |||
| b1c4481795 | |||
| dd4902be18 | |||
| c057cb9aab | |||
| e2543954c0 | |||
| 7ba812c6c9 | |||
| b15acfed42 | |||
| 648c976dd4 | |||
| dd0a3ad7b8 | |||
| b37ddb766f | |||
| 7cc832ea15 | |||
| fc5fcef079 | |||
| 280466df53 | |||
| 2bd44e2fe8 | |||
| 9a87f3673a | |||
| 5af85ee7a3 | |||
| e150e5f6c3 | |||
| 44dc5a92b7 | |||
| 1f938c7f1a | |||
| 225f8c788b | |||
| 17d069e9b5 | |||
| 21b9557b97 | |||
| 76b29cb174 | |||
| 1c24a59075 | |||
| 9080744669 | |||
| 47cd183e61 | |||
| 9dcb2f9094 | |||
| 2e762854fa | |||
| 819b62a747 | |||
| 599089ad67 | |||
| ddfe0e0c98 | |||
| 27a8e7c89a | |||
| b9e34a107b | |||
| df3bb0428c | |||
| 728b60e8c3 | |||
| 808ef1a19e | |||
| d6397baa59 | |||
| ecdc05d265 | |||
| 7bb5860200 | |||
| b3d180a288 | |||
| a9ae92883d | |||
| 1866b7852a | |||
| aca90b38c8 | |||
| 2eae3a70d9 | |||
| 26510de9e0 | |||
| 2a585c98d8 | |||
| 9f86396edf | |||
| 3477d3f1a2 | |||
| caa1677181 | |||
| 84509b7cc2 | |||
| 09847513ab | |||
| 40e30eaa36 | |||
| 527e6b0ca7 | |||
| 8bdd24ba16 | |||
| 4a6c980779 | |||
| 47af56cfa0 | |||
| e186bc8e15 | |||
| ba094f2967 | |||
| 8c507ea007 | |||
| 640dc7cf5a | |||
| 28d9bccb6e | |||
| 1aa3e70d89 | |||
| e4662cf209 | |||
| f17702c66a | |||
| b8e0c21dcc | |||
| 4ea56ba987 | |||
| e793683668 | |||
| 9645b9f189 | |||
| a0d2bc25b1 | |||
| a1cbf9a055 | |||
| 34e5cd9d16 | |||
| b37ea77934 | |||
| 7827b15479 | |||
| f8e57b04b0 | |||
| 6423d13054 | |||
| 3ca2a8be39 | |||
| 388ac5740d | |||
| 7825adf8e0 | |||
| 1eaa2ca8b4 | |||
| e366c15dde | |||
| 63f0c87920 | |||
| 247a285b68 | |||
| ad9382de08 | |||
| 05c952073b | |||
| 9a385c2118 | |||
| 5928ddba40 | |||
| 19b867dae8 | |||
| adf9ac52b2 | |||
| 7c63370548 | |||
| b8c3da4951 | |||
| 322f3773e4 | |||
| 990e6ee24e | |||
| 87e25cc597 | |||
| 5009371e0f | |||
| f5c57d2a1c | |||
| ba09e08da5 | |||
| 4b2c129a4e | |||
| 2fb67b09cd | |||
| 50b8718e3a | |||
| e306da65c9 | |||
| e57ffa9447 | |||
| 35a668f3a8 | |||
| 6ecf7a80f5 | |||
| 4fe44d0f47 | |||
| 227ec74a51 | |||
| 9560211307 | |||
| 617cad0b1a | |||
| 30afd6c95b | |||
| 04943b216b | |||
| aed4706e4c | |||
| eeef2cfb3f | |||
| 3240ec63b7 | |||
| ae25d97307 | |||
| b07d999bce | |||
| ad1cc7f0b9 | |||
| 81cba70f33 | |||
| 8b0f0fe60f | |||
| cde2d55694 | |||
| 67baad5182 | |||
| d9ecd20414 | |||
| 0def2a9b13 | |||
| a75ced62ce | |||
| 96b8bc638b | |||
| ba8689468e | |||
| 2b1a7eec47 | |||
| 35bcaa804b | |||
| 8660d22476 | |||
| 3bc4e756cb | |||
| 163075a821 | |||
| 286c77e3b0 | |||
| 19c8d13e86 | |||
| 2c9d6a47f4 | |||
| 4a6ec11f6f | |||
| d169a964dd | |||
| 892be5444c | |||
| d6881e833d | |||
| c14f5eeca0 | |||
| c263012287 | |||
| 5aae70875d | |||
| 0fb6d5a903 | |||
| 1a570aea83 | |||
| 2669819292 | |||
| a9c986020b | |||
| 9d6468bbf6 | |||
| 5c89a386ca | |||
| 348c58a387 | |||
| cb0a5c90e3 | |||
| 6267187950 | |||
| 3575aff235 | |||
| 54d5d4cc63 | |||
| f2bdcda369 | |||
| 4020e9a1ce | |||
| 1d51d6cac0 | |||
| 9447e87ebd | |||
| d1f8832af6 | |||
| b08346cc64 | |||
| 3fdcf3c7b7 | |||
| 84c0ad9ded | |||
| 0061d9d1a9 | |||
| 6e589527db | |||
| cac5203523 | |||
| dafa6a546c | |||
| e08260d6a2 | |||
| e42f96faa7 | |||
| 23abd3def4 | |||
| 5e844682ea | |||
| b2e7809ee5 | |||
| fb75906fab | |||
| 0a6d5a733a | |||
| f15d62c35e | |||
| 970f735b76 | |||
| aa49db07d3 | |||
| 6fd5f8d8c7 | |||
| e70241fc53 | |||
| e68f25bcfb | |||
| 3bd58b7738 | |||
| 76af85264f | |||
| 473d97649c | |||
| e1c1cf6c9b | |||
| 2243e0ab14 | |||
| a25c6245bf | |||
| 0b6d55a8a3 | |||
| 9eaeea5462 | |||
| 483fb34e3a | |||
| 001fbeb525 | |||
| 8fd685b4dc | |||
| 1f76f73aa6 | |||
| ef0f00792a | |||
| 5ed0b4d972 | |||
| f3282003f4 | |||
| b534dc2199 | |||
| 5df723b9db | |||
| b5d43b1d58 | |||
| 4ebed8d029 | |||
| 25a5b435a6 | |||
| 8872a13fd8 | |||
| ee6e96c226 | |||
| 4013a7e8b7 | |||
| 1a09658ec9 | |||
| 982166ac62 | |||
| 44d6060924 | |||
| 95d6685597 | |||
| a625a8aea3 | |||
| 7131c62600 | |||
| 88e20582dd | |||
| 56209fa0ec | |||
| d7e2f29eae | |||
| 2ead44e1c5 | |||
| fd18916b20 | |||
| d7f246c86d | |||
| 38e5321dbb | |||
| 275fa479d6 | |||
| 60147a8ec9 | |||
| 8b982df1c0 | |||
| 688c799d1b | |||
| cc778778ee | |||
| b5fa993531 | |||
| 85e7ed380e | |||
| fcc056e204 | |||
| 16663d4db3 | |||
| c24a91c42a | |||
| 681fd43599 | |||
| 1fa7791b8b | |||
| 38e5b17b0f | |||
| ac40b90f27 | |||
| 11657207c2 | |||
| 8982c8d045 | |||
| 373fd417eb | |||
| e3831be16e | |||
| b4f4d9b3c5 | |||
| e70d08c3a1 | |||
| a13e28930a | |||
| fd90d558b3 | |||
| 88bfa6fa96 | |||
| e17c6e4c9f | |||
| ee1e9b4aec | |||
| 64153eb39a | |||
| f489487a20 | |||
| 44306b036d | |||
| bead7192fe | |||
| c01fd01c92 | |||
| 788c5b32f3 | |||
| 08d773e346 | |||
| 5864de6dff | |||
| 2dd1a00834 | |||
| 76896fe9d6 | |||
| 46c82d07d0 | |||
| 3e9210d29f | |||
| 0fba3bde2d | |||
| ca8cf3620a | |||
| 8f43a65b74 | |||
| bf57258895 | |||
| cbeaee8501 | |||
| 32ebd08158 | |||
| 757e3b5272 | |||
| 6cc4ce46c2 | |||
| 2310bf628f | |||
| 85d77798c3 | |||
| d4a629cbe8 | |||
| 3440dd4bf7 | |||
| df886b3a6b | |||
| ce831c0e05 | |||
| 1a04e54f03 | |||
| aac61a8948 | |||
| e25629e64f | |||
| 518c3eb6f0 | |||
| c6a5bdf4eb | |||
| 9c591ce285 | |||
| a70429d2c8 | |||
| 970832411c | |||
| 38765c8f0e | |||
| 681083e757 | |||
| ac2494968c | |||
| 68a821b0ec | |||
| 7f95759df0 | |||
| ca6adffc25 | |||
| c58db675e3 | |||
| 1494657368 | |||
| d506ad77b7 | |||
| 95a29dec7b | |||
| 27f358b314 | |||
| fa4ff3e46b | |||
| 44565b320c | |||
| f2de8ce8df | |||
| 6ceda94cd4 | |||
| 635e84610e | |||
| 94bd8da75e | |||
| 627be5725a | |||
| cd5786d2a7 | |||
| 9c190d74bb | |||
| b43f77bc01 | |||
| f351d80453 | |||
| ffebc44c64 | |||
| 22d7aef7be | |||
| 36bd903736 | |||
| 24c00c0235 | |||
| 14c824dfee | |||
| dae5adc38b | |||
| 57bc6f7763 | |||
| 45b805b05f | |||
| 0522ea61f6 | |||
| a2f24a265a | |||
| 0583a5f430 | |||
| 7e80615fe0 | |||
| 71fffa41a9 | |||
| b2e6075aa3 | |||
| d1852b2415 | |||
| 0b0876877e | |||
| 147bf664cc | |||
| dbbc56ef0d | |||
| 9c8f8ba56f | |||
| 73e5f1adf2 | |||
| 311174b1e7 | |||
| cba30f0831 | |||
| 03b8503049 | |||
| 3c8c411dc5 | |||
| 2e6150b5ef | |||
| 55a54098dd | |||
| f1269c7618 | |||
| 6ac56cbcf6 | |||
| 6f6a1aede6 | |||
| 1e3dc6ba16 | |||
| 1b2c8bdf8e | |||
| 9107c65ad0 | |||
| 8d80c58e2d | |||
| 9c50e909e3 | |||
| b31caf3310 | |||
| fac949cf95 | |||
| 880a269e21 | |||
| aca70434be | |||
| f6280dec68 | |||
| 4c4bc8ffd7 | |||
| 2f4c96b9dc | |||
| bc2ce0ee62 | |||
| 77d686724e | |||
| c5dd9a24cb | |||
| 6bc05c5bdc | |||
| 0c145dd885 | |||
| e7aa8d0e27 | |||
| c368ae6596 | |||
| eea7b6547e | |||
| 6cbf8cc8db | |||
| d2fcf07b37 | |||
| c552e90897 | |||
| 345ef27b15 | |||
| c872ee8844 | |||
| 34c51fa9be | |||
| 5c20e7b62a | |||
| c15fdc7fa9 | |||
| d0c207f6b9 | |||
| 1893f6cea4 | |||
| a2fa3a39e9 | |||
| 628f9a2879 | |||
| 6ec368901b | |||
| af05f72010 | |||
| a66b4f138a | |||
| 609a2d3a7d | |||
| afd6811c08 | |||
| ff5bc25228 | |||
| 09dd778f97 | |||
| 668edbc45e | |||
| f64e0c9acc | |||
| 99a76b5a6e | |||
| 3968bb2bf5 | |||
| df382d3e64 | |||
| b6f51bc080 | |||
| cf6ec3777f | |||
| 48f3d52880 | |||
| 7f48923153 | |||
| eac72dc852 | |||
| d3da378ba3 | |||
| 78d33b4ffd | |||
| db0c266a17 | |||
| 75b34b6a95 | |||
| 1978ffa8df | |||
| d4afc6d1c0 | |||
| 6eee9476a6 | |||
| 45c16e5318 | |||
| f2884f9b0f | |||
| 431e703137 | |||
| 31c349b0f1 | |||
| b7bb7815ad | |||
| b5acf81be2 | |||
| 2c79f07abf | |||
| bd02c8e34b | |||
| c359e35747 | |||
| 0a7c365d78 | |||
| a1e5135a9e | |||
| e9e0dbabe4 | |||
| 75b14d4c15 | |||
| 217098132f | |||
| 6c064ecd51 | |||
| cc4a29fc11 | |||
| 7386da14dc | |||
| 0a657be394 | |||
| 9062d9720b | |||
| 43186e4e59 | |||
| 3254bc3c14 | |||
| cee53689aa | |||
| afb2aaab5a | |||
| e9b791adaa | |||
| 7f6c9e61c8 | |||
| a7c76b1f48 | |||
| a47d1ac32a | |||
| 01d0d125a6 | |||
| 5db2a46369 | |||
| 4c3913353c | |||
| 71e59922ec | |||
| d56dd25245 | |||
| a477a2046d | |||
| f6e8d5e281 | |||
| 0d221fa759 | |||
| e2fd9add83 | |||
| e858930755 | |||
| ffa4e7570d | |||
| cd6bd0e3cc | |||
| ef41daedb9 | |||
| 114e835d4a | |||
| 12345b14ac | |||
| 1e02353922 | |||
| 6c868033e3 | |||
| 1e6e6a4977 | |||
| f9bf6f814d | |||
| 06b512c2e3 | |||
| 64db48276c | |||
| 34fc2136c6 | |||
| fb5c25bb68 | |||
| d12643197e | |||
| f58847502e | |||
| 57d44c9066 | |||
| b1842de9fa | |||
| f2dcd8bb75 | |||
| 829b97a0d8 | |||
| be4fe1b684 | |||
| 09af3b052b | |||
| 3292b98080 | |||
| 11a66ee181 | |||
| 7d976b1bc7 | |||
| 43f0f396ab | |||
| ec66d7c0b8 | |||
| c79ef17a58 | |||
| 0dbe7b9cc1 | |||
| 1d2677543d | |||
| e56648e393 | |||
| 562dc72129 | |||
| bc8b377d6d | |||
| b60438b51c | |||
| 277786771c | |||
| 61f6ecbf17 | |||
| 6772886e65 | |||
| 2a22acfdc9 | |||
| 323dddf082 | |||
| a2454cf5ee | |||
| 91917dcbb4 | |||
| 97f03d3933 | |||
| 0d28978a48 | |||
| 8ad9c215af | |||
| ecebb4b853 | |||
| 3e985facdd | |||
| 6c7ddcca9c | |||
| e9cd48c62d | |||
| e63f083a40 | |||
| a376ec1b12 | |||
| a3e38d2322 | |||
| e7446dc6cb | |||
| 75cff21efe | |||
| 5c2ded1486 | |||
| 8197621232 | |||
| 0cc816922c | |||
| a337c480a9 | |||
| cc40726618 | |||
| 7f58df876b | |||
| 330aa1b6e3 | |||
| 252479189c | |||
| f56269301f | |||
| d87ea93e89 | |||
| 1d6f0c8e63 | |||
| 9359c388d1 | |||
| e2621e0207 | |||
| 2837f75d50 | |||
| ab1f7337a5 | |||
| 21a4680811 | |||
| 019f460af2 | |||
| 2a7c406412 | |||
| 1018118341 | |||
| 5fbb324d7d | |||
| 7d4c760332 | |||
| d636775cad | |||
| 18a2996a93 | |||
| 4f1aca8ceb | |||
| 416640891f | |||
| 02bb7bea92 | |||
| f92d1708f2 | |||
| 37e32738b9 | |||
| 2d3ede02da | |||
| 95f97efa72 | |||
| a89f7dc116 | |||
| f1ed9d5b53 | |||
| e7afa0b3e4 | |||
| 20303ed6a8 | |||
| fdfe1097c4 | |||
| ea71a1f8a7 | |||
| 9d6e7c2481 | |||
| 41c539965c | |||
| ab432ba3dd | |||
| 11472f9aa9 | |||
| 1ec78e57ce | |||
| 418e4133b7 | |||
| 2e1a32d540 | |||
| c885652338 | |||
| ee4a93c0d3 | |||
| 9f63f42e6e | |||
| 4d30be716e | |||
| e3de339ab2 | |||
| 2a6b3b981a | |||
| a8d261f271 | |||
| 6c22894a93 | |||
| 6e8eca37ef | |||
| 63e65b2114 | |||
| 1222716e16 | |||
| 98ed21a1e2 | |||
| 093ebb2d8f | |||
| 42f597a7af | |||
| 88331d16a2 | |||
| fa1926fa84 | |||
| bdbe988dc7 | |||
| 6b07aeb703 | |||
| 4015c5b5b2 | |||
| a62fd01725 | |||
| 58fad83d4f | |||
| 1e916896ab | |||
| 803cc66abb | |||
| 8500daf1cc | |||
| 4baec5754d | |||
| 607e506767 | |||
| 65821c4367 | |||
| fd283b9c84 | |||
| 2132706c6e | |||
| e781dbd789 | |||
| 07f8f8c489 | |||
| a7e1eb15ab | |||
| a27b00e616 | |||
| c7211ed171 | |||
| fbaa6cd2ed | |||
| e2868f871a | |||
| b866106293 | |||
| fda508e3f8 | |||
| 436eb3a637 | |||
| 155a1eeca9 | |||
| 79fdfa878c | |||
| 9a026d93fb | |||
| 125c0ced6e | |||
| 36b262dff9 | |||
| 4d8b130f2d | |||
| d22d903a36 | |||
| c76c72c017 | |||
| 3e9fff6c19 | |||
| da7ba7b1e9 | |||
| 794e55aadd | |||
| ebc305c0e1 | |||
| 358d9ef1c8 | |||
| a931c5ced9 | |||
| 189177300a | |||
| b77ed5aab6 | |||
| 501447fa5f | |||
| 8962e3bcf4 | |||
| d3f07c4ac4 | |||
| 1280eed00e | |||
| 391ae80e36 | |||
| 67754ecfb8 | |||
| e155cb4ab0 | |||
| 89a7584d0c | |||
| f01ae4a45b | |||
| b8d0e22aa8 | |||
| e2784fb69b | |||
| e9a9c1ab7a | |||
| 724d8c405b | |||
| 4ca158e47b | |||
| 32511bd9e9 | |||
| e1af6d34e4 | |||
| 6e2038c1f5 | |||
| 76e7205692 | |||
| 7e362110d2 | |||
| 7b98e48b9c | |||
| d8373715e2 | |||
| 9961f6172f | |||
| e2e217700d | |||
| 2035cf64c2 | |||
| 8b7850b3ff | |||
| 2f8ea33da5 | |||
| edc3aa6157 | |||
| 4a6868495e | |||
| 9ae3adf0d7 | |||
| a8a51eb212 | |||
| afb05f42db | |||
| 2a42a582fd | |||
| 86d7205760 | |||
| c8cca08dfa | |||
| 603cf011c3 | |||
| 91bfd116e8 | |||
| 95c953fd52 | |||
| 0237f12ceb | |||
| a17b211983 | |||
| a575b701b7 | |||
| 7c7587f167 | |||
| 793c1c8ec6 | |||
| 9c111a1470 | |||
| f21ae53950 | |||
| 8ab42cf09f | |||
| a6a4cb81c5 | |||
| 619abdae51 | |||
| f5d01a90a9 | |||
| 3a8a4b7c86 | |||
| d0f02f9247 | |||
| 12bb14da48 | |||
| b708c25f90 | |||
| 4f1514022a | |||
| 43abb87e82 | |||
| 2e19d7697a | |||
| 72a1c00e46 | |||
| fa461502a0 | |||
| 0ec205ad1d | |||
| 82ada5c102 | |||
| 9fa89651c4 | |||
| 6aa32fb77a | |||
| be96cee77d | |||
| 4ffa658faf | |||
| 1eea532c9e | |||
| ee7ea5445a | |||
| 7e77c0c92c | |||
| ec8a07ec6a | |||
| bdf17e5c12 | |||
| e071c7812f | |||
| 8bef6b9347 | |||
| 11b3f1c70c | |||
| e75cc10988 | |||
| 0d93633326 | |||
| 295b2fb56b | |||
| d62bbd995f | |||
| 1361c7b7e5 | |||
| 3b81a69591 | |||
| 72a2ab93c3 | |||
| d3590c2325 | |||
| 5ead8995e1 | |||
| ea1c3079cb | |||
| d81196316d | |||
| eba631d245 | |||
| 2ee9dafd16 | |||
| 839ca49a7a | |||
| 3d06af0001 | |||
| f081c9dbcb | |||
| bc56196f27 | |||
| e45f5d867b | |||
| 35ea0b1b8b | |||
| 3427b99521 | |||
| 8fe4eb1dba | |||
| b08fb47a47 | |||
| b0bb05bcdb | |||
| 6193a18734 | |||
| c7e3dba8c4 | |||
| 1ac37d9110 | |||
| ca4b257188 | |||
| ef5f86273e | |||
| e44eaf0ccd | |||
| 3235d2495a | |||
| 81b3711c33 | |||
| 588549ca91 | |||
| 64fe2e978e | |||
| 3e223b53e4 | |||
| 64c0679a7e | |||
| 67cf93d182 | |||
| fd821f3553 | |||
| e3c36a28f2 | |||
| 480a1100ea | |||
| 25f5407933 | |||
| 395d5ebea7 | |||
| dd525d8a30 | |||
| 32c621139b | |||
| 5d7a841380 | |||
| 619416f7ad | |||
| df12d6bab1 | |||
| ad4e7fa14f | |||
| df62643fc7 | |||
| 42f325295e | |||
| 48341176df | |||
| 754ebaba1f | |||
| 7728e83f1e | |||
| 42634e9c60 | |||
| 1d7865c558 | |||
| c481b77e73 | |||
| 4871243445 | |||
| 1a31999abf | |||
| b8f790ca8b | |||
| 1c82d8c936 | |||
| 58a40316af | |||
| af11ebdc4f | |||
| 2a8251fc94 | |||
| 561009c330 | |||
| e270cf6989 | |||
| 752a7c9b1c | |||
| c83af5fc2a | |||
| b857d9a37b | |||
| ac49d1f7aa | |||
| 1b237ccefe | |||
| cdbe3433c0 | |||
| 6300b27ac4 | |||
| bbc429bb99 | |||
| 673f12dd8a | |||
| 0af8902dde | |||
| 4ed3517c47 | |||
| ef88f574c1 | |||
| 9fca1aca3a | |||
| 9c5c05202f | |||
| c9c2f76960 | |||
| 23f5c808e8 | |||
| 956e9ab034 | |||
| 0288af196d | |||
| 9384cc04b8 | |||
| 01e82620aa | |||
| fa8b12497c | |||
| 534ede4839 | |||
| c3b04d554c | |||
| 3fedbad7bb | |||
| 11362becc8 | |||
| 2d2c5cfc3d | |||
| 3ce71e855f | |||
| dce5c40007 | |||
| 7410d75ef5 | |||
| be2556aa0c | |||
| 97ac0e09b7 | |||
| 61a2eb4a43 | |||
| 7deedf6ec9 | |||
| 9e250a9616 | |||
| a712861633 | |||
| 280c7df79a | |||
| 47eb032689 | |||
| 741ae2575d | |||
| 70f298baae | |||
| 04afc101a3 | |||
| 784a394298 | |||
| 6e0fb6145e | |||
| 7b5d8a4079 | |||
| 9d9911c4c0 | |||
| 7ac2dc6712 | |||
| ce00b3d5f8 | |||
| 38167af916 | |||
| adb4a3f72a | |||
| a34a05262e | |||
| fcd528abfa | |||
| 3eee143bed | |||
| 203fcc1dca | |||
| 2fa49d36d8 | |||
| 7bb7d91c7e | |||
| 1fdd3352ec | |||
| 6d26e1c31f | |||
| bb935bf167 | |||
| 01cd5d1d79 | |||
| 9f5ef32173 | |||
| d65f2b432b | |||
| b8ea5dc3be | |||
| cbfd41ad28 | |||
| 753fdae0d4 | |||
| 011c50a042 | |||
| a695eecd9a | |||
| 3d45f9afeb | |||
| 82e38e3abe | |||
| 3671953472 | |||
| b250472d60 | |||
| 2ee0cda83e | |||
| 186da7ce8b | |||
| 4db3b83681 | |||
| 13fef5a452 | |||
| 80863f94d3 | |||
| 9ad57d658e | |||
| a90ad6e16c | |||
| 19130b109e | |||
| 9217af315b | |||
| 13d434c00f | |||
| 2bb57aebb5 | |||
| 048966bda2 | |||
| ee69bfedcb | |||
| f63ac9609b | |||
| 7adf4934aa | |||
| 66afa9872d | |||
| 4e0347fb05 | |||
| 442ab790d4 | |||
| ddc37ab4d1 | |||
| c3a795c825 | |||
| 88bb7da0b2 | |||
| d180c4732f | |||
| bdfe197c64 | |||
| 42fedf81c4 | |||
| c93a4b6808 | |||
| 3562b566d0 | |||
| 65a9d6ac50 | |||
| 2e21c97d82 | |||
| efc8533c2d | |||
| 407e64632e | |||
| 3d1c796f14 | |||
| 2db3cf0aa0 | |||
| f59f017bde | |||
| abbaef459b | |||
| dd1b0a1056 | |||
| 79b741225b | |||
| c78bb18c8d | |||
| 4cf3479036 | |||
| 8bf8cdf51b | |||
| a59633ae0b | |||
| f6db62c758 | |||
| 44b49f4ce9 | |||
| b6e97dd26c | |||
| 67bb18b580 | |||
| 4786dc78a3 | |||
| fb51e85f79 | |||
| acf1289d50 | |||
| b8edcb880d | |||
| e86839d2db | |||
| 3049d24f6f | |||
| 28d5d11474 | |||
| 503fcee432 | |||
| 4c3051267e | |||
| df14ab57bf | |||
| 374d5a5d1d | |||
| ee288bf7f9 | |||
| 96ab539a0f | |||
| 1662b11fb2 | |||
| 7ee385866a | |||
| f3590f2b7b | |||
| 22e4eb4570 | |||
| 82c38f67ba | |||
| 4c58cec393 | |||
| 02e29955bf | |||
| f4681b2053 | |||
| b41d3ed2e6 | |||
| 55f3f20531 | |||
| a6c88a1d01 | |||
| 3ff4b056b7 | |||
| a5fd66706a | |||
| 293ba0db51 | |||
| d5d36f85be | |||
| f921b6a7e7 | |||
| fe633d9698 | |||
| c4cd6ef710 | |||
| df81490f86 | |||
| c99bf6e63a | |||
| 46c878341d | |||
| 21c9598409 | |||
| 03a23958a3 | |||
| f37e5e5d83 | |||
| bf4eb330e0 | |||
| cdb7a00f0a | |||
| 8613834129 | |||
| 8625aab5f0 | |||
| 7e6ebbebe0 | |||
| ed98983687 | |||
| 3a590e4ba1 | |||
| 1971bfe014 | |||
| dd98ac3a89 | |||
| 2678dc3a09 | |||
| f2e203d699 | |||
| 52ff61a91c | |||
| 8a22169036 | |||
| 7c66c5e4fc | |||
| 44d884590a | |||
| 662a9fdad7 | |||
| 648aaaa778 | |||
| a8cb855613 | |||
| 7079e2aee2 | |||
| 65a5dcbe25 | |||
| 5454521c2a | |||
| 9932e7f012 | |||
| a7b52b5578 | |||
| 114611d173 | |||
| 3b8b0cbbbf | |||
| 83bba733c7 | |||
| c991b9812f | |||
| 5454c5b1e7 | |||
| 7fe067498f | |||
| ea71485f3b | |||
| 41acb6c985 | |||
| 772468d1ec | |||
| 296ae71606 | |||
| 160c055906 | |||
| 86b02a9adf | |||
| 30b28d5a8f | |||
| d02182793b | |||
| 358323492e | |||
| e99c775d51 | |||
| e068776141 | |||
| 130d732745 | |||
| f24db3402f | |||
| 0cfbde13b9 | |||
| 9a99f85026 | |||
| 481c6165ca | |||
| dc8e73b822 | |||
| 8ba9b37ec9 | |||
| a5b5b10d29 | |||
| 3e55297eb0 | |||
| bd27ff150a | |||
| cb872a5bff | |||
| f540e3495c | |||
| 9ce34ce395 | |||
| 608abf2e55 | |||
| c6e10abc51 | |||
| 4df2feabe2 | |||
| 3c355b4848 | |||
| a7c14bc7c3 | |||
| 3a91ed1382 | |||
| e2cfc676d8 | |||
| 8008ad16b1 | |||
| 5dd919f028 | |||
| 43348ce236 | |||
| 9f2443e01c | |||
| c8cb9d8a7c | |||
| ffc8ec8c8d | |||
| 9cab063671 | |||
| baf5f1891f | |||
| bdc8caa5f6 | |||
| 7a5ec24d8d | |||
| eac8925134 | |||
| c3ffe9c3de | |||
| 6c47ff73fd | |||
| cfe18d41d6 | |||
| 5beb3dc755 | |||
| e44221c9c2 | |||
| 56e3353879 | |||
| 861dab0d9e | |||
| 7c97ad05fd | |||
| bf55214e1a | |||
| 207359ec2c | |||
| 1f89a00896 | |||
| b449f8afaa | |||
| 2d53c691f5 | |||
| 3b0585ae4b | |||
| 2e6f5c1bd9 | |||
| c87903d3d7 | |||
| e03e961a2f | |||
| c7c3d366bc | |||
| 691cc8759a | |||
| 2786d7a20c | |||
| 9571d4a67b | |||
| bb462659a3 | |||
| 85e55b6deb | |||
| e354354b1a | |||
| 56a2f9eedc | |||
| 9cf81d5c7b | |||
| 1800ebc893 | |||
| 9895ff3c22 | |||
| 52fae93a14 | |||
| 5a3303837d | |||
| f501aaeb01 | |||
| 98e082ccac | |||
| 9e67b05df9 | |||
| f80e700997 | |||
| 2f1b5ff2d4 | |||
| 9b5265e6fd | |||
| b88739eed8 | |||
| 3108d3ecfe | |||
| f78a0ec88a | |||
| afaab7c9e0 | |||
| 8dce661830 | |||
| 47723b4e63 | |||
| f50722c4bc | |||
| a34588179d | |||
| 3fb268f687 | |||
| 368f76b713 | |||
| 09e4fe56f4 | |||
| e077235eee | |||
| d9805185ee | |||
| f58d9243fa | |||
| 9a2f101df7 | |||
| af278b5724 | |||
| 71f836d013 | |||
| 8490dc7841 | |||
| 2dbcd6712e | |||
| ef919ab431 | |||
| 29b7b2963e | |||
| 8ae8042d65 | |||
| c4af52e8ee | |||
| 9ef9498c35 | |||
| 68763f4988 | |||
| 98d8a3477d | |||
| 07756ab2da | |||
| a2003101a0 | |||
| ce5bf5749f | |||
| 7519447966 | |||
| e1cfa68dcb | |||
| 614348d3a2 | |||
| 4320888d9d | |||
| d84cfeffcc | |||
| a762afc7c1 | |||
| 7c0f5ca7bc | |||
| 6047addfae | |||
| b66e5e9911 | |||
| e5cd71dd1e | |||
| 28e043f97a | |||
| af2dd8f9bb | |||
| 0f1be45990 | |||
| d91fa69e83 | |||
| a286082a1c | |||
| fde80896a1 | |||
| 579d3d9f86 | |||
| f11021b505 | |||
| 21ed3ea515 | |||
| e927ac49ac | |||
| e8494dcbfd | |||
| 99706bdf5a | |||
| 6b9a01c50d | |||
| 13e0b77c7f | |||
| dcaef14b5f | |||
| 6d6f7af3d7 | |||
| dfe8149b17 | |||
| fc220efb6c | |||
| 6d83815ab1 | |||
| 461b1735dd | |||
| 642738e0c0 | |||
| 99c77fff5e | |||
| 8a255d3b0d | |||
| 94d2a11096 | |||
| 403cda5bc8 | |||
| d6b9810b94 | |||
| 7c6e7742a8 | |||
| 220826f141 | |||
| 2007277bfc | |||
| f74295127b | |||
| 4ed4bcf660 | |||
| 4da2f9c9e7 | |||
| 782b10fbde | |||
| 4bb5248593 | |||
| 5127555740 | |||
| 276ac5c1d1 | |||
| d6e16c5373 | |||
| a93844bb83 | |||
| 6f5a6418a7 | |||
| f63161e40a | |||
| b500dab88a | |||
| eed5d05af5 | |||
| 4b6fe80fb2 | |||
| 05a5c3c3f9 | |||
| 3937d995b6 | |||
| 1b2af78e65 | |||
| 974e3fa904 | |||
| c0cd0ce07e | |||
| 4f7925580a | |||
| e87bff736e | |||
| 948ff287f1 | |||
| b452d7c794 | |||
| 1426c7066b | |||
| f84982f99a | |||
| 8d4f34461e | |||
| 516757ba38 | |||
| 25dfa32b7e | |||
| 2eeb507b78 | |||
| 1d04a3a5c7 | |||
| d3b56bd79b | |||
| 53d64348fd | |||
| d46ba7d1cd | |||
| c339a97443 | |||
| dfc30c2137 | |||
| e9418bfb0b | |||
| f7116d70e7 | |||
| 2d495e6124 | |||
| 5df2b3aa0a | |||
| a00449e34b | |||
| 6250f903ff | |||
| 5bb59f3136 | |||
| d76a67450c | |||
| a38df48d5a | |||
| e283b51dbc | |||
| 44491c6167 | |||
| afaa34ffc2 | |||
| 031fd1baff | |||
| e643966191 | |||
| ad7ef0be4f | |||
| b9e6665eab | |||
| 9bcd932bed | |||
| bfaa9ed5f9 | |||
| a2418cf260 | |||
| 69a6f5ee36 | |||
| e781df10b8 | |||
| f16861d5a9 | |||
| ad504ff081 | |||
| 9a18198988 | |||
| de64e3afc3 | |||
| 7253b07b0c | |||
| b4f344627d | |||
| c385257c92 | |||
| 859afaeece | |||
| e510d5a197 | |||
| 33967953b7 | |||
| 34c2cd3c06 | |||
| 5cd6f44e73 | |||
| b75f1e1758 | |||
| f12e732854 | |||
| c9a5fbf221 | |||
| c930b8f155 | |||
| 1e802bd0a3 | |||
| c46167073b | |||
| 5237f4a634 | |||
| b54be81408 | |||
| 39a5ecb6d2 | |||
| a6128f118a | |||
| 448a56afe9 | |||
| 0d243beb36 | |||
| 9e4092a050 | |||
| 44d4d9e563 | |||
| f249d55d3c | |||
| 85076dc697 | |||
| c074bd455f | |||
| c04e8f98ec | |||
| ef6dcd9291 | |||
| c51c8602a3 | |||
| 9659de6ebf | |||
| 396979954f | |||
| 836e350a15 | |||
| 0532048f37 | |||
| 78e18f0a01 | |||
| e1bf82b88e | |||
| ed0dffb409 | |||
| c66a2589d8 | |||
| 7140b2c4e8 | |||
| 21e87095dc | |||
| cfe030378a | |||
| 556a476f9f | |||
| 49efce7eca | |||
| c425b44ca9 | |||
| af16e64fd9 | |||
| 60b3daf686 | |||
| 80dccfeb54 | |||
| c89d268986 | |||
| ee7956d921 | |||
| 609ef5f3a7 | |||
| 0477910fc0 | |||
| 85a3a97d13 | |||
| 9eb035e6f5 | |||
| 2a407f007c | |||
| 5992cdfdaf | |||
| 0d7d843fc9 | |||
| fae405bfe0 | |||
| c59c74aeed | |||
| e36357d381 | |||
| 8d96f40934 | |||
| a1253b004d | |||
| ee79401a34 | |||
| 37990310eb | |||
| d806c6ab55 | |||
| ed013db011 | |||
| 5fcee9dfe5 | |||
| 159ce931b4 | |||
| 2d0d0ed786 | |||
| 635ad552d3 | |||
| d3eab88ab9 | |||
| c3a36b7cdb | |||
| f231db0aff | |||
| 0a610f88e0 | |||
| e2764c9764 | |||
| d6ef64aef4 | |||
| cf454c6306 | |||
| 18daa6aacd | |||
| 55717756e6 | |||
| 852df6e2ce | |||
| bc2bce8fae | |||
| e5efa2803a | |||
| d3ccd9ee61 | |||
| 593af0509f | |||
| 60073da049 | |||
| 681a3c3a5c | |||
| f63c0293bf | |||
| 7e8cda6c12 | |||
| 1bdb64d9e7 | |||
| e2ada7ce90 | |||
| 2751b7728f | |||
| 9ee708fbe6 | |||
| 851acca4b2 | |||
| 4cddd95337 | |||
| 5a7acda57d | |||
| 62f04fc9a6 | |||
| 4dbe7d92aa | |||
| c79db0380d | |||
| e6cae6e7b9 | |||
| 791be5006b | |||
| 240b642d6d | |||
| 574d9a3036 | |||
| 34e5dac549 | |||
| 1fc06da7a2 | |||
| 4000fef4c7 | |||
| 503029733c | |||
| 734c456fa5 | |||
| 5b81d7a0c1 | |||
| f9b76ce022 | |||
| fa3431f983 | |||
| 2d673acb1d | |||
| 05c05b5700 | |||
| 58b7c42fc5 | |||
| f78fb0a4f0 | |||
| 2b34d66ca4 | |||
| f21fac9edc | |||
| 15a93b1651 | |||
| b7ffd4e350 | |||
| 833d16404f | |||
| c55dbfc7fa | |||
| 4476cce08c | |||
| 3db6f42f04 | |||
| 4732dae384 | |||
| a9397e23ba | |||
| c5645a1084 | |||
| 556766bf2b | |||
| 33bea4bf6c | |||
| bf31ced530 | |||
| 642fd08d1f | |||
| 9d0bbf52a2 | |||
| 9056fd7fd1 | |||
| 7513906aaa | |||
| 7b15edb44c | |||
| e25bdd009b | |||
| 72f09e6a22 | |||
| 7b8a4501e6 | |||
| ba443232b3 | |||
| 71153abaf6 | |||
| d9ebd2d08e | |||
| 792c9b65b1 | |||
| 202a4c0a08 | |||
| 8cb8e22cdc | |||
| 38972d7ee4 | |||
| 959609db99 | |||
| e3d24856b9 | |||
| 6513cb6a88 | |||
| 77578b0ade | |||
| 32f51086ca | |||
| f426f2c0af | |||
| a6916e1f59 | |||
| 8f757810e7 | |||
| 2b786ef945 | |||
| dc7fb6eb98 | |||
| 3ff9af1421 | |||
| fc44b4e0d4 | |||
| 0ebb33da18 | |||
| eceba6932c | |||
| 1f6f7f570e | |||
| 23d65da0e2 | |||
| 824b30b3da | |||
| 90fee4ef7f | |||
| adeb3dea61 | |||
| 75f254d162 | |||
| 00aad53181 | |||
| bf7005acfc | |||
| e37c882fe2 | |||
| 624888adab | |||
| 144df86380 | |||
| 50c51530e7 | |||
| 7312b8cbe7 | |||
| 4e878f5848 | |||
| d99b5fb2f9 | |||
| 51e0097db1 | |||
| 1cac87fa50 | |||
| e688f781f5 | |||
| 2f2c40406f | |||
| a406f3d78a | |||
| 22d35cf89a | |||
| 8e2723951d | |||
| 4d4c111d5f | |||
| 0c294a58bf | |||
| 81864def9e | |||
| 6771c91224 | |||
| 596e13b24c | |||
| a07c2cb1f8 | |||
| ae6dfce7e3 | |||
| 1f371f3319 | |||
| a41e7fc298 | |||
| 739c5fb326 | |||
| 8b40b9944f | |||
| 19d587d17c | |||
| 45678c8536 | |||
| c21c9c24f8 | |||
| 32588c63eb | |||
| 611f0e7248 | |||
| 2d408eb985 | |||
| ac166691c4 | |||
| 7516177304 | |||
| f8d713a412 | |||
| 6d36ee3f53 | |||
| e6606e363a | |||
| dee8af6ec9 | |||
| 00af240915 | |||
| 730057ed4d | |||
| 675ba205ef | |||
| 9ab13a3e0c | |||
| 9b7ea1e83c | |||
| 2c84a07bdc | |||
| dd1ddfcd8e | |||
| cd21611646 | |||
| 360767a6eb | |||
| 2bf89f11c8 | |||
| 018123aa21 | |||
| 0bf149c894 | |||
| b0797db321 | |||
| 58e870d185 | |||
| 70ec98a9e0 | |||
| 22bb5cc276 | |||
| cecb623e76 | |||
| 04aa0ad04d | |||
| 58be8e95b0 | |||
| 5f8a792cf4 | |||
| 5dbbe174de | |||
| 9908e5cf05 | |||
| 3d11a98eef | |||
| 5ce8917c82 | |||
| 9c11e6bd09 | |||
| dd3df2258d | |||
| 826280f35c | |||
| 5536bd5d59 | |||
| 90bf32c342 | |||
| d917e738db | |||
| 42307972a8 | |||
| bd9aa18053 | |||
| 3074242aa0 | |||
| 684e687b6a | |||
| 1dff84ace3 | |||
| 99d8e96a35 | |||
| c3a3cbee3f | |||
| 748f826418 | |||
| 032a4743ac | |||
| 7c99294ba8 | |||
| 1665d8e0f6 | |||
| 6e9f24af66 | |||
| 359b55aeab | |||
| b1dda0a679 | |||
| a7286e5527 | |||
| c055a8e9e8 | |||
| 8c0a24c463 | |||
| 164c249a48 | |||
| adb3a08638 | |||
| cba28fbb72 | |||
| 4de35bc252 | |||
| 27407b8642 | |||
| 3490b1b388 | |||
| 46c5cd4ee2 | |||
| 73b387011d | |||
| 931b00d651 | |||
| c461a483b3 | |||
| 36a74a212c | |||
| 291a54d07e | |||
| 6a5bedc02e | |||
| e11d4e4e4c | |||
| fdb6af4ce3 | |||
| 1c28055afe | |||
| 72fa983e81 | |||
| bb29a493c7 | |||
| 1c647c5365 | |||
| 6b53ebdc5e | |||
| c2cb11c594 | |||
| 153d6fe337 | |||
| 11e187fc76 | |||
| fa38317438 | |||
| 7c2d640106 | |||
| 64b23393a0 | |||
| ae9fce8c45 | |||
| bd35a9924e | |||
| 2f4e82e0b9 | |||
| e9649a5217 | |||
| 98f4c8518e | |||
| 75b24c2352 | |||
| 16ce1f0823 | |||
| af1853de04 | |||
| b3711ad8cb | |||
| e450101a12 | |||
| 3f3b548d74 | |||
| 6284b3960d |
@@ -0,0 +1,8 @@
|
||||
c14f5eeca042232025797990396232e4feab53e7
|
||||
d6881e833da0b6278313870d37d42ca6ece8686a
|
||||
892be5444c5bd01a601c99243ac8d3e7b2f0e779
|
||||
d169a964dd7e9ab5c98b35a82bcbc76dc6229a7a
|
||||
4a6ec11f6f84a3d1fed2ee9b2f85d783ac3daa9d
|
||||
2c9d6a47f4d24a923069775206bf734feeb14830
|
||||
8f1743ed64a2a52c201a1ef5e312b4d3a7a922cf
|
||||
ad7f030344c405e10cfa7d5cf06d82c121eeb825
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [aothms]
|
||||
@@ -12,3 +12,14 @@ __pycache__
|
||||
.vscode
|
||||
# PyCharm files
|
||||
.idea
|
||||
|
||||
# Docs
|
||||
/docs/output
|
||||
/docs/rst_files
|
||||
/docs/doxygen
|
||||
/src/ifcblenderexport/docs/_build
|
||||
|
||||
# gettext binary translation files
|
||||
*.mo
|
||||
# Vim
|
||||
*.swp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
os: linux
|
||||
dist: xenial
|
||||
dist: bionic
|
||||
sudo: required
|
||||
|
||||
before_install:
|
||||
@@ -17,11 +17,11 @@ install:
|
||||
build-essential cmake python2.7 libpython2.7-dev swig zlib1g liblzma5 opencollada-dev wget apt-transport-https
|
||||
- sudo mkdir -p /usr/include/json/nlohmann/
|
||||
- sudo wget https://github.com/nlohmann/json/releases/download/v3.6.1/json.hpp -O /usr/include/json/nlohmann/json.hpp
|
||||
- sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
|
||||
- sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
|
||||
- sudo apt-get update -qq && sudo apt-get install -y dart
|
||||
- export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
|
||||
- git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
|
||||
# - sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
|
||||
# - sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
|
||||
# - sudo apt-get update -qq && sudo apt-get install -y dart
|
||||
# - export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
|
||||
# - git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
|
||||
|
||||
script:
|
||||
- pwd
|
||||
@@ -36,7 +36,7 @@ script:
|
||||
-DPYTHON_INCLUDE_DIR=/usr/include/python2.7 \
|
||||
-DPYTHON_EXECUTABLE=/usr/bin/python2.7 \
|
||||
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
|
||||
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.a;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.55;/usr/lib/x86_64-linux-gnu/libicudata.so.55" \
|
||||
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.so;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.60;/usr/lib/x86_64-linux-gnu/libicudata.so.60" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include/json \
|
||||
..
|
||||
@@ -48,9 +48,9 @@ script:
|
||||
- cd input
|
||||
|
||||
- /usr/local/bin/IfcConvert -yv acad2010_walls.ifc acad2010_walls.glb
|
||||
- gltf_validator acad2010_walls.glb
|
||||
# - gltf_validator acad2010_walls.glb
|
||||
|
||||
- python -c "from __future__ import print_function; from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); print(f[1][0], file=open('encoding.txt', 'w', encoding='utf-8'))" && grep "'a' 1m³ ≤ 5m³ ≥ 10m³" encoding.txt
|
||||
- /usr/bin/python2.7 -c "from __future__ import print_function; from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
|
||||
|
||||
- |
|
||||
(for i in *.ifc; do \
|
||||
|
||||
@@ -7,7 +7,7 @@ For more information, see
|
||||
* [http://ifcopenshell.org](http://ifcopenshell.org)
|
||||
* [http://academy.ifcopenshell.org](http://academy.ifcopenshell.org)
|
||||
|
||||
[](https://api.travis-ci.org/IfcOpenShell/IfcOpenShell)
|
||||
[](https://travis-ci.org/IfcOpenShell/IfcOpenShell)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
@@ -46,6 +46,8 @@ Instructions in a nutshell (**assuming Visual Studio 2015 x64 environment variab
|
||||
> build-deps.cmd
|
||||
> run-cmake.bat
|
||||
|
||||
NB: `build-deps.cmd` need to be ran from the directory containing it, i.e. the `./win` folder.
|
||||
|
||||
You can now open and build the solution file in Visual Studio:
|
||||
|
||||
> ..\build-vs2015-x64\IfcOpenShell.sln
|
||||
@@ -156,7 +158,7 @@ Usage examples
|
||||
|
||||
**Using the IfcOpenShell Python interface**
|
||||
|
||||
$ wget -O duplex.zip http://projects.buildingsmartalliance.org/files/?artifact_id=4278
|
||||
$ wget -O duplex.zip https://portal.nibs.org/files/wl/?id=4DsTgHFQAcOXzFetxbpRCECPbbfUqpgo
|
||||
$ unzip duplex.zip
|
||||
$ python
|
||||
>>> import ifcopenshell
|
||||
@@ -216,9 +218,9 @@ Usage examples
|
||||
>>> f.write("out.ifc")
|
||||
|
||||
[LGPL]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/COPYING "LGPL"
|
||||
[IFC]: http://www.buildingsmart-tech.org/specifications/ifc-overview "IFC"
|
||||
[IFC2x3 TC1]: http://www.buildingsmart-tech.org/specifications/ifc-releases/ifc2x3-tc1-release "IFC2x3 TC1"
|
||||
[IFC4 Add1]: http://www.buildingsmart-tech.org/specifications/ifc-releases/ifc4-add1-release "IFC4 Add1"
|
||||
[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"
|
||||
[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"
|
||||
|
||||
@@ -36,6 +36,7 @@ OPTION(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." O
|
||||
OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
|
||||
OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
|
||||
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
|
||||
OPTION(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
|
||||
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
|
||||
OPTION(BUILD_EXAMPLES "Build example applications." ON)
|
||||
OPTION(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON)
|
||||
@@ -49,6 +50,7 @@ endif()
|
||||
if (${BUILD_CONVERT})
|
||||
OPTION(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
|
||||
endif()
|
||||
OPTION(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
|
||||
|
||||
# TODO QtViewer is deprecated ATM as it uses the 0.4 API
|
||||
# OPTION(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer (requires Qt 4 framework)." OFF)
|
||||
@@ -142,6 +144,14 @@ IF(WIN32 AND ("$ENV{CONDA_BUILD}" STREQUAL ""))
|
||||
SET(Boost_USE_STATIC_LIBS ON)
|
||||
SET(Boost_USE_STATIC_RUNTIME ON)
|
||||
SET(Boost_USE_MULTITHREADED ON)
|
||||
# Disable Boost's autolinking as the libraries to be linked to are supplied
|
||||
# already by CMake, and wrong libraries would be asked for when code is
|
||||
# compiled with a toolset different from default.
|
||||
if(MSVC)
|
||||
ADD_DEFINITIONS(-DBOOST_ALL_NO_LIB)
|
||||
# Necessary for boost version >= 1.67
|
||||
SET(BCRYPT_LIBRARIES "bcrypt.lib")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
# Disable Boost's autolinking as the libraries to be linked to are supplied
|
||||
# already by CMake, and it's going to conflict if there are multiple, as is
|
||||
@@ -208,6 +218,10 @@ endfunction()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
|
||||
IF(MSVC)
|
||||
add_debug_variants(LIBXML2_LIBRARIES "${LIBXML2_LIBRARIES}" d)
|
||||
ENDIF()
|
||||
|
||||
# Find Open CASCADE
|
||||
IF("${OCC_INCLUDE_DIR}" STREQUAL "")
|
||||
SET(OCC_INCLUDE_DIR "/usr/include/oce/" CACHE FILEPATH "Open CASCADE header files")
|
||||
@@ -227,7 +241,7 @@ ENDIF()
|
||||
|
||||
SET(OPENCASCADE_LIBRARY_NAMES
|
||||
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
|
||||
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset
|
||||
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
|
||||
)
|
||||
|
||||
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
|
||||
@@ -459,7 +473,7 @@ function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2")
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1")
|
||||
|
||||
if(COMPILE_SCHEMA)
|
||||
# @todo, this appears to be untested at the moment
|
||||
@@ -543,7 +557,7 @@ file(GLOB IFCPARSE_CPP_FILES ../src/ifcparse/*.cpp)
|
||||
set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
|
||||
|
||||
add_library(IfcParse ${IFCPARSE_FILES})
|
||||
set_target_properties(IfcParse PROPERTIES COMPILE_FLAGS -DIFC_PARSE_EXPORTS)
|
||||
set_target_properties(IfcParse PROPERTIES COMPILE_FLAGS -DIFC_PARSE_EXPORTS VERSION "0.6.0" SOVERSION "0.6")
|
||||
|
||||
TARGET_LINK_LIBRARIES(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES})
|
||||
|
||||
@@ -566,7 +580,7 @@ file(GLOB SCHEMA_AGNOSTIC_CPP_FILES ../src/ifcgeom_schema_agnostic/*.cpp)
|
||||
set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES})
|
||||
|
||||
add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES})
|
||||
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
|
||||
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS VERSION "0.6.0" SOVERSION "0.6")
|
||||
|
||||
if (UNIX)
|
||||
find_package(Threads)
|
||||
@@ -593,7 +607,7 @@ foreach(s ${SCHEMA_VERSIONS})
|
||||
endforeach()
|
||||
|
||||
add_library(Serializers ${SERIALIZERS_FILES})
|
||||
set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}")
|
||||
set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS ${CONVERT_PRECISION}" VERSION "0.6.0" SOVERSION "0.6")
|
||||
|
||||
TARGET_LINK_LIBRARIES(Serializers ${SERIALIZER_SCHEMA_LIBRARIES})
|
||||
|
||||
@@ -642,6 +656,12 @@ INSTALL(TARGETS IfcGeomServer
|
||||
|
||||
endif()
|
||||
|
||||
# Documentation
|
||||
IF(BUILD_DOCUMENTATION)
|
||||
set(CMAKE_MODULE_PATH "../docs/cmake")
|
||||
ADD_SUBDIRECTORY(../docs docs)
|
||||
ENDIF()
|
||||
|
||||
IF(BUILD_IFCPYTHON)
|
||||
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
|
||||
ENDIF()
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#Look for an executable called sphinx-build
|
||||
find_program(SPHINX_EXECUTABLE
|
||||
NAMES sphinx-build
|
||||
DOC "Path to sphinx-build executable")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
#Handle standard arguments to find_package like REQUIRED and QUIET
|
||||
find_package_handle_standard_args(Sphinx
|
||||
"Failed to find sphinx-build executable"
|
||||
SPHINX_EXECUTABLE)
|
||||
|
||||
find_package(Doxygen REQUIRED)
|
||||
#find_package(Sphinx REQUIRED)
|
||||
|
||||
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
|
||||
|
||||
MESSAGE(STATUS "SPHINX BUILD ${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
file(MAKE_DIRECTORY ./output/doxygen)
|
||||
|
||||
if (DOXYGEN_FOUND)
|
||||
|
||||
add_custom_target(Sphinx ALL
|
||||
COMMAND
|
||||
${SPHINX_EXECUTABLE} -v -T -b html
|
||||
${SPHINX_SOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/output
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output
|
||||
COMMENT "Generating documentation with Sphinx")
|
||||
|
||||
# add_custom_target(ifcopenshell_python_docs ALL
|
||||
# COMMAND make html
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
|
||||
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
|
||||
# COMMENT "Generating documentation with Sphinx")
|
||||
|
||||
else (DOXYGEN_FOUND)
|
||||
message("Doxygen need to be installed to generate the doxygen documentation")
|
||||
endif (DOXYGEN_FOUND)
|
||||
@@ -0,0 +1,11 @@
|
||||
#Look for an executable called sphinx-build
|
||||
find_program(SPHINX_EXECUTABLE
|
||||
NAMES sphinx-build
|
||||
DOC "Path to sphinx-build executable")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
#Handle standard arguments to find_package like REQUIRED and QUIET
|
||||
find_package_handle_standard_args(Sphinx
|
||||
"Failed to find sphinx-build executable"
|
||||
SPHINX_EXECUTABLE)
|
||||
@@ -0,0 +1,11 @@
|
||||
#Look for an executable called sphinx-build
|
||||
find_program(SPHINX_EXECUTABLE
|
||||
NAMES sphinx-build
|
||||
DOC "Path to sphinx-build executable")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
#Handle standard arguments to find_package like REQUIRED and QUIET
|
||||
find_package_handle_standard_args(Sphinx
|
||||
"Failed to find sphinx-build executable"
|
||||
SPHINX_EXECUTABLE)
|
||||
@@ -0,0 +1,48 @@
|
||||
import textwrap
|
||||
# The `extensions` list should already be in here from `sphinx-quickstart`
|
||||
extensions = [
|
||||
# there may be others here already, e.g. 'sphinx.ext.mathjax'
|
||||
'breathe',
|
||||
'exhale',
|
||||
'sphinx.ext.intersphinx'
|
||||
]
|
||||
|
||||
project = 'IfcOpenShell'
|
||||
copyright = '2020, IfcOpenShell'
|
||||
author = ''
|
||||
|
||||
# Setup the breathe extension
|
||||
breathe_projects = {
|
||||
"My Project": "./output/doxygen/xml"
|
||||
}
|
||||
breathe_default_project = "My Project"
|
||||
|
||||
# Setup the exhale extension
|
||||
exhale_args = {
|
||||
# These arguments are required
|
||||
"containmentFolder": "./rst_files",
|
||||
"rootFileName": "library_root.rst",
|
||||
"rootFileTitle": "IfcOpenShell",
|
||||
"doxygenStripFromPath": "..",
|
||||
# Suggested optional arguments
|
||||
"createTreeView": True,
|
||||
# TIP: if using the sphinx-bootstrap-theme, you need
|
||||
# "treeViewIsBootstrap": True,
|
||||
"exhaleExecutesDoxygen": True,
|
||||
# "exhaleDoxygenStdin": "INPUT = ../src/serializers/ ../src/ifcgeom",
|
||||
"exhaleUseDoxyfile": True
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
# html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# # Tell sphinx what the primary language being documented is.
|
||||
# primary_domain = 'cpp'
|
||||
|
||||
# # Tell sphinx what the pygments highlight language should be.
|
||||
# highlight_language = 'cpp'
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# This program requires doxygen, sphinx, breathe and exhale.
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
|
||||
from sys import platform
|
||||
|
||||
|
||||
def copy_content(content, container):
|
||||
with open(content) as f:
|
||||
with open(container, "w") as f1:
|
||||
for line in f:
|
||||
f1.write(line)
|
||||
|
||||
|
||||
conf_path = "./conf.py"
|
||||
index_path = "./index.rst"
|
||||
|
||||
|
||||
if not os.path.isdir("./output/doxygen/xml"):
|
||||
os.makedirs("output/doxygen/xml")
|
||||
|
||||
if os.path.isfile(conf_path) and not os.path.isfile(index_path):
|
||||
os.remove(conf_path)
|
||||
if os.path.isfile(index_path) and not os.path.isfile(conf_path):
|
||||
os.remove(index_path)
|
||||
|
||||
if not (os.path.isfile(conf_path) and os.path.isfile(index_path)):
|
||||
subprocess.run(["sphinx-quickstart", "-q", "-p", "IfcOpenShell"])
|
||||
|
||||
conf_copy = ["./conf_to_copy.py", "./conf.py"]
|
||||
index_copy = ["./index_to_copy.rst", "./index.rst"]
|
||||
|
||||
copy_content(conf_copy[0], conf_copy[1])
|
||||
copy_content(index_copy[0], index_copy[1])
|
||||
|
||||
if platform == "linux" or platform == "linux2":
|
||||
subprocess.run(["make", "html"])
|
||||
if not os.path.isdir("/output/_build"):
|
||||
subprocess.run(["cd","../src/ifcblenderexport/docs"])
|
||||
subprocess.run(["make", "html"])
|
||||
subprocess.run(["cd", "../../../docs"])
|
||||
|
||||
subprocess.run(["mv" "../src/ifcblenderexport/docs/_build", "./output"])
|
||||
|
||||
|
||||
elif platform == "win32" or platform == "win64":
|
||||
subprocess.run(["make.bat", "html"])
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 120 KiB |
@@ -0,0 +1,240 @@
|
||||
APPLY_DEFAULT_MATERIALS
|
||||
-----------------------
|
||||
|
||||
Given the command invocation:
|
||||
|
||||
Duplex_A_20110907_optimized.ifc d.dae -yv --include attribute GlobalId 3bXiCStxP6Fgxdej$yc50U
|
||||
|
||||
You will find log messages along the lines of
|
||||
|
||||
[Warning] {3bXiCStxP6Fgxdej$yc50U} No material and surface styles for:
|
||||
#333=IfcCovering('3bXiCStxP6Fgxdej$yc50U',#1,'Compound Ceiling:Gypsum Board:187483',$,'Compound Ceiling:Gypsum Board',#17840,#17052,'187483',.CEILING.)
|
||||
|
||||
This means that there is no IfcStyledItem associated to the representation items and that the element does not have an IfcMaterial association with IfcMaterialRepresentation from which we can derive a style (colour) for the element.
|
||||
|
||||
The interactive session below shows how with this setting enabled you will get a default generated material from the IFC element entity type and material indices of 0 pointing to that. With this setting disabled the material index would be -1 to indicate a missing style. Note that there is one material index for every triangle in the list of `shp.geometry.faces`.
|
||||
|
||||
>>> import ifcopenshell, ifcopenshell.geom
|
||||
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
|
||||
>>> s = ifcopenshell.geom.settings()
|
||||
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
|
||||
>>>
|
||||
>>> shp = ifcopenshell.geom.create_shape(s, c)
|
||||
>>> shp.geometry.material_ids
|
||||
(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
|
||||
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
|
||||
[]
|
||||
>>>
|
||||
>>> s.set(s.APPLY_DEFAULT_MATERIALS, True)
|
||||
>>> shp = ifcopenshell.geom.create_shape(s, c)
|
||||
>>> shp.geometry.material_ids
|
||||
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
>>> [(m.name, m.diffuse) for m in shp.geometry.materials]
|
||||
[('IfcCovering', (0.7, 0.7, 0.7))]
|
||||
|
||||
This is enabled by default for the IfcConvert serializers as they will not gracefully handle -1 material indices and allows users to quickly assign colours based on entity types in their modelling applications.
|
||||
|
||||
APPLY_LAYERSETS
|
||||
---------------
|
||||
|
||||
This setting is available in IfcConvert as `--enable-layerset-slicing`.
|
||||
|
||||
For IfcWall and IfcSlab elements, takes the associated IfcMaterialLayerSet and builds a set of surfaces to segment the building element geometry.
|
||||
|
||||
Note that enabling this settings is computationally intensive as it involves 3D Boolean operations.
|
||||
|
||||
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
Duplex_A_20110907_optimized.ifc d2.dae --enable-layerset-slicing -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
BUILDING_LOCAL_PLACEMENT
|
||||
------------------------
|
||||
|
||||
This setting is available in IfcConvert using `--building-local-placement`.
|
||||
|
||||
In the typical IfcSite > IfcBuilding > IfcBuildingStorey > ... hierarchy of elements, don't incorporate the ObjectPlacement of the IfcBuilding and above in the placement of elements in the output. This is useful when there is a large offset in this placement that reduces precision in further processing.
|
||||
|
||||
CONVERT_BACK_UNITS
|
||||
------------------
|
||||
|
||||
This setting is available in IfcConvert using `--convert-back-units`.
|
||||
|
||||
Internally IfcOpenShell uses meters as the global length unit to do calculations. This setting restores the coordinate positions after conversion by multiplying the factor of the IfcUnit with UnitType=LENGTHUNIT into the output geometry coordinate values.
|
||||
|
||||
DISABLE_OPENING_SUBTRACTIONS
|
||||
----------------------------
|
||||
|
||||
This setting is available in IfcConvert using `--disable-opening-subtraction`.
|
||||
|
||||
As in most viewer applications, IfcOpeningElement geometry is subtracted from their host elements. This setting disables this behavior.
|
||||
|
||||
Duplex_A_20110907_optimized.ifc d1.dae -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
Duplex_A_20110907_optimized.ifc d3.dae --disable-opening-subtraction -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
Note that disabling this settings will reduce processing time and improve robustness as it involves 3D Boolean operations.
|
||||
|
||||
DISABLE_TRIANGULATION
|
||||
---------------------
|
||||
|
||||
To be used in conjunction with `USE_BREP_DATA`. Do not apply the triangulation and - when `USE_BREP_DATA` is set - return a OpenCASCADE serialized TopoDS_Shape from `create_shape()` and `iterator`.
|
||||
|
||||
>>> import ifcopenshell, ifcopenshell.geom
|
||||
>>> s = ifcopenshell.geom.settings()
|
||||
>>> s.set(s.DISABLE_TRIANGULATION, True)
|
||||
>>> s.set(s.USE_BREP_DATA, True)
|
||||
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
|
||||
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
|
||||
>>> shp = ifcopenshell.geom.create_shape(s, c)
|
||||
>>> print(shp.geometry.brep_data)
|
||||
|
||||
CASCADE Topology V1, (c) Matra-Datavision
|
||||
Locations 0
|
||||
Curve2ds 0
|
||||
Curves 12
|
||||
1 4.6750000000000034 -8.0749999999999904 2.657 -2.0455514041918775e-15 -1 0
|
||||
1 4.6750000000000034 -8.0749999999999904 2.657 1 -3.435893306383461e-15 0
|
||||
1 6.2260000000000044 -8.0749999999999957 2.657 -2.0455514041918724e-15 -1 0
|
||||
1 6.226 -10.246000000000031 2.657 -1 6.8717866127669219e-15 0
|
||||
...
|
||||
|
||||
EDGE_ARROWS
|
||||
-----------
|
||||
|
||||
When `INCLUDE_CURVES` is true and geometric elements include curves (such as the wall axis), add arrow heads to the edges to indicate direction of the curve.
|
||||
|
||||
Duplex_A_20110907_optimized.ifc d4.dae --model --plan --edge-arrows -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
EXCLUDE_SOLIDS_AND_SURFACES
|
||||
---------------------------
|
||||
|
||||
Exclude faces, shells and solids from geometrical output. Implied when using `--plan` without `--model` in IfcConvert.
|
||||
|
||||
FASTER_BOOLEANS
|
||||
---------------
|
||||
|
||||
NOTE: Only applicable when using OCCT 6.9 and earlier.
|
||||
|
||||
This setting is available in IfcConvert using `--merge-boolean-operands`.
|
||||
|
||||
Fuse the collection of all boolean operands into a single union before applying the boolean subtraction, as opposed to doing individual subtractions. This likely improves performance. From OCCT 7.0 onwards the boolean operations with multiple arguments is used.
|
||||
|
||||
GENERATE_UVS
|
||||
------------
|
||||
|
||||
This setting is available in IfcConvert using `--generate-uvs`.
|
||||
|
||||
Applies a box projection on the generated geometry for the element to obtain UV coordinates. This is purely generated, it does not involve texture coordinates stored in the IFC model.
|
||||
|
||||
Duplex_A_20110907_optimized.ifc d5.dae --generate-uvs -yv --include attribute GlobalId 2O2Fr$t4X7Zf8NOew3FNr2
|
||||
|
||||

|
||||
|
||||
INCLUDE_CURVES
|
||||
--------------
|
||||
|
||||
This setting is available in IfcConvert using `--plan`.
|
||||
|
||||
Include edge and wire geometries in the geometric output.
|
||||
|
||||
LAYERSET_FIRST
|
||||
--------------
|
||||
|
||||
This setting is available in IfcConvert using `--layerset-first`.
|
||||
|
||||
When not using APPLY_LAYERSETS, take the first material layer from the set to use as the material for the overall element.
|
||||
|
||||
NO_NORMALS
|
||||
----------
|
||||
|
||||
This setting is available in IfcConvert using `--no-normals`.
|
||||
|
||||
Do not emit normals on geometric output
|
||||
|
||||
SEARCH_FLOOR
|
||||
------------
|
||||
|
||||
Note: Only applicable to Collada .DAE output when used from IfcConvert.
|
||||
|
||||
This setting is available in IfcConvert using `--use-element-hierarchy`.
|
||||
|
||||
Include the spatial hierarchy in the elements.
|
||||
|
||||
SEW_SHELLS
|
||||
----------
|
||||
|
||||
This setting is available in IfcConvert using `--orient-shells`.
|
||||
|
||||
Re-orient or sew connected face sets to have a consistent outwards orientation.
|
||||
|
||||
SITE_LOCAL_PLACEMENT
|
||||
--------------------
|
||||
|
||||
This setting is available in IfcConvert using `--site-local-placement`.
|
||||
|
||||
See `BUILDING_LOCAL_PLACEMENT`, but exclude also the ObjectPlacement of the IfcSite.
|
||||
|
||||
USE_BREP_DATA
|
||||
-------------
|
||||
|
||||
See `DISABLE_TRIANGULATION`.
|
||||
|
||||
USE_PYTHON_OPENCASCADE
|
||||
----------------------
|
||||
|
||||
Note: Only available when an import of `OCC.Core.BRepTools` or `OCC.BRepTools` succeeds.
|
||||
|
||||
This implies `USE_WORLD_COORDS` `USE_BREP_DATA` and `DISABLE_TRIANGULATION`. The serialized TopoDS_Shape of `USE_BREP_DATA` is deserialized by Python OpenCASCADE.
|
||||
|
||||
USE_WORLD_COORDS
|
||||
----------------
|
||||
|
||||
Apply the ObjectPlacement of the building elements to the geometric output. This is implied when using the Wavefront .OBJ output in IfcConvert. Note that this also eliminates the possibility for geometric elements to point to the same interpreted geometry result.
|
||||
|
||||
VALIDATE_QUANTITIES
|
||||
-------------------
|
||||
|
||||
This setting is available in IfcConvert using `--validate`.
|
||||
|
||||
Running IfcConvert with `--validate` will set a non-zero exit code when ever a log message with severity equal or greater than ERROR has been emitted.
|
||||
|
||||
Currently for internal use only. For every building element geometry converted, looks for an associated quantity set where the OwnerHistory's organization name is IfcOpenShell. And looks for the quantities "Total Surface Area", "Volume", "Shape Validation Properties.Surface Genus" and validates these according to the interpreted geometry definition. Emit Logger::Error when calculated values are outside of the tolerance range for the value stored in the model.
|
||||
|
||||
WELD_VERTICES
|
||||
-------------
|
||||
|
||||
Note: In Python, this setting is *on* by default.
|
||||
|
||||
Note: This setting only affects triangulated output.
|
||||
|
||||
This setting is available in IfcConvert using `--weld-vertices`.
|
||||
|
||||
Discards normals and joins vertices solely based on position. This is useful when output is to be modified in a modeling application.
|
||||
|
||||
>>> import ifcopenshell, ifcopenshell.geom
|
||||
>>> s = ifcopenshell.geom.settings()
|
||||
>>> s.set(s.WELD_VERTICES, False)
|
||||
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
|
||||
>>> c = f["3bXiCStxP6Fgxdej$yc50U"]
|
||||
>>> shp = ifcopenshell.geom.create_shape(s, c)
|
||||
>>> shp.geometry.verts
|
||||
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.657, 4.674999999999999, -10.24600000000002, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226, -10.24600000000003, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226, -10.24600000000003, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6, 4.675000000000003, -8.07499999999999, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 6.226000000000004, -8.074999999999996, 2.657, 6.226000000000004, -8.074999999999996, 2.6)
|
||||
>>> shp.geometry.normals
|
||||
(3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 3.059754518198021e-17, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, 2.110175529791737e-16, 0.0, -1.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, 6.8717866127669046e-15, 1.0, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, -1.0, 1.79434333701042e-15, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0, 3.4358933063834523e-15, 1.0, 0.0)
|
||||
>>>
|
||||
>>> s.set(s.WELD_VERTICES, True)
|
||||
>>> shp = ifcopenshell.geom.create_shape(s, c)
|
||||
>>> shp.geometry.verts
|
||||
(4.675000000000003, -8.07499999999999, 2.657, 4.674999999999999, -10.24600000000002, 2.657, 6.226000000000004, -8.074999999999996, 2.657, 6.226, -10.24600000000003, 2.657, 4.675000000000003, -8.07499999999999, 2.6, 4.674999999999999, -10.24600000000002, 2.6, 6.226000000000004, -8.074999999999996, 2.6, 6.226, -10.24600000000003, 2.6)
|
||||
>>> shp.geometry.normals
|
||||
()
|
||||
@@ -0,0 +1,10 @@
|
||||
IfcOpenShell
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
rst_files/library_root
|
||||
|
||||
|
||||
`ifcopenshell-python documentation <_build/html/index.html>`_
|
||||
@@ -0,0 +1,10 @@
|
||||
IfcOpenShell
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
rst_files/library_root
|
||||
|
||||
|
||||
`ifcopenshell-python documentation <_build/html/index.html>`_
|
||||
@@ -23,11 +23,14 @@
|
||||
# This script builds IfcOpenShell and its dependencies #
|
||||
# #
|
||||
# Prerequisites for this script to function correctly: #
|
||||
# * git * bzip2 * tar * c(++) compilers * yacc * autoconf #
|
||||
# * cmake * git * bzip2 * tar * c(++) compilers * yacc * autoconf #
|
||||
# #
|
||||
# if building with USE_OCCT additionally: #
|
||||
# * freetype * glx.h #
|
||||
# #
|
||||
# if building with OCCT 7.4.0 additionally: #
|
||||
# * libfontconfig1-dev #
|
||||
# #
|
||||
# if building with -shared #
|
||||
# * libgl1-mesa-dev libxext-dev libxmu-dev libxmu-headers libxi-dev #
|
||||
# #
|
||||
@@ -35,15 +38,15 @@
|
||||
# * libffi(-dev[el]) #
|
||||
# #
|
||||
# on debian 7.8 these can be obtained with: #
|
||||
# $ apt-get install git gcc g++ autoconf bison bzip2 #
|
||||
# libfreetype6-dev mesa-common-dev libffi-dev #
|
||||
# $ apt-get install git gcc g++ autoconf bison bzip2 cmake #
|
||||
# libfreetype6-dev mesa-common-dev libffi-dev libfontconfig1-dev #
|
||||
# #
|
||||
# on ubuntu 14.04: #
|
||||
# $ apt-get install git gcc g++ autoconf bison make #
|
||||
# libfreetype6-dev mesa-common-dev libffi-dev #
|
||||
# $ apt-get install git gcc g++ autoconf bison make cmake #
|
||||
# libfreetype6-dev mesa-common-dev libffi-dev libfontconfig1-dev #
|
||||
# #
|
||||
# on OS X El Capitan with homebrew: #
|
||||
# $ brew install git bison autoconf automake freetype libffi #
|
||||
# $ brew install git bison autoconf automake freetype libffi cmake #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
@@ -76,7 +79,7 @@ ch.setLevel(logging.INFO)
|
||||
logger.addHandler(ch)
|
||||
|
||||
PROJECT_NAME="IfcOpenShell"
|
||||
PYTHON_VERSIONS=["2.7.16", "3.2.6", "3.3.6", "3.4.6", "3.5.3", "3.6.2", "3.7.3"]
|
||||
PYTHON_VERSIONS=["2.7.16", "3.2.6", "3.3.6", "3.4.6", "3.5.3", "3.6.2", "3.7.3", "3.8.6", "3.9.1"]
|
||||
JSON_VERSION="v3.6.1"
|
||||
OCE_VERSION="0.18"
|
||||
# OCCT_VERSION="7.1.0"
|
||||
@@ -84,13 +87,11 @@ OCE_VERSION="0.18"
|
||||
# OCCT_VERSION="7.2.0"
|
||||
# OCCT_HASH="88af392"
|
||||
OCCT_VERSION="7.3.0p3"
|
||||
BOOST_VERSION="1.59.0"
|
||||
BOOST_VERSION="1.71.0"
|
||||
#PCRE_VERSION="8.39"
|
||||
PCRE_VERSION="8.41"
|
||||
#LIBXML2_VERSION="2.9.3"
|
||||
LIBXML2_VERSION="2.9.9"
|
||||
CMAKE_VERSION="3.4.1"
|
||||
#CMAKE_VERSION="3.14.5"
|
||||
SWIG_VERSION="3.0.12"
|
||||
#SWIG_VERSION="4.0.0"
|
||||
#OPENCOLLADA_VERSION="v1.6.63"
|
||||
@@ -156,6 +157,13 @@ TOOLSET = None
|
||||
if get_os() == "Darwin":
|
||||
# C++11 features used in OCCT 7+ need a more recent stdlib
|
||||
TOOLSET = "10.9" if USE_OCCT else "10.6"
|
||||
|
||||
# python 3.4 doesn't seem to build anymore on recent versions of clang
|
||||
if get_os() == "Darwin":
|
||||
try:
|
||||
PYTHON_VERSIONS.remove("3.4.6")
|
||||
except ValueError as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
IFCOS_NUM_BUILD_PROCS = os.environ["IFCOS_NUM_BUILD_PROCS"]
|
||||
@@ -225,7 +233,6 @@ dependency_tree = {
|
||||
'boost': (),
|
||||
'libxml2': (),
|
||||
'python': (),
|
||||
'swig': (),
|
||||
'occ': (),
|
||||
'pcre': (),
|
||||
'json': ()
|
||||
@@ -257,7 +264,7 @@ print("Building:", *sorted(targets, key=lambda t: len(list(v(t)))))
|
||||
|
||||
# Check that required tools are in PATH
|
||||
|
||||
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch"]:
|
||||
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch", "cmake"]:
|
||||
if which(cmd) is None:
|
||||
raise ValueError("Required tool '%s' not installed or not added to PATH" % (cmd,))
|
||||
|
||||
@@ -301,10 +308,9 @@ def run(cmds, cwd=None):
|
||||
return stdout.strip()
|
||||
|
||||
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
|
||||
CMAKE_VERSION_2=CMAKE_VERSION[:CMAKE_VERSION.rindex('.')]
|
||||
|
||||
OCE_LOCATION="https://github.com/tpaviot/oce/archive/OCE-%s.tar.gz" % (OCE_VERSION,)
|
||||
BOOST_LOCATION="http://downloads.sourceforge.net/project/boost/boost/%s/boost_%s.tar.bz2" % (BOOST_VERSION, BOOST_VERSION_UNDERSCORE)
|
||||
BOOST_LOCATION="https://dl.bintray.com/boostorg/release/%s/source/" % (BOOST_VERSION,)
|
||||
|
||||
# Helper functions
|
||||
|
||||
@@ -320,8 +326,7 @@ def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
|
||||
P=".."
|
||||
else:
|
||||
P=cmake_dir
|
||||
cmake_path= os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake")
|
||||
run([cmake_path, P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
|
||||
run(["cmake", P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
|
||||
|
||||
def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
|
||||
"""Lazily clones the `git` repository denoted by `clone_url` into
|
||||
@@ -422,7 +427,7 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d
|
||||
else:
|
||||
raise ValueError()
|
||||
logger.info("\rBuilding %s... " % (name,))
|
||||
run([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,)], cwd=extract_build_dir)
|
||||
run([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,), "VERBOSE=1"], cwd=extract_build_dir)
|
||||
logger.info( "\rInstalling %s... " % (name,))
|
||||
run([make, "install"], cwd=extract_build_dir)
|
||||
logger.info( "\rInstalled %s \n" % (name,))
|
||||
@@ -443,7 +448,10 @@ cecho("Collecting dependencies:", GREEN)
|
||||
ADDITIONAL_ARGS=[]
|
||||
BOOST_ADDRESS_MODEL=[]
|
||||
if TARGET_ARCH == "i686" and run([uname, "-m"]).strip() == "x86_64":
|
||||
ADDITIONAL_ARGS=["-m32", "-arch i386"]
|
||||
if get_os() == "Darwin":
|
||||
ADDITIONAL_ARGS=["-m32", "-arch i386"]
|
||||
else:
|
||||
ADDITIONAL_ARGS=["-m32"]
|
||||
BOOST_ADDRESS_MODEL=["architecture=x86", "address-model=32"]
|
||||
|
||||
if get_os() == "Darwin":
|
||||
@@ -482,25 +490,8 @@ os.environ["CFLAGS"] = CFLAGS
|
||||
os.environ["LDFLAGS"] = LDFLAGS
|
||||
|
||||
# Some dependencies need a more recent CMake version than most distros provide
|
||||
build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
|
||||
|
||||
# Extract compiler flags from CMake to harmonize settings with other autoconf dependencies
|
||||
CMAKE_FLAG_EXTRACT_DIR="ifcopenshell_cmake_test_%s" % (time.time(),)
|
||||
# was sp.check_output([bash, "-c", "cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 32"]), in bash script, unclear what the exact required format is and whether it's needed
|
||||
if os.path.exists(CMAKE_FLAG_EXTRACT_DIR):
|
||||
shutil.rmtree(CMAKE_FLAG_EXTRACT_DIR)
|
||||
os.makedirs(CMAKE_FLAG_EXTRACT_DIR)
|
||||
BUILD_CFG_UPPER=BUILD_CFG.upper()
|
||||
for FL in ["C", "CXX"]:
|
||||
run([bash, "-c", """echo "
|
||||
message(\"\${CMAKE_%s_FLAGS_%s}\")
|
||||
" > CMakeLists.txt""" % (FL, BUILD_CFG_UPPER)], cwd=CMAKE_FLAG_EXTRACT_DIR)
|
||||
FL="%sFLAGS" % (FL,)
|
||||
FLM="%sFLAGS_MINIMAL" % (FL,)
|
||||
# @TODO: bash code unclear
|
||||
# exec("%sFLAGS=%s" % (FL, sp.check_output([os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake"), "."
|
||||
# declare ${FL}FLAGS_MINIMAL="`$DEPS_DIR/install/cmake-$CMAKE_VERSION/bin/cmake . 2>&1 >/dev/null` ${!FLM}"
|
||||
shutil.rmtree(CMAKE_FLAG_EXTRACT_DIR)
|
||||
# @tfk: this is no longer needed
|
||||
# build_dependency(name="cmake-%s" % (CMAKE_VERSION,), mode="autoconf", build_tool_args=[], download_url="https://cmake.org/files/v%s" % (CMAKE_VERSION_2,), download_name="cmake-%s.tar.gz" % (CMAKE_VERSION,))
|
||||
|
||||
if "json" in targets:
|
||||
json_url = "https://github.com/nlohmann/json/releases/download/{JSON_VERSION}/json.hpp".format(**locals())
|
||||
@@ -525,7 +516,7 @@ if "swig" in targets:
|
||||
build_dependency(
|
||||
name="swig",
|
||||
mode="autoconf",
|
||||
build_tool_args=["--with-pcre-prefix={DEPS_DIR}/install/pcre-{PCRE_VERSION}".format(**locals())],
|
||||
build_tool_args=["--disable-ccache", "--with-pcre-prefix={DEPS_DIR}/install/pcre-{PCRE_VERSION}".format(**locals())],
|
||||
download_url="https://github.com/swig/swig.git",
|
||||
download_name="swig",
|
||||
download_tool=download_tool_git,
|
||||
@@ -540,11 +531,12 @@ if USE_OCCT and "occ" in targets:
|
||||
"-DINSTALL_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}".format(**locals()),
|
||||
"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}".format(**locals()),
|
||||
"-DBUILD_MODULE_Draw=0",
|
||||
"-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off"
|
||||
],
|
||||
download_url = "https://git.dev.opencascade.org/repos/occt.git",
|
||||
download_name = "occt",
|
||||
download_tool=download_tool_git,
|
||||
patch="./patches/occt/enable-exception-handling.patch",
|
||||
patch=None if OCCT_VERSION >= "7.4" else "./patches/occt/enable-exception-handling.patch",
|
||||
revision="V" + OCCT_VERSION.replace('.', '_')
|
||||
)
|
||||
elif "occ" in targets:
|
||||
@@ -595,6 +587,7 @@ if "OpenCOLLADA" in targets:
|
||||
download_url="https://github.com/KhronosGroup/OpenCOLLADA.git",
|
||||
download_name="OpenCOLLADA",
|
||||
download_tool=download_tool_git,
|
||||
patch="./patches/opencollada/pr622_and_disable_subdirs.patch",
|
||||
revision=OPENCOLLADA_VERSION
|
||||
)
|
||||
|
||||
@@ -622,13 +615,19 @@ if "python" in targets:
|
||||
yield v, unicode_conf, abi_tag
|
||||
|
||||
for PYTHON_VERSION, unicode_conf, abi_tag in PYTHON_VERSION_CONFS():
|
||||
build_dependency(
|
||||
"python-{PYTHON_VERSION}{abi_tag}".format(**locals()),
|
||||
"autoconf",
|
||||
PYTHON_CONFIGURE_ARGS + [unicode_conf],
|
||||
"http://www.python.org/ftp/python/{PYTHON_VERSION}/".format(**locals()),
|
||||
"Python-{PYTHON_VERSION}.tgz".format(**locals())
|
||||
)
|
||||
try:
|
||||
build_dependency(
|
||||
"python-{PYTHON_VERSION}{abi_tag}".format(**locals()),
|
||||
"autoconf",
|
||||
PYTHON_CONFIGURE_ARGS + [unicode_conf],
|
||||
"http://www.python.org/ftp/python/{PYTHON_VERSION}/".format(**locals()),
|
||||
"Python-{PYTHON_VERSION}.tgz".format(**locals())
|
||||
)
|
||||
except Exception as e:
|
||||
pyver2 = PYTHON_VERSION[:PYTHON_VERSION.rfind('.')]
|
||||
if os.path.exists("{DEPS_DIR}/install/python-{PYTHON_VERSION}{abi_tag}/bin/python{pyver2}".format(**locals())):
|
||||
print("Installation partially failed")
|
||||
else: raise e
|
||||
|
||||
os.environ["CXXFLAGS"]=OLD_CXX_FLAGS
|
||||
os.environ["CFLAGS"]=OLD_C_FLAGS
|
||||
@@ -652,7 +651,7 @@ if "boost" in targets:
|
||||
list(map(str_concat("cxxflags"), CXXFLAGS.strip().split(' '))) + \
|
||||
list(map(str_concat("linkflags"), LDFLAGS.strip().split(' '))) + \
|
||||
["stage", "-s", "NO_BZIP2=1"],
|
||||
download_url="http://downloads.sourceforge.net/project/boost/boost/{BOOST_VERSION}/".format(**locals()),
|
||||
download_url=BOOST_LOCATION,
|
||||
download_name="boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2".format(**locals())
|
||||
)
|
||||
|
||||
@@ -682,7 +681,8 @@ cmake_args=[
|
||||
"-DCMAKE_INSTALL_PREFIX=" "{DEPS_DIR}/install/ifcopenshell".format(**locals()),
|
||||
"-DBOOST_ROOT=" "{DEPS_DIR}/install/boost-{BOOST_VERSION}".format(**locals()),
|
||||
"-DGLTF_SUPPORT=" "ON",
|
||||
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals())
|
||||
"-DJSON_INCLUDE_DIR=" "{DEPS_DIR}/install/json".format(**locals()),
|
||||
"-DBoost_NO_BOOST_CMAKE=" "On"
|
||||
]
|
||||
|
||||
if "occ" in targets and USE_OCCT:
|
||||
@@ -738,10 +738,15 @@ if "IfcOpenShell-Python" in targets:
|
||||
for PYTHON_VERSION, _, TAG in PYTHON_VERSION_CONFS():
|
||||
logger.info("\rConfiguring python {PYTHON_VERSION}{TAG} wrapper...".format(**locals()))
|
||||
|
||||
python_dir = os.path.join(IFCOS_DIR, "python-{PYTHON_VERSION}{TAG}".format(**locals()))
|
||||
# python_dir = os.path.join(IFCOS_DIR, "python-{PYTHON_VERSION}{TAG}".format(**locals()))
|
||||
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
|
||||
if not os.path.exists(python_dir):
|
||||
os.makedirs(python_dir)
|
||||
|
||||
cache_path = os.path.join(python_dir, "CMakeCache.txt")
|
||||
if os.path.exists(cache_path):
|
||||
os.unlink(cache_path)
|
||||
|
||||
PYTHON_LIBRARY=run([bash, "-c", "ls {DEPS_DIR}/install/python-{PYTHON_VERSION}{TAG}/lib/libpython*.*".format(**locals())])
|
||||
PYTHON_INCLUDE=run([bash, "-c", "ls -d {DEPS_DIR}/install/python-{PYTHON_VERSION}{TAG}/include/python*".format(**locals())])
|
||||
PYTHON_EXECUTABLE=os.path.join(DEPS_DIR, "install", "python-{PYTHON_VERSION}{TAG}".format(**locals()), "bin", "python{PYTHON_VERSION[0]}".format(**locals()))
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 95abbe21..293c951c 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -284,10 +284,10 @@ add_subdirectory(COLLADASaxFrameworkLoader)
|
||||
add_subdirectory(COLLADAStreamWriter)
|
||||
|
||||
# building COLLADAValidator app
|
||||
-add_subdirectory(COLLADAValidator)
|
||||
+# add_subdirectory(COLLADAValidator)
|
||||
|
||||
# DAE validator app
|
||||
-add_subdirectory(DAEValidator)
|
||||
+# add_subdirectory(DAEValidator)
|
||||
|
||||
# Library export
|
||||
install(EXPORT LibraryExport DESTINATION ${OPENCOLLADA_INST_CMAKECONFIG} FILE OpenCOLLADATargets.cmake)
|
||||
diff --git a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
|
||||
index 1f9a3eef..dd6f5c59 100644
|
||||
--- a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
|
||||
+++ b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "GeneratedSaxParserUtils.h"
|
||||
#include <math.h>
|
||||
+#include <cmath>
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
#include <limits>
|
||||
@@ -0,0 +1,22 @@
|
||||
From a0deb4ce8b43cf3c8b8c0a4225c6be5296446dbd Mon Sep 17 00:00:00 2001
|
||||
From: Adam Eri <adam.eri@blackmirror.media>
|
||||
Date: Tue, 3 Sep 2019 23:30:20 +0200
|
||||
Subject: [PATCH] Resolves compile error on macOS
|
||||
|
||||
Resolves "no member named 'isnan' in namespace 'std'" on macOS
|
||||
---
|
||||
GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
|
||||
index 1f9a3eef..dd6f5c59 100644
|
||||
--- a/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
|
||||
+++ b/GeneratedSaxParser/src/GeneratedSaxParserUtils.cpp
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "GeneratedSaxParserUtils.h"
|
||||
#include <math.h>
|
||||
+#include <cmath>
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
#include <limits>
|
||||
@@ -0,0 +1,2 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
@@ -0,0 +1,64 @@
|
||||
# ****************************************************************************
|
||||
# line endings
|
||||
|
||||
# to suppress line ending changes in github, add ?w=1 at link end of a diff
|
||||
|
||||
# for more information see
|
||||
# FreeCAD source code src/Mod/.gitattributes
|
||||
# FreeCAD forum topic https://forum.freecadweb.org/viewtopic.php?f=17&t=41117
|
||||
# FreeCAD pull request https://github.com/FreeCAD/FreeCAD/pull/2752
|
||||
|
||||
|
||||
# get all used file types
|
||||
|
||||
# in a directory in a bash use
|
||||
# find . -type f -name '*.*' | sed 's|.*\.||' | sort -u
|
||||
|
||||
# search for a specific file ending
|
||||
# find . -type f -name '*.ico'
|
||||
|
||||
|
||||
# normalize the line endings of the following files
|
||||
*.cpp text
|
||||
*.css text
|
||||
*.csv text
|
||||
*.feature text
|
||||
*.gitattributes text
|
||||
*.gitignore text
|
||||
*.gitkeep
|
||||
*.h text
|
||||
*.html text
|
||||
*.ifc text
|
||||
*.json text
|
||||
*.md text
|
||||
*.po text
|
||||
*.pot text
|
||||
*.py text
|
||||
*.txt text
|
||||
|
||||
|
||||
# files not normalized ATM
|
||||
# bat
|
||||
# bnf
|
||||
# blend
|
||||
# i
|
||||
# ico
|
||||
# mo
|
||||
# mpass
|
||||
# png
|
||||
# pth
|
||||
# pyc
|
||||
# rst
|
||||
# svg
|
||||
# ttf
|
||||
# xsd
|
||||
|
||||
|
||||
# line endings of all directories will be normalized
|
||||
# to exclude a directory from being normalized add it here
|
||||
# example: to exclude dirctory ifcbimtester use the following line without #
|
||||
# ifcbimtester/** -text
|
||||
|
||||
|
||||
# use git to manually correct the file endings
|
||||
# git add --renormalize .
|
||||
@@ -0,0 +1,59 @@
|
||||
# bcf
|
||||
|
||||
A simple Python implementation of BCF. The data model is described in `data.py`.
|
||||
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
|
||||
is available via `bcfapi.py`.
|
||||
|
||||
Currently supports BCF version 2.1.
|
||||
|
||||
## bcfxml
|
||||
|
||||
The `bcfxml` module lets you interact with the BCF-XML standard.
|
||||
|
||||
```
|
||||
from bcf.bcfxml import BcfXml
|
||||
|
||||
bcfxml = BcfXml()
|
||||
|
||||
# Load a project
|
||||
project = bcfxml.get_project("/path/to/file.bcf")
|
||||
|
||||
# The project is also stored in the module
|
||||
# project == bcfxml.project
|
||||
|
||||
print(project.name)
|
||||
|
||||
# To edit a project, just modify the object directly
|
||||
bcfxml.project.name = "New name"
|
||||
bcfxml.edit_project()
|
||||
|
||||
# The BCF file is extracted to this temporary directory
|
||||
print(bcfxml.filepath)
|
||||
|
||||
# Get a dictionary of topics
|
||||
topics = bcfxml.get_topics()
|
||||
|
||||
# Note: topics == bcfxml.topics
|
||||
for guid, topic in bcfxml.topics.items():
|
||||
print("Topic guid is", guid)
|
||||
print("Topic guid is", topic.guid)
|
||||
print("Topic title is", topic.title)
|
||||
|
||||
# Fetch extra data about a topic
|
||||
header = bcfxml.get_header(guid)
|
||||
comments = bcfxml.get_comments(guid)
|
||||
viewpoints = bcfxml.get_viewpoints(guid)
|
||||
|
||||
# Note: comments == topic.comments, and so on
|
||||
for comment_guid, comment in comments.items():
|
||||
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.edit_topic(topic)
|
||||
```
|
||||
@@ -0,0 +1,762 @@
|
||||
import os
|
||||
import uuid
|
||||
import shutil
|
||||
import zipfile
|
||||
import logging
|
||||
import tempfile
|
||||
import bcf.data
|
||||
from datetime import datetime
|
||||
from xml.dom import minidom
|
||||
from xmlschema import XMLSchema
|
||||
from contextlib import contextmanager
|
||||
from shutil import copyfile
|
||||
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
@contextmanager
|
||||
def cd(newdir):
|
||||
prevdir = os.getcwd()
|
||||
os.chdir(os.path.expanduser(newdir))
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(prevdir)
|
||||
|
||||
|
||||
class BcfXml:
|
||||
def __init__(self):
|
||||
self.filepath = None
|
||||
self.logger = logging.getLogger("bcfxml")
|
||||
self.author = "john@doe.com"
|
||||
self.project = bcf.data.Project()
|
||||
self.version = "2.1"
|
||||
self.topics = {}
|
||||
|
||||
def new_project(self):
|
||||
self.project.project_id = str(uuid.uuid4())
|
||||
self.project.name = "New Project"
|
||||
self.topics = {}
|
||||
if self.filepath:
|
||||
self.close_project()
|
||||
self.filepath = tempfile.mkdtemp()
|
||||
self.edit_project()
|
||||
self.edit_version()
|
||||
|
||||
def get_project(self, filepath=None):
|
||||
if not filepath:
|
||||
return self.project
|
||||
zip_file = zipfile.ZipFile(filepath)
|
||||
self.filepath = tempfile.mkdtemp()
|
||||
zip_file.extractall(self.filepath)
|
||||
data = self._read_xml("project.bcfp", "project.xsd")
|
||||
self.project.project_id = data["Project"]["@ProjectId"]
|
||||
self.project.name = data["Project"]["Name"]
|
||||
return self.project
|
||||
|
||||
def edit_project(self):
|
||||
self.document = minidom.Document()
|
||||
root = self._create_element(self.document, "ProjectExtension")
|
||||
project = self._create_element(root, "Project", {"ProjectId": self.project.project_id})
|
||||
self._create_element(project, "Name", text=self.project.name)
|
||||
self._create_element(root, "ExtensionSchema", text="extensions.xsd")
|
||||
with open(os.path.join(self.filepath, "project.bcfp"), "wb") as f:
|
||||
f.write(self.document.toprettyxml(encoding="utf-8"))
|
||||
|
||||
def save_project(self, filepath):
|
||||
with cd(self.filepath):
|
||||
zip_file = zipfile.ZipFile(filepath, "w", zipfile.ZIP_DEFLATED)
|
||||
for root, dirs, files in os.walk("./"):
|
||||
for file in files:
|
||||
zip_file.write(os.path.join(root, file))
|
||||
zip_file.close()
|
||||
|
||||
def get_version(self):
|
||||
data = self._read_xml("bcf.version", "version.xsd")
|
||||
self.version = data["@VersionId"]
|
||||
return self.version
|
||||
|
||||
def edit_version(self):
|
||||
self.document = minidom.Document()
|
||||
root = self._create_element(self.document, "Version", {"VersionId": self.version})
|
||||
version = self._create_element(root, "DetailedVersion", text=self.version)
|
||||
with open(os.path.join(self.filepath, "bcf.version"), "wb") as f:
|
||||
f.write(self.document.toprettyxml(encoding="utf-8"))
|
||||
|
||||
def get_topics(self):
|
||||
self.topics = {}
|
||||
topics = []
|
||||
subdirs = []
|
||||
for (dirpath, dirnames, filenames) in os.walk(self.filepath):
|
||||
subdirs = dirnames
|
||||
break
|
||||
for subdir in subdirs:
|
||||
self.topics[subdir] = self.get_topic(subdir)
|
||||
return self.topics
|
||||
|
||||
def get_header(self, guid):
|
||||
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||
if "Header" not in data:
|
||||
return
|
||||
header = bcf.data.Header()
|
||||
for item in data["Header"]["File"]:
|
||||
header_file = bcf.data.HeaderFile()
|
||||
optional_keys = {
|
||||
"filename": "Filename",
|
||||
"date": "Date",
|
||||
"reference": "Reference",
|
||||
"ifc_project": "@IfcProject",
|
||||
"ifc_spatial_structure_element": "@IfcSpatialStructureElement",
|
||||
"is_external": "@isExternal",
|
||||
}
|
||||
for key, value in optional_keys.items():
|
||||
if value in item:
|
||||
setattr(header_file, key, item[value])
|
||||
header.files.append(header_file)
|
||||
self.topics[guid].header = header
|
||||
return header
|
||||
|
||||
def get_topic(self, guid):
|
||||
if guid in self.topics:
|
||||
return self.topics[guid]
|
||||
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||
topic = bcf.data.Topic()
|
||||
self.topics[guid] = topic
|
||||
|
||||
mandatory_keys = {
|
||||
"guid": "@Guid",
|
||||
"title": "Title",
|
||||
"creation_date": "CreationDate",
|
||||
"creation_author": "CreationAuthor",
|
||||
}
|
||||
for key, value in mandatory_keys.items():
|
||||
setattr(topic, key, data["Topic"][value])
|
||||
|
||||
optional_keys = {
|
||||
"priority": "Priority",
|
||||
"index": "Index",
|
||||
"labels": "Labels",
|
||||
"reference_links": "ReferenceLink",
|
||||
"modified_date": "ModifiedDate",
|
||||
"modified_author": "ModifiedAuthor",
|
||||
"due_date": "DueDate",
|
||||
"assigned_to": "AssignedTo",
|
||||
"stage": "Stage",
|
||||
"description": "Description",
|
||||
"topic_status": "@TopicStatus",
|
||||
"topic_type": "@TopicType",
|
||||
}
|
||||
for key, value in optional_keys.items():
|
||||
if value in data["Topic"]:
|
||||
setattr(topic, key, data["Topic"][value])
|
||||
|
||||
if "BimSnippet" in data["Topic"]:
|
||||
bim_snippet = bcf.data.BimSnippet()
|
||||
keys = {
|
||||
"snippet_type": "@SnippetType",
|
||||
"is_external": "@IsExternal",
|
||||
"reference": "Reference",
|
||||
"reference_schema": "ReferenceSchema",
|
||||
}
|
||||
for key, value in keys.items():
|
||||
if value in data["Topic"]["BimSnippet"]:
|
||||
setattr(bim_snippet, key, data["Topic"]["BimSnippet"][value])
|
||||
topic.bim_snippet = bim_snippet
|
||||
|
||||
if "DocumentReference" in data["Topic"]:
|
||||
for item in data["Topic"]["DocumentReference"]:
|
||||
document_reference = bcf.data.DocumentReference()
|
||||
keys = {
|
||||
"referenced_document": "ReferencedDocument",
|
||||
"is_external": "@IsExternal",
|
||||
"guid": "@Guid",
|
||||
"description": "Description",
|
||||
}
|
||||
for key, value in keys.items():
|
||||
if value in item:
|
||||
setattr(document_reference, key, item[value])
|
||||
topic.document_references.append(document_reference)
|
||||
|
||||
if "RelatedTopic" in data["Topic"]:
|
||||
for item in data["Topic"]["RelatedTopic"]:
|
||||
related_topic = bcf.data.RelatedTopic()
|
||||
related_topic.guid = item["@Guid"]
|
||||
topic.related_topics.append(related_topic)
|
||||
return topic
|
||||
|
||||
def add_topic(self, topic=None):
|
||||
if topic is None:
|
||||
topic = bcf.data.Topic()
|
||||
if not topic.guid:
|
||||
topic.guid = str(uuid.uuid4())
|
||||
if not topic.title:
|
||||
topic.title = "New Topic"
|
||||
os.mkdir(os.path.join(self.filepath, topic.guid))
|
||||
self.edit_topic(topic)
|
||||
return topic
|
||||
|
||||
def edit_topic(self, topic):
|
||||
if not topic.creation_date:
|
||||
topic.creation_date = datetime.utcnow().isoformat()
|
||||
topic.creation_author = self.author
|
||||
else:
|
||||
topic.modified_date = datetime.utcnow().isoformat()
|
||||
topic.modified_author = self.author
|
||||
|
||||
self.document = minidom.Document()
|
||||
root = self._create_element(self.document, "Markup")
|
||||
|
||||
self.write_header(topic.header, root)
|
||||
|
||||
topic_el = self._create_element(
|
||||
root,
|
||||
"Topic",
|
||||
{
|
||||
"Guid": topic.guid,
|
||||
"TopicType": topic.topic_type,
|
||||
"TopicStatus": topic.topic_status,
|
||||
},
|
||||
)
|
||||
|
||||
for reference_link in topic.reference_links:
|
||||
self._create_element(topic_el, "ReferenceLink", text=reference_link)
|
||||
|
||||
text_map = {
|
||||
"Title": topic.title,
|
||||
"Priority": topic.priority,
|
||||
"Index": topic.index,
|
||||
}
|
||||
for key, value in text_map.items():
|
||||
if value:
|
||||
self._create_element(topic_el, key, text=value)
|
||||
|
||||
for label in topic.labels:
|
||||
self._create_element(topic_el, "Labels", text=label)
|
||||
|
||||
text_map = {
|
||||
"CreationDate": topic.creation_date,
|
||||
"CreationAuthor": topic.creation_author,
|
||||
"ModifiedDate": topic.modified_date,
|
||||
"ModifiedAuthor": topic.modified_author,
|
||||
"DueDate": topic.due_date,
|
||||
"AssignedTo": topic.assigned_to,
|
||||
"Stage": topic.stage,
|
||||
"Description": topic.description,
|
||||
}
|
||||
for key, value in text_map.items():
|
||||
if value:
|
||||
self._create_element(topic_el, key, text=value)
|
||||
|
||||
if topic.bim_snippet:
|
||||
bim_snippet = self._create_element(
|
||||
topic_el,
|
||||
"BimSnippet",
|
||||
{"SnippetType": topic.bim_snippet.snippet_type, "isExternal": topic.bim_snippet.is_external},
|
||||
)
|
||||
self._create_element(bim_snippet, "Reference", text=topic.bim_snippet.reference)
|
||||
self._create_element(bim_snippet, "ReferenceSchema", text=topic.bim_snippet.reference_schema)
|
||||
for reference in topic.document_references:
|
||||
reference_el = self._create_element(
|
||||
topic_el, "DocumentReference", {"Guid": reference.guid, "isExternal": reference.is_external}
|
||||
)
|
||||
self._create_element(reference_el, "ReferencedDocument", text=reference.referenced_document)
|
||||
self._create_element(reference_el, "Description", text=reference.description)
|
||||
for related_topic in topic.related_topics:
|
||||
self._create_element(topic_el, "RelatedTopic", {"Guid": related_topic.guid})
|
||||
|
||||
self.write_comments(topic.comments, root)
|
||||
self.write_viewpoints(topic.viewpoints, root, topic)
|
||||
|
||||
with open(os.path.join(self.filepath, topic.guid, "markup.bcf"), "wb") as f:
|
||||
f.write(self.document.toprettyxml(encoding="utf-8"))
|
||||
|
||||
def write_header(self, header, root):
|
||||
if not header or not header.files:
|
||||
return
|
||||
header_el = self._create_element(root, "Header")
|
||||
for f in header.files:
|
||||
file_el = self._create_element(
|
||||
header_el,
|
||||
"File",
|
||||
{
|
||||
"IfcProject": f.ifc_project,
|
||||
"IfcSpatialStructureElement": f.ifc_spatial_structure_element,
|
||||
"isExternal": f.is_external,
|
||||
},
|
||||
)
|
||||
self._create_element(file_el, "Filename", text=f.filename)
|
||||
self._create_element(file_el, "Date", text=f.date)
|
||||
self._create_element(file_el, "Reference", text=f.reference)
|
||||
|
||||
def write_comments(self, comments, root):
|
||||
for comment in comments.values():
|
||||
comment_el = self._create_element(root, "Comment", {"Guid": comment.guid})
|
||||
text_map = {
|
||||
"Date": comment.date,
|
||||
"Author": comment.author,
|
||||
"Comment": comment.comment,
|
||||
"ModifiedDate": comment.modified_date,
|
||||
"ModifiedAuthor": comment.modified_author,
|
||||
}
|
||||
for key, value in text_map.items():
|
||||
if value:
|
||||
self._create_element(comment_el, key, text=value)
|
||||
if comment.viewpoint:
|
||||
self._create_element(comment_el, "Viewpoint", {"Guid": comment.viewpoint.guid})
|
||||
|
||||
def add_comment(self, topic, comment=None):
|
||||
if comment is None:
|
||||
comment = bcf.data.Comment()
|
||||
if not comment.guid:
|
||||
comment.guid = str(uuid.uuid4())
|
||||
if not comment.comment:
|
||||
comment.comment = "'Free software' is a matter of liberty, not price. To understand the concept, you should think of 'free' as in 'free speech,' not as in 'free beer'."
|
||||
topic.comments[comment.guid] = comment
|
||||
self.edit_comment(comment, topic)
|
||||
|
||||
def edit_comment(self, comment, topic):
|
||||
if not comment.date:
|
||||
comment.date = datetime.utcnow().isoformat()
|
||||
comment.author = self.author
|
||||
else:
|
||||
comment.modified_date = datetime.utcnow().isoformat()
|
||||
comment.modified_author = self.author
|
||||
self.edit_topic(topic)
|
||||
|
||||
def delete_comment(self, guid, topic):
|
||||
if guid in topic.comments:
|
||||
del topic.comments[guid]
|
||||
self.edit_topic(topic)
|
||||
|
||||
def delete_topic(self, guid):
|
||||
if guid in self.topics:
|
||||
del self.topics[guid]
|
||||
shutil.rmtree(os.path.join(self.filepath, guid))
|
||||
|
||||
def write_viewpoints(self, viewpoints, root, topic):
|
||||
for viewpoint in viewpoints.values():
|
||||
viewpoint_el = self._create_element(root, "Viewpoints", {"Guid": viewpoint.guid})
|
||||
text_map = {"Viewpoint": viewpoint.viewpoint, "Snapshot": viewpoint.snapshot, "Index": viewpoint.index}
|
||||
for key, value in text_map.items():
|
||||
if value:
|
||||
self._create_element(viewpoint_el, key, text=value)
|
||||
self.write_viewpoint(viewpoint, topic)
|
||||
|
||||
def write_viewpoint(self, viewpoint, topic):
|
||||
document = minidom.Document()
|
||||
root = self._create_element(document, "VisualizationInfo", {"Guid": viewpoint.guid})
|
||||
self.write_viewpoint_components(viewpoint, root)
|
||||
self.write_viewpoint_orthogonal_camera(viewpoint, root)
|
||||
self.write_viewpoint_perspective_camera(viewpoint, root)
|
||||
self.write_viewpoint_lines(viewpoint, root)
|
||||
self.write_viewpoint_clipping_planes(viewpoint, root)
|
||||
self.write_viewpoint_bitmaps(viewpoint, root)
|
||||
with open(os.path.join(self.filepath, topic.guid, viewpoint.viewpoint), "wb") as f:
|
||||
f.write(document.toprettyxml(encoding="utf-8"))
|
||||
|
||||
def write_viewpoint_components(self, viewpoint, parent):
|
||||
if not viewpoint.components:
|
||||
return
|
||||
components_el = self._create_element(parent, "Components")
|
||||
if viewpoint.components.view_setup_hints:
|
||||
view_setup_hints = self._create_element(
|
||||
components_el,
|
||||
"ViewSetupHints",
|
||||
{
|
||||
"SpacesVisible": viewpoint.components.view_setup_hints.spaces_visible,
|
||||
"SpaceBoundariesVisible": viewpoint.components.view_setup_hints.space_boundaries_visible,
|
||||
"OpeningsVisible": viewpoint.components.view_setup_hints.openings_visible,
|
||||
},
|
||||
)
|
||||
if viewpoint.components.selection:
|
||||
selection_el = self._create_element(components_el, "Selection")
|
||||
for selection in viewpoint.components.selection:
|
||||
self.write_component(selection, selection_el)
|
||||
visibility = self._create_element(
|
||||
components_el, "Visibility", {"DefaultVisibility": viewpoint.components.visibility.default_visibility}
|
||||
)
|
||||
if viewpoint.components.visibility.exceptions:
|
||||
exceptions_el = self._create_element(visibility, "Exceptions")
|
||||
for exception in viewpoint.components.visibility.exceptions:
|
||||
self.write_component(exception, exceptions_el)
|
||||
if viewpoint.components.coloring:
|
||||
coloring_el = self._create_element(components_el, "Coloring")
|
||||
for color in viewpoint.components.coloring:
|
||||
color_el = self._create_element(coloring_el, "Color", {"Color": color.color})
|
||||
for component in color.components:
|
||||
self.write_component(component, color_el)
|
||||
|
||||
def write_viewpoint_orthogonal_camera(self, viewpoint, parent):
|
||||
if not viewpoint.orthogonal_camera:
|
||||
return
|
||||
camera = viewpoint.orthogonal_camera
|
||||
camera_el = self._create_element(parent, "OrthogonalCamera")
|
||||
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
|
||||
self.write_vector(camera_view_point, camera.camera_view_point)
|
||||
camera_direction = self._create_element(camera_el, "CameraDirection")
|
||||
self.write_vector(camera_direction, camera.camera_direction)
|
||||
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
|
||||
self.write_vector(camera_up_vector, camera.camera_up_vector)
|
||||
self._create_element(camera_el, "ViewToWorldScale", text=camera.view_to_world_scale)
|
||||
|
||||
def write_viewpoint_perspective_camera(self, viewpoint, parent):
|
||||
if not viewpoint.perspective_camera:
|
||||
return
|
||||
camera = viewpoint.perspective_camera
|
||||
camera_el = self._create_element(parent, "PerspectiveCamera")
|
||||
camera_view_point = self._create_element(camera_el, "CameraViewPoint")
|
||||
self.write_vector(camera_view_point, camera.camera_view_point)
|
||||
camera_direction = self._create_element(camera_el, "CameraDirection")
|
||||
self.write_vector(camera_direction, camera.camera_direction)
|
||||
camera_up_vector = self._create_element(camera_el, "CameraUpVector")
|
||||
self.write_vector(camera_up_vector, camera.camera_up_vector)
|
||||
self._create_element(camera_el, "FieldOfView", text=camera.field_of_view)
|
||||
|
||||
def write_viewpoint_lines(self, viewpoint, parent):
|
||||
if not viewpoint.lines:
|
||||
return
|
||||
lines_el = self._create_element(parent, "Lines")
|
||||
for line in viewpoint.lines:
|
||||
line_el = self._create_element(lines_el, "Line")
|
||||
start_point_el = self._create_element(line_el, "StartPoint")
|
||||
self.write_vector(start_point_el, line.start_point)
|
||||
end_point_el = self._create_element(line_el, "EndPoint")
|
||||
self.write_vector(end_point_el, line.end_point)
|
||||
|
||||
def write_viewpoint_clipping_planes(self, viewpoint, parent):
|
||||
if not viewpoint.clipping_planes:
|
||||
return
|
||||
planes_el = self._create_element(parent, "ClippingPlanes")
|
||||
for plane in viewpoint.clipping_planes:
|
||||
plane_el = self._create_element(planes_el, "ClippingPlane")
|
||||
location_el = self._create_element(plane_el, "Location")
|
||||
self.write_vector(location_el, plane.location)
|
||||
direction_el = self._create_element(plane_el, "Direction")
|
||||
self.write_vector(direction_el, plane.direction)
|
||||
|
||||
def write_viewpoint_bitmaps(self, viewpoint, parent):
|
||||
if not viewpoint.bitmaps:
|
||||
return
|
||||
for bitmap in viewpoint.bitmaps:
|
||||
bitmap_el = self._create_element(parent, "Bitmap")
|
||||
|
||||
text_map = {"Bitmap": bitmap.bitmap_type, "Reference": bitmap.reference}
|
||||
for key, value in text_map.items():
|
||||
self._create_element(bitmap_el, key, text=value)
|
||||
|
||||
location_el = self._create_element(bitmap_el, "Location")
|
||||
self.write_vector(location_el, bitmap.location)
|
||||
normal_el = self._create_element(bitmap_el, "Normal")
|
||||
self.write_vector(normal_el, bitmap.normal)
|
||||
up_el = self._create_element(bitmap_el, "Up")
|
||||
self.write_vector(up_el, bitmap.up)
|
||||
|
||||
self._create_element(bitmap_el, "Height", text=bitmap.height)
|
||||
|
||||
def write_vector(self, parent, from_obj):
|
||||
self._create_element(parent, "X", text=from_obj.x)
|
||||
self._create_element(parent, "Y", text=from_obj.y)
|
||||
self._create_element(parent, "Z", text=from_obj.z)
|
||||
|
||||
def write_component(self, data, parent):
|
||||
component_el = self._create_element(parent, "Component", {"IfcGuid": data.ifc_guid})
|
||||
text_map = {"OriginatingSystem": data.originating_system, "AuthoringToolId": data.authoring_tool_id}
|
||||
for key, value in text_map.items():
|
||||
if value:
|
||||
self._create_element(component_el, key, text=value)
|
||||
|
||||
def add_viewpoint(self, topic, viewpoint=None):
|
||||
if not viewpoint:
|
||||
viewpoint = bcf.data.Viewpoint()
|
||||
if not viewpoint.guid:
|
||||
viewpoint.guid = str(uuid.uuid4())
|
||||
if not viewpoint.viewpoint:
|
||||
viewpoint.viewpoint = f"{viewpoint.guid}.bcfv"
|
||||
if viewpoint.snapshot:
|
||||
topic_filepath = os.path.join(self.filepath, topic.guid)
|
||||
filepath = os.path.join(topic_filepath, viewpoint.snapshot)
|
||||
if not os.path.exists(filepath):
|
||||
filename = viewpoint.guid + os.path.splitext(viewpoint.snapshot)[-1]
|
||||
copyfile(viewpoint.snapshot, os.path.join(topic_filepath, filename))
|
||||
viewpoint.snapshot = filename
|
||||
topic.viewpoints[viewpoint.guid] = viewpoint
|
||||
self.edit_topic(topic)
|
||||
|
||||
def delete_viewpoint(self, guid, topic):
|
||||
if guid not in topic.viewpoints:
|
||||
return
|
||||
viewpoint = topic.viewpoints[guid]
|
||||
if viewpoint.snapshot:
|
||||
filepath = os.path.join(self.filepath, topic.guid, viewpoint.snapshot)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
if viewpoint.viewpoint:
|
||||
filepath = os.path.join(self.filepath, topic.guid, viewpoint.viewpoint)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
for bitmap in viewpoint.bitmaps:
|
||||
if not bitmap.reference:
|
||||
continue
|
||||
filepath = os.path.join(self.filepath, topic.guid, bitmap.reference)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
del topic.viewpoints[guid]
|
||||
self.edit_topic(topic)
|
||||
|
||||
def delete_file(self, topic, index):
|
||||
if not topic.header:
|
||||
return
|
||||
f = topic.header.files.pop(index)
|
||||
filepath = os.path.join(self.filepath, topic.guid, f.reference)
|
||||
if not f.is_external and os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
self.edit_topic(topic)
|
||||
|
||||
def delete_bim_snippet(self, topic):
|
||||
if not topic.bim_snippet:
|
||||
return
|
||||
if topic.bim_snippet.reference and not topic.bim_snippet.is_external:
|
||||
filepath = os.path.join(self.filepath, topic.guid, topic.bim_snippet.reference)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
topic.bim_snippet = None
|
||||
self.edit_topic(topic)
|
||||
|
||||
def delete_document_reference(self, topic, index):
|
||||
document_reference = topic.document_references[index]
|
||||
if document_reference.referenced_document and not document_reference.is_external:
|
||||
filepath = os.path.join(self.filepath, topic.guid, document_reference.referenced_document)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
del topic.document_references[index]
|
||||
self.edit_topic(topic)
|
||||
|
||||
def add_document_reference(self, topic, document_reference):
|
||||
if os.path.exists(document_reference.referenced_document):
|
||||
topic_filepath = os.path.join(self.filepath, topic.guid)
|
||||
filename = os.path.basename(document_reference.referenced_document)
|
||||
copyfile(document_reference.referenced_document, os.path.join(topic_filepath, filename))
|
||||
document_reference.referenced_document = filename
|
||||
document_reference.is_external = False
|
||||
else:
|
||||
document_reference.is_external = True
|
||||
if not document_reference.guid:
|
||||
document_reference.guid = str(uuid.uuid4())
|
||||
topic.document_references.append(document_reference)
|
||||
self.edit_topic(topic)
|
||||
|
||||
def add_bim_snippet(self, topic, bim_snippet):
|
||||
if topic.bim_snippet:
|
||||
self.delete_bim_snippet(topic)
|
||||
if os.path.exists(bim_snippet.reference):
|
||||
topic_filepath = os.path.join(self.filepath, topic.guid)
|
||||
filename = os.path.basename(bim_snippet.reference)
|
||||
copyfile(bim_snippet.reference, os.path.join(topic_filepath, filename))
|
||||
bim_snippet.reference = filename
|
||||
bim_snippet.is_external = False
|
||||
else:
|
||||
bim_snippet.is_external = True
|
||||
topic.bim_snippet = bim_snippet
|
||||
self.edit_topic(topic)
|
||||
|
||||
def add_file(self, topic, header_file):
|
||||
if os.path.exists(header_file.reference):
|
||||
topic_filepath = os.path.join(self.filepath, topic.guid)
|
||||
header_file.filename = os.path.basename(header_file.reference)
|
||||
copyfile(header_file.reference, os.path.join(topic_filepath, header_file.filename))
|
||||
header_file.reference = header_file.filename
|
||||
header_file.is_external = False
|
||||
header_file.date = datetime.utcnow().isoformat()
|
||||
if not topic.header:
|
||||
topic.header = bcf.data.Header()
|
||||
topic.header.files.append(header_file)
|
||||
self.edit_topic(topic)
|
||||
|
||||
def get_comments(self, guid):
|
||||
comments = {}
|
||||
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||
if "Comment" not in data:
|
||||
return comments
|
||||
for item in data["Comment"]:
|
||||
comment = bcf.data.Comment()
|
||||
mandatory_keys = {"guid": "@Guid", "date": "Date", "author": "Author", "comment": "Comment"}
|
||||
for key, value in mandatory_keys.items():
|
||||
setattr(comment, key, item[value])
|
||||
optional_keys = {"modified_date": "ModifiedDate", "modified_author": "ModifiedAuthor"}
|
||||
for key, value in optional_keys.items():
|
||||
if value in item:
|
||||
setattr(comment, key, item[value])
|
||||
if "Viewpoint" in item:
|
||||
viewpoint = bcf.data.Viewpoint()
|
||||
viewpoint.guid = item["Viewpoint"]["@Guid"]
|
||||
comment.viewpoint = viewpoint
|
||||
comments[comment.guid] = comment
|
||||
self.topics[guid].comments = comments
|
||||
return comments
|
||||
|
||||
def get_viewpoints(self, guid):
|
||||
viewpoints = {}
|
||||
data = self._read_xml(os.path.join(guid, "markup.bcf"), "markup.xsd")
|
||||
if "Viewpoints" not in data:
|
||||
return viewpoints
|
||||
for item in data["Viewpoints"]:
|
||||
viewpoint = self.get_viewpoint(item, guid)
|
||||
viewpoints[viewpoint.guid] = viewpoint
|
||||
self.topics[guid].viewpoints = viewpoints
|
||||
return viewpoints
|
||||
|
||||
def get_viewpoint(self, data, topic_guid):
|
||||
viewpoint = bcf.data.Viewpoint()
|
||||
viewpoint.guid = data["@Guid"]
|
||||
optional_keys = {"viewpoint": "Viewpoint", "snapshot": "Snapshot", "index": "Index"}
|
||||
for key, value in optional_keys.items():
|
||||
if value in data:
|
||||
setattr(viewpoint, key, data[value])
|
||||
visinfo = self._read_xml(os.path.join(topic_guid, viewpoint.viewpoint), "visinfo.xsd")
|
||||
viewpoint.components = self.get_viewpoint_components(visinfo)
|
||||
viewpoint.orthogonal_camera = self.get_viewpoint_orthogonal_camera(visinfo)
|
||||
viewpoint.perspective_camera = self.get_viewpoint_perspective_camera(visinfo)
|
||||
viewpoint.lines = self.get_viewpoint_lines(visinfo)
|
||||
viewpoint.clipping_planes = self.get_viewpoint_clipping_planes(visinfo)
|
||||
viewpoint.bitmaps = self.get_viewpoint_bitmaps(visinfo)
|
||||
return viewpoint
|
||||
|
||||
def get_viewpoint_components(self, visinfo):
|
||||
if "Components" not in visinfo:
|
||||
return None
|
||||
components = bcf.data.Components()
|
||||
data = visinfo["Components"]
|
||||
if "ViewSetupHints" in data:
|
||||
view_setup_hints = bcf.data.ViewSetupHints()
|
||||
optional_keys = {
|
||||
"spaces_visible": "@SpacesVisible",
|
||||
"space_boundaries_visible": "@SpaceBoundariesVisible",
|
||||
"openings_visible": "@OpeningsVisible",
|
||||
}
|
||||
for key, value in optional_keys.items():
|
||||
if value in data["ViewSetupHints"]:
|
||||
setattr(view_setup_hints, key, data["ViewSetupHints"][value])
|
||||
components.view_setup_hints = view_setup_hints
|
||||
if "Selection" in data and "Component" in data["Selection"]:
|
||||
for item in data["Selection"]["Component"]:
|
||||
components.selection.append(self.get_component(item))
|
||||
if "Visibility" in data:
|
||||
component_visibility = bcf.data.ComponentVisibility()
|
||||
if "@DefaultVisibility" in data["Visibility"]:
|
||||
component_visibility.default_visibility = data["Visibility"]["@DefaultVisibility"]
|
||||
if "Exceptions" in data["Visibility"] and "Component" in data["Visibility"]["Exceptions"]:
|
||||
for item in data["Visibility"]["Exceptions"]["Component"]:
|
||||
component_visibility.exceptions.append(self.get_component(item))
|
||||
components.visibility = component_visibility
|
||||
if "Coloring" in data and "Color" in data["Coloring"]:
|
||||
for item in data["Coloring"]["Color"]:
|
||||
color = bcf.data.Color()
|
||||
color.color = item["@Color"]
|
||||
for item2 in item["Component"]:
|
||||
color.components.append(self.get_component(item2))
|
||||
components.coloring.append(color)
|
||||
return components
|
||||
|
||||
def get_viewpoint_orthogonal_camera(self, visinfo):
|
||||
if "OrthogonalCamera" not in visinfo:
|
||||
return None
|
||||
camera = bcf.data.OrthogonalCamera()
|
||||
data = visinfo["OrthogonalCamera"]
|
||||
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
|
||||
self.set_vector(camera.camera_direction, data["CameraDirection"])
|
||||
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
|
||||
camera.view_to_world_scale = data["ViewToWorldScale"]
|
||||
return camera
|
||||
|
||||
def get_viewpoint_perspective_camera(self, visinfo):
|
||||
if "PerspectiveCamera" not in visinfo:
|
||||
return None
|
||||
camera = bcf.data.PerspectiveCamera()
|
||||
data = visinfo["PerspectiveCamera"]
|
||||
self.set_vector(camera.camera_view_point, data["CameraViewPoint"])
|
||||
self.set_vector(camera.camera_direction, data["CameraDirection"])
|
||||
self.set_vector(camera.camera_up_vector, data["CameraUpVector"])
|
||||
camera.field_of_view = data["FieldOfView"]
|
||||
return camera
|
||||
|
||||
def get_viewpoint_lines(self, visinfo):
|
||||
if "Lines" not in visinfo:
|
||||
return []
|
||||
lines = []
|
||||
for item in visinfo["Lines"]["Line"]:
|
||||
line = bcf.data.Line()
|
||||
self.set_vector(line.start_point, item["StartPoint"])
|
||||
self.set_vector(line.end_point, item["EndPoint"])
|
||||
lines.append(line)
|
||||
return lines
|
||||
|
||||
def get_viewpoint_clipping_planes(self, visinfo):
|
||||
if "ClippingPlanes" not in visinfo:
|
||||
return []
|
||||
planes = []
|
||||
for item in visinfo["ClippingPlanes"]["ClippingPlane"]:
|
||||
plane = bcf.data.ClippingPlane()
|
||||
self.set_vector(plane.location, item["Location"])
|
||||
self.set_vector(plane.direction, item["Direction"])
|
||||
planes.append(plane)
|
||||
return planes
|
||||
|
||||
def get_viewpoint_bitmaps(self, visinfo):
|
||||
if "Bitmap" not in visinfo:
|
||||
return []
|
||||
bitmaps = []
|
||||
for item in visinfo["Bitmap"]:
|
||||
bitmap = bcf.data.Bitmap()
|
||||
bitmap.reference = item["Reference"]
|
||||
bitmap.bitmap_type = item["Bitmap"].upper()
|
||||
self.set_vector(bitmap.location, item["Location"])
|
||||
self.set_vector(bitmap.normal, item["Normal"])
|
||||
self.set_vector(bitmap.up, item["Up"])
|
||||
bitmap.height = item["Height"]
|
||||
bitmaps.append(bitmap)
|
||||
return bitmaps
|
||||
|
||||
def set_vector(self, to_obj, from_xml):
|
||||
to_obj.x = from_xml["X"]
|
||||
to_obj.y = from_xml["Y"]
|
||||
to_obj.z = from_xml["Z"]
|
||||
|
||||
def get_component(self, data):
|
||||
component = bcf.data.Component()
|
||||
optional_keys = {
|
||||
"originating_system": "OriginatingSystem",
|
||||
"authoring_tool_id": "AuthoringToolId",
|
||||
"ifc_guid": "@IfcGuid",
|
||||
}
|
||||
for key, value in optional_keys.items():
|
||||
if value in data:
|
||||
setattr(component, key, data[value])
|
||||
return component
|
||||
|
||||
def close_project(self):
|
||||
shutil.rmtree(self.filepath)
|
||||
|
||||
def _read_xml(self, filename, xsd):
|
||||
schema = XMLSchema(os.path.join(cwd, "xsd", xsd))
|
||||
filepath = os.path.join(self.filepath, filename)
|
||||
(data, errors) = schema.to_dict(filepath, validation="lax")
|
||||
for error in errors:
|
||||
self.logger.error(error)
|
||||
return data
|
||||
|
||||
def _create_element(self, parent, name, attributes={}, text=None):
|
||||
element = self.document.createElement(name)
|
||||
for key, value in attributes.items():
|
||||
if isinstance(value, bool):
|
||||
element.setAttribute(key, str(value).lower())
|
||||
elif value:
|
||||
element.setAttribute(key, value)
|
||||
if text is not None:
|
||||
text = self.document.createTextNode(str(text))
|
||||
element.appendChild(text)
|
||||
parent.appendChild(element)
|
||||
return element
|
||||
|
||||
def __del__(self):
|
||||
self.close_project()
|
||||
@@ -0,0 +1,178 @@
|
||||
class Project:
|
||||
def __init__(self):
|
||||
self.project_id = ""
|
||||
self.name = ""
|
||||
|
||||
|
||||
class BimSnippet:
|
||||
def __init__(self):
|
||||
self.snippet_type = None
|
||||
self.is_external = False
|
||||
self.reference = None
|
||||
self.reference_schema = None
|
||||
|
||||
|
||||
class DocumentReference:
|
||||
def __init__(self):
|
||||
self.referenced_document = None
|
||||
self.description = None
|
||||
self.guid = None
|
||||
self.is_external = False
|
||||
|
||||
|
||||
class RelatedTopic:
|
||||
def __init__(self):
|
||||
self.guid = None
|
||||
|
||||
|
||||
class HeaderFile:
|
||||
def __init__(self):
|
||||
self.filename = None
|
||||
self.date = None
|
||||
self.reference = None
|
||||
self.ifc_project = None
|
||||
self.ifc_spatial_structure_element = None
|
||||
self.is_external = True
|
||||
|
||||
|
||||
class Header:
|
||||
def __init__(self):
|
||||
self.files = []
|
||||
|
||||
|
||||
class Topic:
|
||||
def __init__(self):
|
||||
self.reference_links = []
|
||||
self.title = ""
|
||||
self.priority = None
|
||||
self.index = None # Deprecated, stored, but ignored
|
||||
self.labels = []
|
||||
self.creation_date = None
|
||||
self.creation_author = None
|
||||
self.modified_date = None
|
||||
self.modified_author = None
|
||||
self.due_date = None
|
||||
self.assigned_to = None
|
||||
self.stage = None
|
||||
self.description = None
|
||||
self.bim_snippet = None
|
||||
self.document_references = []
|
||||
self.related_topics = []
|
||||
self.topic_status = None
|
||||
self.topic_type = None
|
||||
self.guid = None
|
||||
|
||||
self.header = None
|
||||
self.comments = {}
|
||||
self.viewpoints = {}
|
||||
|
||||
|
||||
class Comment:
|
||||
def __init__(self):
|
||||
self.guid = None
|
||||
self.date = None
|
||||
self.author = None
|
||||
self.comment = None
|
||||
self.viewpoint = None
|
||||
self.modified_date = None
|
||||
self.modified_author = None
|
||||
self.topic_guid = None # Part of BCF-API
|
||||
|
||||
|
||||
class ViewSetupHints:
|
||||
def __init__(self):
|
||||
self.spaces_visible = False
|
||||
self.space_boundaries_visible = False
|
||||
self.openings_visible = False
|
||||
|
||||
|
||||
class Component:
|
||||
def __init__(self):
|
||||
self.originating_system = None
|
||||
self.authoring_tool_id = None
|
||||
self.ifc_guid = None
|
||||
|
||||
|
||||
class ComponentVisibility:
|
||||
def __init__(self):
|
||||
self.exceptions = []
|
||||
self.default_visibility = False
|
||||
|
||||
|
||||
class Color:
|
||||
def __init__(self):
|
||||
self.color = None
|
||||
self.components = []
|
||||
|
||||
|
||||
class Components:
|
||||
def __init__(self):
|
||||
self.view_setup_hints = None
|
||||
self.selection = []
|
||||
self.visibility = None
|
||||
self.coloring = []
|
||||
|
||||
|
||||
class Point:
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.z = 0
|
||||
|
||||
|
||||
class Direction(Point):
|
||||
pass
|
||||
|
||||
|
||||
class OrthogonalCamera:
|
||||
def __init__(self):
|
||||
self.camera_view_point = Point()
|
||||
self.camera_direction = Direction()
|
||||
self.camera_up_vector = Direction()
|
||||
self.view_to_world_scale = 1.0
|
||||
|
||||
|
||||
class PerspectiveCamera:
|
||||
def __init__(self):
|
||||
self.camera_view_point = Point()
|
||||
self.camera_direction = Direction()
|
||||
self.camera_up_vector = Direction()
|
||||
self.field_of_view = 60.0
|
||||
|
||||
|
||||
class Line:
|
||||
def __init__(self):
|
||||
self.start_point = Point()
|
||||
self.end_point = Point()
|
||||
|
||||
|
||||
class ClippingPlane:
|
||||
def __init__(self):
|
||||
self.location = Point()
|
||||
self.direction = Direction()
|
||||
|
||||
|
||||
class Bitmap:
|
||||
def __init__(self):
|
||||
self.reference = "" # Only in BCF-XML
|
||||
self.bitmap_data = None # Only in BCF-API
|
||||
self.bitmap_type = "PNG" # Enum of png or jpg
|
||||
self.location = Point()
|
||||
self.normal = Direction()
|
||||
self.up = Direction()
|
||||
self.height = 1.0
|
||||
|
||||
|
||||
class Viewpoint:
|
||||
def __init__(self):
|
||||
self.guid = None
|
||||
self.viewpoint = None
|
||||
self.snapshot = None
|
||||
self.index = None
|
||||
|
||||
self.components = None # It's not a list, despite the plural name
|
||||
self.orthogonal_camera = None
|
||||
self.perspective_camera = None
|
||||
self.lines = []
|
||||
self.clipping_planes = []
|
||||
self.bitmaps = []
|
||||
@@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="Markup">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Header" type="Header" minOccurs="0"/>
|
||||
<xs:element name="Topic" type="Topic"/>
|
||||
<xs:element name="Comment" type="Comment" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
|
||||
<xs:element name="Viewpoints" type="ViewPoint" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="Header">
|
||||
<xs:sequence>
|
||||
<xs:element name="File" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Filename" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="Date" type="xs:dateTime" minOccurs="0"/>
|
||||
<!-- Reference (URL) of the file -->
|
||||
<xs:element name="Reference" type="xs:string" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="FileAttributes"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<!-- ISG Jira issue BCF-9. Add support for several viewpoints and snapshots per issue -->
|
||||
<xs:complexType name="ViewPoint">
|
||||
<xs:sequence>
|
||||
<!-- viewpoint file (xml) -->
|
||||
<xs:element name="Viewpoint" type="xs:string" minOccurs="0"/>
|
||||
<!-- the snapshot png -->
|
||||
<xs:element name="Snapshot" type="xs:string" minOccurs="0"/>
|
||||
<!-- the viewpoint index (sort order) -->
|
||||
<xs:element name="Index" type="xs:int" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||
<!-- Guid of the viewpoint -->
|
||||
</xs:complexType>
|
||||
<!-- BimSnippet -->
|
||||
<xs:complexType name="BimSnippet">
|
||||
<xs:sequence>
|
||||
<!--
|
||||
Name of the file in the topic folder containing the snippet or a URL.
|
||||
E.G.- Expresscode containing p.e Issue, Request
|
||||
// Maybe some header infos ?? // IfcEntites // Geometry
|
||||
-->
|
||||
<!-- Reference (name) to the snippet file -->
|
||||
<xs:element name="Reference" type="xs:string"/>
|
||||
<xs:element name="ReferenceSchema" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="SnippetType" type="xs:string" use="required"/>
|
||||
<xs:attribute name="isExternal" type="xs:boolean" default="false"/>
|
||||
<!-- This flag is true when the reference is a URL pointing outside of the BCF file-->
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Topic">
|
||||
<xs:sequence>
|
||||
<xs:element name="ReferenceLink" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="Title" type="xs:string"/>
|
||||
<xs:element name="Priority" type="Priority" minOccurs="0"/>
|
||||
<!-- ISG Jira issue BCF-8 Add a way save order the topics -->
|
||||
<xs:element name="Index" type="xs:int" minOccurs="0"/>
|
||||
<xs:element name="Labels" type="TopicLabel" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="CreationDate" type="xs:dateTime" minOccurs="1"/>
|
||||
<xs:element name="CreationAuthor" type="UserIdType" minOccurs="1"/>
|
||||
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
|
||||
<xs:element name="ModifiedAuthor" type="UserIdType" minOccurs="0"/>
|
||||
<xs:element name="DueDate" type="xs:dateTime" minOccurs="0"/>
|
||||
<xs:element name="AssignedTo" type="UserIdType" minOccurs="0"/>
|
||||
<xs:element name="Stage" type="Stage" minOccurs="0"/>
|
||||
<xs:element name="Description" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="BimSnippet" type="BimSnippet" minOccurs="0"/>
|
||||
<!-- Name of the file in the topic folder or url -->
|
||||
<xs:element name="DocumentReference" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<!-- Name of the file in the topic folder or url -->
|
||||
<xs:element name="ReferencedDocument" type="xs:string" minOccurs="0"/>
|
||||
<!-- Human readable name of the document -->
|
||||
<xs:element name="Description" type="xs:string" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="DocumentReference"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="RelatedTopic" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||
<xs:attribute name="TopicType" type="TopicType"/>
|
||||
<xs:attribute name="TopicStatus" type="TopicStatus"/>
|
||||
</xs:complexType>
|
||||
<!-- Reference to a document inside of the topic folder or a url pointing to the web -->
|
||||
<xs:attributeGroup name="DocumentReference">
|
||||
<!-- Guid of the DocumentReference -->
|
||||
<xs:attribute name="Guid" type="Guid"/>
|
||||
<!-- A flag that is true when the ReferencedDocument points outside of the BCF file (a URL) -->
|
||||
<xs:attribute name="isExternal" type="xs:boolean" default="false"/>
|
||||
</xs:attributeGroup>
|
||||
<xs:complexType name="Comment">
|
||||
<xs:sequence>
|
||||
<xs:element name="Date" type="xs:dateTime"/>
|
||||
<xs:element name="Author" type="UserIdType"/>
|
||||
<xs:element name="Comment" type="xs:string"/>
|
||||
<xs:element name="Viewpoint" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ModifiedDate" type="xs:dateTime" minOccurs="0"/>
|
||||
<xs:element name="ModifiedAuthor" type="UserIdType" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="TopicStatus">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="TopicType">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="TopicLabel">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Priority">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="UserIdType">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Stage">
|
||||
<xs:restriction base="xs:string"/>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Guid">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="IfcGuid">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:length value="22"/>
|
||||
<xs:pattern value="[0-9,A-Z,a-z,_$]*"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:attributeGroup name="FileAttributes">
|
||||
<xs:attribute name="IfcProject" type="IfcGuid"/>
|
||||
<xs:attribute name="IfcSpatialStructureElement" type="IfcGuid"/>
|
||||
<xs:attribute name="isExternal" type="xs:boolean" default="true"/>
|
||||
</xs:attributeGroup>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="ProjectExtension">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Project" type="Project" minOccurs="0"/>
|
||||
<xs:element name="ExtensionSchema" type="xs:anyURI"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="Project">
|
||||
<xs:sequence>
|
||||
<xs:element name="Name" type="xs:string" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="ProjectId" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Mit XMLSpy v2011 rel. 3 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:element name="Version">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="DetailedVersion" type="xs:string" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="VersionId" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,191 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Mit XMLSpy v2011 rel. 2 sp1 (http://www.altova.com) von Klaus Linhard (IABI e.V.) bearbeitet -->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||
<xs:element name="VisualizationInfo">
|
||||
<xs:annotation>
|
||||
<xs:documentation>VisualizationInfo documentation</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Components" type="Components" minOccurs="0"/>
|
||||
<xs:element name="OrthogonalCamera" type="OrthogonalCamera" minOccurs="0"/>
|
||||
<xs:element name="PerspectiveCamera" type="PerspectiveCamera" minOccurs="0"/>
|
||||
<xs:element name="Lines" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Line" type="Line" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ClippingPlanes" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="ClippingPlane" type="ClippingPlane" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<!-- ISG Jira issue BCF-17: Add support for text in the viewpoints -->
|
||||
<xs:element name="Bitmap" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Bitmap" type="BitmapFormat"/>
|
||||
<!-- Name of the bitmap file in the topic folder -->
|
||||
<xs:element name="Reference" type="xs:string"/>
|
||||
<!-- Location of the center of the bitmap -->
|
||||
<xs:element name="Location" type="Point"/>
|
||||
<!-- Normal of the bitmap -->
|
||||
<xs:element name="Normal" type="Direction"/>
|
||||
<!-- Upvector of the bitmap -->
|
||||
<xs:element name="Up" type="Direction"/>
|
||||
<!-- Height of the bitmap -->
|
||||
<xs:element name="Height" type="xs:double"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<!-- Guid of the viewpoint -->
|
||||
<xs:attribute name="Guid" type="Guid" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="OrthogonalCamera">
|
||||
<xs:sequence>
|
||||
<xs:element name="CameraViewPoint" type="Point"/>
|
||||
<xs:element name="CameraDirection" type="Direction"/>
|
||||
<xs:element name="CameraUpVector" type="Direction"/>
|
||||
<xs:element name="ViewToWorldScale" type="xs:double">
|
||||
<xs:annotation>
|
||||
<xs:documentation>view's visible size in meters</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="PerspectiveCamera">
|
||||
<xs:sequence>
|
||||
<xs:element name="CameraViewPoint" type="Point"/>
|
||||
<xs:element name="CameraDirection" type="Direction"/>
|
||||
<xs:element name="CameraUpVector" type="Direction"/>
|
||||
<xs:element name="FieldOfView" type="FieldOfView">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
It is currently limited to a value between 45 and 60 degrees.
|
||||
This limitation will be dropped in the next release and viewers
|
||||
should be expect values outside this range in current implementations.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Point">
|
||||
<xs:sequence>
|
||||
<xs:element name="X" type="xs:double"/>
|
||||
<xs:element name="Y" type="xs:double"/>
|
||||
<xs:element name="Z" type="xs:double"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Direction">
|
||||
<xs:sequence>
|
||||
<xs:element name="X" type="xs:double"/>
|
||||
<xs:element name="Y" type="xs:double"/>
|
||||
<xs:element name="Z" type="xs:double"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="FieldOfView">
|
||||
<xs:restriction base="xs:double">
|
||||
<xs:minInclusive value="1"/>
|
||||
<xs:maxInclusive value="170"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="Components">
|
||||
<xs:sequence>
|
||||
<xs:element name="ViewSetupHints" type="ViewSetupHints" minOccurs="0" />
|
||||
<!-- Components with relevance to the viewpoint. They should be displayed highlighted or selected in a viewer -->
|
||||
<xs:element name="Selection" type="ComponentSelection" minOccurs="0" />
|
||||
<xs:element name="Visibility" type="ComponentVisibility" minOccurs="1" />
|
||||
<xs:element name="Coloring" type="ComponentColoring" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="ViewSetupHints">
|
||||
<xs:attribute name="SpacesVisible" type="xs:boolean"/>
|
||||
<xs:attribute name="SpaceBoundariesVisible" type="xs:boolean"/>
|
||||
<xs:attribute name="OpeningsVisible" type="xs:boolean"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="ComponentSelection">
|
||||
<xs:sequence>
|
||||
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="ComponentVisibility">
|
||||
<xs:sequence>
|
||||
<xs:element name="Exceptions" minOccurs="0">
|
||||
<!-- List Components that are different than the DefaultVisibility. E.g. if DefaultVisibility = false then list
|
||||
Components that should be visible -->
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="DefaultVisibility" type="xs:boolean"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="ComponentColoring">
|
||||
<xs:sequence>
|
||||
<xs:element name="Color" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Component" type="Component" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute ref="Color"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Component">
|
||||
<xs:sequence>
|
||||
<xs:element name="OriginatingSystem" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="AuthoringToolId" type="xs:string" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute ref="IfcGuid"/>
|
||||
<!-- ISG Jira Issue BCF-14 -->
|
||||
</xs:complexType>
|
||||
<xs:attribute name="Color">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:normalizedString">
|
||||
<!-- Should either match 3 or 4 hex bytes , e.g. "FF00FF" or "FF00FF99" -->
|
||||
<xs:pattern value="[0-9,a-f,A-F]{6}([0-9,a-f,A-F]{2})?"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IfcGuid">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:normalizedString">
|
||||
<xs:length value="22"/>
|
||||
<xs:pattern value="[0-9,A-Z,a-z,_$]*"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:complexType name="Line">
|
||||
<xs:sequence>
|
||||
<xs:element name="StartPoint" type="Point"/>
|
||||
<xs:element name="EndPoint" type="Point"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="ClippingPlane">
|
||||
<xs:sequence>
|
||||
<xs:element name="Location" type="Point"/>
|
||||
<xs:element name="Direction" type="Direction"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<!-- ISG Jira issue BCF-17: Add support for text in the viewpoints -->
|
||||
<xs:simpleType name="BitmapFormat">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="PNG"/>
|
||||
<xs:enumeration value="JPG"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Guid">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
@@ -1,143 +1,143 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Example of curve rebar. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "ifcparse\Ifc2x3.h"
|
||||
#include "ifcparse\IfcUtil.h"
|
||||
#include "ifcparse\IfcHierarchyHelper.h"
|
||||
#include "ifcgeom\IfcGeom.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
void create_curve_rebar(IfcHierarchyHelper& file)
|
||||
{
|
||||
int dia = 24;
|
||||
int R = 3 * dia;
|
||||
int length = 12 * dia;
|
||||
|
||||
double crossSectionarea = M_PI * (dia / 2) * 2;
|
||||
IfcSchema::IfcReinforcingBar* rebar = new IfcSchema::IfcReinforcingBar(
|
||||
guid(), 0, S("test"), null,
|
||||
null, 0, 0,
|
||||
null, S("SR24"), //SteelGrade
|
||||
dia, //diameter
|
||||
crossSectionarea, //crossSectionarea = math.pi*(12.0/2)**2
|
||||
0,
|
||||
IfcSchema::IfcReinforcingBarRoleEnum::IfcReinforcingBarRoleEnum::IfcReinforcingBarRole_LIGATURE,
|
||||
IfcSchema::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurface_PLAIN //PLAIN or TEXTURED
|
||||
);
|
||||
|
||||
file.addBuildingProduct(rebar);
|
||||
rebar->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment::list::ptr segments(new IfcSchema::IfcCompositeCurveSegment::list());
|
||||
|
||||
IfcSchema::IfcCartesianPoint* p1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, 0, 1000.);
|
||||
IfcSchema::IfcCartesianPoint* p2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, 0, 0);
|
||||
IfcSchema::IfcCartesianPoint* p3 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R, 0);
|
||||
IfcSchema::IfcCartesianPoint* p4 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R, -R);
|
||||
IfcSchema::IfcCartesianPoint* p5 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R + length, -R);
|
||||
|
||||
/*first segment - line */
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points1(new IfcSchema::IfcCartesianPoint::list());
|
||||
points1->push(p1);
|
||||
points1->push(p2);
|
||||
file.addEntities(points1->generalize());
|
||||
IfcSchema::IfcPolyline* poly1 = new IfcSchema::IfcPolyline(points1);
|
||||
file.addEntity(poly1);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment1 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly1);
|
||||
file.addEntity(segment1);
|
||||
segments->push(segment1);
|
||||
|
||||
/*second segment - arc */
|
||||
IfcSchema::IfcAxis2Placement3D* axis1 = new IfcSchema::IfcAxis2Placement3D(p3, file.addTriplet<IfcSchema::IfcDirection>(1, 0, 0), file.addTriplet<IfcSchema::IfcDirection>(0, 1, 0));
|
||||
file.addEntity(axis1);
|
||||
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(axis1, R);
|
||||
file.addEntity(circle);
|
||||
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
|
||||
trim1->push(new IfcSchema::IfcParameterValue(180));
|
||||
trim1->push(p2);
|
||||
|
||||
trim2->push(new IfcSchema::IfcParameterValue(270));
|
||||
trim2->push(p4);
|
||||
IfcSchema::IfcTrimmedCurve* trimmed_curve = new IfcSchema::IfcTrimmedCurve(circle, trim1, trim2, false, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
file.addEntity(trimmed_curve);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment2 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, false, trimmed_curve);
|
||||
file.addEntity(segment2);
|
||||
segments->push(segment2);
|
||||
|
||||
/*third segment - line */
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points2(new IfcSchema::IfcCartesianPoint::list());
|
||||
points2->push(p4);
|
||||
points2->push(p5);
|
||||
file.addEntities(points2->generalize());
|
||||
IfcSchema::IfcPolyline* poly2 = new IfcSchema::IfcPolyline(points2);
|
||||
file.addEntity(poly2);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment3 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly2);
|
||||
file.addEntity(segment3);
|
||||
segments->push(segment3);
|
||||
|
||||
IfcSchema::IfcCompositeCurve* curve = new IfcSchema::IfcCompositeCurve(segments, false);
|
||||
file.addEntity(curve);
|
||||
|
||||
IfcSchema::IfcSweptDiskSolid* solid = new IfcSchema::IfcSweptDiskSolid(curve, dia / 2, null, 0, 1);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list());
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list());
|
||||
items->push(solid);
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("AdvancedSweptSolid"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
file.addEntity(shape);
|
||||
|
||||
rebar->setRepresentation(shape);
|
||||
|
||||
IfcSchema::IfcObjectPlacement* storey_placement = file.getSingle<IfcSchema::IfcBuildingStorey>()->ObjectPlacement();
|
||||
rebar->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 0, 0));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
IfcHierarchyHelper file;
|
||||
file.header().file_name().name("ifc_curve_rebar.ifc");
|
||||
create_curve_rebar(file);
|
||||
std::ofstream f("ifc_curve_rebar.ifc");
|
||||
f << file;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Example of curve rebar. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "ifcparse\Ifc2x3.h"
|
||||
#include "ifcparse\IfcUtil.h"
|
||||
#include "ifcparse\IfcHierarchyHelper.h"
|
||||
#include "ifcgeom\IfcGeom.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
void create_curve_rebar(IfcHierarchyHelper& file)
|
||||
{
|
||||
int dia = 24;
|
||||
int R = 3 * dia;
|
||||
int length = 12 * dia;
|
||||
|
||||
double crossSectionarea = M_PI * (dia / 2) * 2;
|
||||
IfcSchema::IfcReinforcingBar* rebar = new IfcSchema::IfcReinforcingBar(
|
||||
guid(), 0, S("test"), null,
|
||||
null, 0, 0,
|
||||
null, S("SR24"), //SteelGrade
|
||||
dia, //diameter
|
||||
crossSectionarea, //crossSectionarea = math.pi*(12.0/2)**2
|
||||
0,
|
||||
IfcSchema::IfcReinforcingBarRoleEnum::IfcReinforcingBarRoleEnum::IfcReinforcingBarRole_LIGATURE,
|
||||
IfcSchema::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurface_PLAIN //PLAIN or TEXTURED
|
||||
);
|
||||
|
||||
file.addBuildingProduct(rebar);
|
||||
rebar->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment::list::ptr segments(new IfcSchema::IfcCompositeCurveSegment::list());
|
||||
|
||||
IfcSchema::IfcCartesianPoint* p1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, 0, 1000.);
|
||||
IfcSchema::IfcCartesianPoint* p2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, 0, 0);
|
||||
IfcSchema::IfcCartesianPoint* p3 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R, 0);
|
||||
IfcSchema::IfcCartesianPoint* p4 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R, -R);
|
||||
IfcSchema::IfcCartesianPoint* p5 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R + length, -R);
|
||||
|
||||
/*first segment - line */
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points1(new IfcSchema::IfcCartesianPoint::list());
|
||||
points1->push(p1);
|
||||
points1->push(p2);
|
||||
file.addEntities(points1->generalize());
|
||||
IfcSchema::IfcPolyline* poly1 = new IfcSchema::IfcPolyline(points1);
|
||||
file.addEntity(poly1);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment1 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly1);
|
||||
file.addEntity(segment1);
|
||||
segments->push(segment1);
|
||||
|
||||
/*second segment - arc */
|
||||
IfcSchema::IfcAxis2Placement3D* axis1 = new IfcSchema::IfcAxis2Placement3D(p3, file.addTriplet<IfcSchema::IfcDirection>(1, 0, 0), file.addTriplet<IfcSchema::IfcDirection>(0, 1, 0));
|
||||
file.addEntity(axis1);
|
||||
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(axis1, R);
|
||||
file.addEntity(circle);
|
||||
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
|
||||
trim1->push(new IfcSchema::IfcParameterValue(180));
|
||||
trim1->push(p2);
|
||||
|
||||
trim2->push(new IfcSchema::IfcParameterValue(270));
|
||||
trim2->push(p4);
|
||||
IfcSchema::IfcTrimmedCurve* trimmed_curve = new IfcSchema::IfcTrimmedCurve(circle, trim1, trim2, false, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
file.addEntity(trimmed_curve);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment2 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, false, trimmed_curve);
|
||||
file.addEntity(segment2);
|
||||
segments->push(segment2);
|
||||
|
||||
/*third segment - line */
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points2(new IfcSchema::IfcCartesianPoint::list());
|
||||
points2->push(p4);
|
||||
points2->push(p5);
|
||||
file.addEntities(points2->generalize());
|
||||
IfcSchema::IfcPolyline* poly2 = new IfcSchema::IfcPolyline(points2);
|
||||
file.addEntity(poly2);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment3 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly2);
|
||||
file.addEntity(segment3);
|
||||
segments->push(segment3);
|
||||
|
||||
IfcSchema::IfcCompositeCurve* curve = new IfcSchema::IfcCompositeCurve(segments, false);
|
||||
file.addEntity(curve);
|
||||
|
||||
IfcSchema::IfcSweptDiskSolid* solid = new IfcSchema::IfcSweptDiskSolid(curve, dia / 2, null, 0, 1);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list());
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list());
|
||||
items->push(solid);
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("AdvancedSweptSolid"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
file.addEntity(shape);
|
||||
|
||||
rebar->setRepresentation(shape);
|
||||
|
||||
IfcSchema::IfcObjectPlacement* storey_placement = file.getSingle<IfcSchema::IfcBuildingStorey>()->ObjectPlacement();
|
||||
rebar->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 0, 0));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
IfcHierarchyHelper file;
|
||||
file.header().file_name().name("ifc_curve_rebar.ifc");
|
||||
create_curve_rebar(file);
|
||||
std::ofstream f("ifc_curve_rebar.ifc");
|
||||
f << file;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# ifc2ca
|
||||
Files and scripts for the use of [`Code_Aster`](https://code-aster.org) in IFC-driven FEM analyses
|
||||
|
||||
### File Organisation
|
||||
#### Scripts:
|
||||
- [`ifc2ca.py`](ifc2ca.py): a python script to extract and create a `json` file from an `ifc` file
|
||||
- [`scriptSalome.py`](scriptSalome.py): a python script to run in the [`Salome-Meca`](https://www.code-aster.org/spip.php?article303) environment. Creates the geometry and the mesh of the structure
|
||||
- [`scriptCodeAster.py`](scriptCodeAster.py): a python script to create the input file (`.comm`) for Code_Aster
|
||||
|
||||
#### Analysis Models
|
||||
|
||||
A number of analysis models with all the relative input/output and script files are provided in [this repository](https://github.com/IfcOpenShell/analysis-models) within the IfcOpenShell Organization
|
||||
|
||||
---
|
||||
|
||||
### Current Status
|
||||
Read [Change Log](changelog.md)
|
||||
@@ -0,0 +1,504 @@
|
||||
import json
|
||||
import ifcopenshell
|
||||
import os
|
||||
|
||||
|
||||
class CA2IFC:
|
||||
def __init__(self, inputFilename, outputFilename):
|
||||
self.inputFilename = inputFilename
|
||||
self.outputFilename = outputFilename
|
||||
self.data = None
|
||||
self.f = None
|
||||
self.reps = {}
|
||||
self.origin = None
|
||||
self.xAxis = None
|
||||
self.yAxis = None
|
||||
self.zAxis = None
|
||||
|
||||
def convert(self):
|
||||
# load json file
|
||||
with open(self.inputFilename) as dataFile:
|
||||
self.data = json.load(dataFile)
|
||||
|
||||
# initiate ifc file
|
||||
self.f = ifcopenshell.file()
|
||||
|
||||
# create header
|
||||
self.create_header()
|
||||
|
||||
# create global axes
|
||||
globalAxes = self.create_global_axes()
|
||||
localPlacement = self.f.createIfcLocalPlacement(None, globalAxes)
|
||||
|
||||
# TODO: create units
|
||||
lengthUnit = self.f.createIfcSIUnit(None, "LENGTHUNIT", None, "METRE")
|
||||
unitAssignment = self.f.createIfcUnitAssignment((lengthUnit,))
|
||||
|
||||
# create owner history
|
||||
ownerHistory = self.create_owner_history()
|
||||
|
||||
# create representations and subrepresentations
|
||||
self.reps = self.create_reference_subrep(globalAxes)
|
||||
|
||||
# create project and model
|
||||
project = self.f.createIfcProject(
|
||||
self.guid(), ownerHistory, "A Project", None, None, None, None, (self.reps["model"],), unitAssignment
|
||||
)
|
||||
model = self.f.createIfcStructuralAnalysisModel(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
self.data["name"],
|
||||
None,
|
||||
None,
|
||||
"NOTDEFINED",
|
||||
globalAxes,
|
||||
None,
|
||||
None,
|
||||
localPlacement,
|
||||
)
|
||||
self.f.createIfcRelDeclares(self.guid(), ownerHistory, None, None, project, (model,))
|
||||
|
||||
# create materials
|
||||
ifcMaterials = [None for _ in range(len(self.data["db"]["materials"]))]
|
||||
for i, material in enumerate(self.data["db"]["materials"]):
|
||||
ifcMaterials[i] = self.create_material(material)
|
||||
|
||||
# create profiles
|
||||
ifcProfiles = [None for _ in range(len(self.data["db"]["profiles"]))]
|
||||
for i, profile in enumerate(self.data["db"]["profiles"]):
|
||||
ifcProfiles[i] = self.create_profile(profile)
|
||||
|
||||
# create material-profile sets
|
||||
mpSets = list(
|
||||
set([el["material"] + "-" + el["profile"] for el in self.data["elements"] if el["geometryType"] == "line"])
|
||||
)
|
||||
ifcMaterialProfileSets = [None for _ in range(len(mpSets))]
|
||||
for i, mpSet in enumerate(mpSets):
|
||||
materialIndex = [mat["ifcName"] for mat in self.data["db"]["materials"]].index(mpSet.split("-")[0])
|
||||
profileIndex = [prof["ifcName"] for prof in self.data["db"]["profiles"]].index(mpSet.split("-")[1])
|
||||
material = ifcMaterials[materialIndex]
|
||||
profile = ifcProfiles[profileIndex]
|
||||
matProf = self.f.createIfcMaterialProfile(
|
||||
self.data["db"]["materials"][materialIndex]["name"]
|
||||
+ " | "
|
||||
+ self.data["db"]["profiles"][profileIndex]["profileName"],
|
||||
None,
|
||||
material,
|
||||
profile,
|
||||
)
|
||||
ifcMaterialProfileSets[i] = self.f.createIfcMaterialProfileSet(None, None, (matProf,))
|
||||
|
||||
# create structural elements
|
||||
ifcElements = [None for _ in range(len(self.data["elements"]))]
|
||||
for i, el in enumerate(self.data["elements"]):
|
||||
# geometry - product definition shape
|
||||
prodDefShape = self.create_geometry(el)
|
||||
|
||||
if el["geometryType"] == "line":
|
||||
# z axis TODO: group by elements
|
||||
localZAxis = self.f.createIfcDirection(tuple(el["orientation"][2]))
|
||||
# element
|
||||
ifcElements[i] = self.f.createIfcStructuralCurveMember(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
el["name"],
|
||||
None,
|
||||
None,
|
||||
localPlacement,
|
||||
prodDefShape,
|
||||
el["predefinedType"],
|
||||
localZAxis,
|
||||
)
|
||||
|
||||
if el["geometryType"] == "surface":
|
||||
ifcElements[i] = self.f.createIfcStructuralSurfaceMember(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
el["name"],
|
||||
None,
|
||||
None,
|
||||
localPlacement,
|
||||
prodDefShape,
|
||||
el["predefinedType"],
|
||||
el["thickness"],
|
||||
)
|
||||
|
||||
# create structural point connections
|
||||
ifcConnections = [None for _ in range(len(self.data["connections"]))]
|
||||
for i, conn in enumerate(self.data["connections"]):
|
||||
# geometry - product definition shape
|
||||
prodDefShape = self.create_geometry(conn)
|
||||
|
||||
# boundary conditions
|
||||
if conn["appliedCondition"]:
|
||||
bc = self.create_applied_conditions(conn["appliedCondition"], conn["geometryType"])
|
||||
if conn["geometryType"] == "point":
|
||||
appliedCondition = self.f.createIfcBoundaryNodeCondition(
|
||||
None, bc["dx"], bc["dy"], bc["dz"], bc["drx"], bc["dry"], bc["drz"]
|
||||
)
|
||||
if conn["geometryType"] == "line":
|
||||
appliedCondition = self.f.createIfcBoundaryEdgeCondition(
|
||||
None, bc["dx"], bc["dy"], bc["dz"], bc["drx"], bc["dry"], bc["drz"]
|
||||
)
|
||||
if conn["geometryType"] == "surface":
|
||||
appliedCondition = self.f.createIfcBoundaryFaceCondition(None, bc["dx"], bc["dy"], bc["dz"])
|
||||
else:
|
||||
appliedCondition = None
|
||||
|
||||
if conn["geometryType"] == "point":
|
||||
# local axes
|
||||
localAxes = self.create_orientation(conn["orientation"])
|
||||
# connection
|
||||
ifcConnections[i] = self.f.createIfcStructuralPointConnection(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
conn["name"],
|
||||
None,
|
||||
None,
|
||||
localPlacement,
|
||||
prodDefShape,
|
||||
appliedCondition,
|
||||
localAxes,
|
||||
)
|
||||
|
||||
if conn["geometryType"] == "line":
|
||||
# z axis TODO: group by elements
|
||||
localZAxis = self.f.createIfcDirection(tuple(conn["orientation"][2]))
|
||||
# connection
|
||||
ifcConnections[i] = self.f.createIfcStructuralCurveConnection(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
conn["name"],
|
||||
None,
|
||||
None,
|
||||
localPlacement,
|
||||
prodDefShape,
|
||||
appliedCondition,
|
||||
localZAxis,
|
||||
)
|
||||
|
||||
if conn["geometryType"] == "surface":
|
||||
ifcConnections[i] = self.f.createIfcStructuralSurfaceConnection(
|
||||
self.guid(), ownerHistory, conn["name"], None, None, localPlacement, prodDefShape, appliedCondition
|
||||
)
|
||||
|
||||
# assign material-profile-sets
|
||||
for i, mpSet in enumerate(mpSets):
|
||||
groupOfElements = []
|
||||
for j, el in enumerate(self.data["elements"]):
|
||||
if el["geometryType"] == "line" and el["material"] + "-" + el["profile"] == mpSet:
|
||||
groupOfElements.append(ifcElements[j])
|
||||
|
||||
if groupOfElements:
|
||||
self.f.createIfcRelAssociatesMaterial(
|
||||
self.guid(), ownerHistory, None, None, tuple(groupOfElements), ifcMaterialProfileSets[i]
|
||||
)
|
||||
|
||||
# assign materials
|
||||
for i, mat in enumerate(self.data["db"]["materials"]):
|
||||
groupOfElements = []
|
||||
for j, el in enumerate(self.data["elements"]):
|
||||
if el["geometryType"] == "surface" and el["material"] == mat["ifcName"]:
|
||||
groupOfElements.append(ifcElements[j])
|
||||
if groupOfElements:
|
||||
self.f.createIfcRelAssociatesMaterial(
|
||||
self.guid(), ownerHistory, None, None, tuple(groupOfElements), ifcMaterials[i]
|
||||
)
|
||||
|
||||
# create connections with elements
|
||||
for i, el in enumerate(self.data["elements"]):
|
||||
for conn in el["connections"]:
|
||||
j = [c["ifcName"] for c in self.data["connections"]].index(conn["relatedConnection"])
|
||||
geometryType = self.data["connections"][j]["geometryType"]
|
||||
|
||||
if conn["appliedCondition"]:
|
||||
bc = self.create_applied_conditions(conn["appliedCondition"], geometryType)
|
||||
if geometryType == "point":
|
||||
appliedCondition = self.f.createIfcBoundaryNodeCondition(
|
||||
None, bc["dx"], bc["dy"], bc["dz"], bc["drx"], bc["dry"], bc["drz"]
|
||||
)
|
||||
if geometryType == "line":
|
||||
appliedCondition = self.f.createIfcBoundaryEdgeCondition(
|
||||
None, bc["dx"], bc["dy"], bc["dz"], bc["drx"], bc["dry"], bc["drz"]
|
||||
)
|
||||
if geometryType == "surface":
|
||||
appliedCondition = self.f.createIfcBoundaryFaceCondition(None, bc["dx"], bc["dy"], bc["dz"])
|
||||
else:
|
||||
appliedCondition = None
|
||||
|
||||
# local axes
|
||||
localAxes = self.create_orientation(conn["orientation"])
|
||||
|
||||
if geometryType == "point":
|
||||
if not conn["eccentricity"]:
|
||||
self.f.createIfcRelConnectsStructuralMember(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
None,
|
||||
None,
|
||||
ifcElements[i],
|
||||
ifcConnections[j],
|
||||
appliedCondition,
|
||||
None,
|
||||
None,
|
||||
localAxes,
|
||||
)
|
||||
else:
|
||||
pointOnElement = self.f.createIfcCartesianPoint(tuple(conn["eccentricity"]["pointOnElement"]))
|
||||
vector = conn["eccentricity"]["vector"]
|
||||
connPointEcc = self.f.createIfcConnectionPointEccentricity(
|
||||
pointOnElement, None, vector[0], vector[1], vector[2]
|
||||
)
|
||||
self.f.createIfcRelConnectsWithEccentricity(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
None,
|
||||
None,
|
||||
ifcElements[i],
|
||||
ifcConnections[j],
|
||||
appliedCondition,
|
||||
None,
|
||||
None,
|
||||
localAxes,
|
||||
connPointEcc,
|
||||
)
|
||||
|
||||
if geometryType in ["line", "surface"]:
|
||||
self.f.createIfcRelConnectsStructuralMember(
|
||||
self.guid(),
|
||||
ownerHistory,
|
||||
None,
|
||||
None,
|
||||
ifcElements[i],
|
||||
ifcConnections[j],
|
||||
appliedCondition,
|
||||
None,
|
||||
None,
|
||||
localAxes,
|
||||
)
|
||||
|
||||
# assign elements and connections to group
|
||||
self.f.createIfcRelAssignsToGroup(
|
||||
self.guid(), ownerHistory, None, None, tuple(ifcElements + ifcConnections), None, model
|
||||
)
|
||||
|
||||
# finalize ifc file
|
||||
self.f.write(self.outputFilename)
|
||||
|
||||
def guid(self):
|
||||
return ifcopenshell.guid.new()
|
||||
|
||||
def create_header(self):
|
||||
self.f.wrapped_data.header.file_name.name = os.path.basename(self.outputFilename)
|
||||
|
||||
def create_global_axes(self):
|
||||
self.xAxis = self.f.createIfcDirection((1.0, 0.0, 0.0))
|
||||
self.yAxis = self.f.createIfcDirection((0.0, 1.0, 0.0))
|
||||
self.zAxis = self.f.createIfcDirection((0.0, 0.0, 1.0))
|
||||
self.origin = self.f.createIfcCartesianPoint((0.0, 0.0, 0.0))
|
||||
axes = self.f.createIfcAxis2Placement3D(self.origin, self.zAxis, self.xAxis)
|
||||
|
||||
return axes
|
||||
|
||||
def create_orientation(self, orientation):
|
||||
xAxis = self.f.createIfcDirection(tuple(orientation[0]))
|
||||
zAxis = self.f.createIfcDirection(tuple(orientation[2]))
|
||||
axes = self.f.createIfcAxis2Placement3D(self.origin, zAxis, xAxis)
|
||||
|
||||
return axes
|
||||
|
||||
def create_owner_history(self):
|
||||
actor = self.f.createIfcActorRole("ENGINEER", None, None)
|
||||
person = self.f.createIfcPerson("Christovasilis", None, "Ioannis", None, None, None, (actor,))
|
||||
organization = self.f.createIfcOrganization(
|
||||
None,
|
||||
"IfcOpenShell",
|
||||
"IfcOpenShell, an open source (LGPL) software library that helps users and software developers to work with the IFC file format.",
|
||||
)
|
||||
p_o = self.f.createIfcPersonAndOrganization(person, organization)
|
||||
application = self.f.createIfcApplication(organization, "v0.0.x", "IFC2CA", "IFC2CA")
|
||||
ownerHistory = self.f.createIfcOwnerHistory(p_o, application, "READWRITE", None, None, p_o, application)
|
||||
|
||||
return ownerHistory
|
||||
|
||||
def create_reference_subrep(self, globalAxes):
|
||||
modelRep = self.f.createIfcGeometricRepresentationContext(None, "Model", 3, 1.0e-05, globalAxes, None)
|
||||
bodySubRep = self.f.createIfcGeometricRepresentationSubContext(
|
||||
"Body", "Model", None, None, None, None, modelRep, None, "MODEL_VIEW", None
|
||||
)
|
||||
refSubRep = self.f.createIfcGeometricRepresentationSubContext(
|
||||
"Reference", "Model", None, None, None, None, modelRep, None, "GRAPH_VIEW", None
|
||||
)
|
||||
|
||||
return {"model": modelRep, "body": bodySubRep, "reference": refSubRep}
|
||||
|
||||
def create_material(self, material):
|
||||
ifcMaterial = self.f.createIfcMaterial(material["name"], None, material["category"])
|
||||
|
||||
mechProps = []
|
||||
if "youngModulus" in material["mechProps"]:
|
||||
youngModulus = self.f.createIfcPropertySingleValue(
|
||||
"YoungModulus", None, self.f.createIfcModulusOfElasticityMeasure(material["mechProps"]["youngModulus"])
|
||||
)
|
||||
mechProps.append(youngModulus)
|
||||
if "shearModulus" in material["mechProps"]:
|
||||
shearModulus = self.f.createIfcPropertySingleValue(
|
||||
"ShearModulus", None, self.f.createIfcModulusOfElasticityMeasure(material["mechProps"]["shearModulus"])
|
||||
)
|
||||
mechProps.append(shearModulus)
|
||||
if "poissonRatio" in material["mechProps"]:
|
||||
poissonRatio = self.f.createIfcPropertySingleValue(
|
||||
"PoissonRatio", None, self.f.createIfcPositiveRatioMeasure(material["mechProps"]["poissonRatio"])
|
||||
)
|
||||
mechProps.append(poissonRatio)
|
||||
if mechProps:
|
||||
self.f.createIfcMaterialProperties(
|
||||
"Pset_MaterialMechanical", material["name"], tuple(mechProps), ifcMaterial
|
||||
)
|
||||
|
||||
commonProps = []
|
||||
if "massDensity" in material["commonProps"]:
|
||||
massDensity = self.f.createIfcPropertySingleValue(
|
||||
"MassDensity", None, self.f.createIfcMassDensityMeasure(material["commonProps"]["massDensity"])
|
||||
)
|
||||
commonProps.append(massDensity)
|
||||
if commonProps:
|
||||
self.f.createIfcMaterialProperties("Pset_MaterialCommon", material["name"], tuple(commonProps), ifcMaterial)
|
||||
|
||||
return ifcMaterial
|
||||
|
||||
def create_profile(self, profile):
|
||||
if profile["profileShape"] == "rectangular":
|
||||
ifcProfile = self.f.createIfcRectangleProfileDef(
|
||||
profile["profileType"], profile["profileName"], None, profile["xDim"], profile["yDim"]
|
||||
)
|
||||
|
||||
if profile["profileShape"] == "iSymmetrical":
|
||||
ifcProfile = self.f.createIfcIShapeProfileDef(
|
||||
profile["profileType"],
|
||||
profile["profileName"],
|
||||
None,
|
||||
profile["commonProps"]["overallWidth"],
|
||||
profile["commonProps"]["overallDepth"],
|
||||
profile["commonProps"]["webThickness"],
|
||||
profile["commonProps"]["flangeThickness"],
|
||||
profile["commonProps"]["filletRadius"],
|
||||
)
|
||||
|
||||
mechProps = []
|
||||
if "massPerLength" in profile["mechProps"]:
|
||||
massPerLength = self.f.createIfcPropertySingleValue(
|
||||
"MassPerLength", None, self.f.createIfcMassPerLengthMeasure(profile["mechProps"]["massPerLength"])
|
||||
)
|
||||
mechProps.append(massPerLength)
|
||||
if "crossSectionArea" in profile["mechProps"]:
|
||||
crossSectionArea = self.f.createIfcPropertySingleValue(
|
||||
"CrossSectionArea", None, self.f.createIfcAreaMeasure(profile["mechProps"]["crossSectionArea"])
|
||||
)
|
||||
mechProps.append(crossSectionArea)
|
||||
if "momentOfInertiaY" in profile["mechProps"]:
|
||||
momentOfInertiaY = self.f.createIfcPropertySingleValue(
|
||||
"MomentOfInertiaY",
|
||||
None,
|
||||
self.f.createIfcMomentOfInertiaMeasure(profile["mechProps"]["momentOfInertiaY"]),
|
||||
)
|
||||
mechProps.append(momentOfInertiaY)
|
||||
if "momentOfInertiaZ" in profile["mechProps"]:
|
||||
momentOfInertiaZ = self.f.createIfcPropertySingleValue(
|
||||
"MomentOfInertiaZ",
|
||||
None,
|
||||
self.f.createIfcMomentOfInertiaMeasure(profile["mechProps"]["momentOfInertiaZ"]),
|
||||
)
|
||||
mechProps.append(momentOfInertiaZ)
|
||||
if "torsionalConstantX" in profile["mechProps"]:
|
||||
torsionalConstantX = self.f.createIfcPropertySingleValue(
|
||||
"TorsionalConstantX",
|
||||
None,
|
||||
self.f.createIfcMomentOfInertiaMeasure(profile["mechProps"]["torsionalConstantX"]),
|
||||
)
|
||||
mechProps.append(torsionalConstantX)
|
||||
if mechProps:
|
||||
self.f.createIfcProfileProperties(
|
||||
"Pset_ProfileMechanical", profile["profileName"], tuple(mechProps), ifcProfile
|
||||
)
|
||||
|
||||
return ifcProfile
|
||||
|
||||
def create_geometry(self, object):
|
||||
if object["geometryType"] == "point":
|
||||
point = self.f.createIfcCartesianPoint(tuple(object["geometry"]))
|
||||
vertex = self.f.createIfcVertexPoint(point)
|
||||
vertexTopologyRep = self.f.createIfcTopologyRepresentation(
|
||||
self.reps["reference"], "Reference", "Vertex", (vertex,)
|
||||
)
|
||||
vertexProdDefShape = self.f.createIfcProductDefinitionShape(None, None, (vertexTopologyRep,))
|
||||
|
||||
return vertexProdDefShape
|
||||
|
||||
if object["geometryType"] == "line":
|
||||
startPoint = self.f.createIfcCartesianPoint(tuple(object["geometry"][0]))
|
||||
startVertex = self.f.createIfcVertexPoint(startPoint)
|
||||
endPoint = self.f.createIfcCartesianPoint(tuple(object["geometry"][1]))
|
||||
endVertex = self.f.createIfcVertexPoint(endPoint)
|
||||
edge = self.f.createIfcEdge(startVertex, endVertex)
|
||||
edgeTopologyRep = self.f.createIfcTopologyRepresentation(
|
||||
self.reps["reference"], "Reference", "Edge", (edge,)
|
||||
)
|
||||
edgeProdDefShape = self.f.createIfcProductDefinitionShape(None, None, (edgeTopologyRep,))
|
||||
|
||||
return edgeProdDefShape
|
||||
|
||||
if object["geometryType"] == "surface":
|
||||
verts = [None for _ in range(len(object["geometry"]))]
|
||||
for i, p in enumerate(object["geometry"]):
|
||||
point = self.f.createIfcCartesianPoint(tuple(p))
|
||||
verts[i] = self.f.createIfcVertexPoint(point)
|
||||
|
||||
orientedEdges = [None for _ in range(len(object["geometry"]))]
|
||||
for i, v in enumerate(verts):
|
||||
v2Index = (i + 1) if i < len(verts) - 1 else 0
|
||||
edge = self.f.createIfcEdge(v, verts[v2Index])
|
||||
orientedEdges[i] = self.f.createIfcOrientedEdge(None, None, edge, True)
|
||||
|
||||
edgeLoop = self.f.createIfcEdgeLoop(tuple(orientedEdges))
|
||||
localAxes = self.create_orientation(object["orientation"])
|
||||
plane = self.f.createIfcPlane(localAxes)
|
||||
faceBound = self.f.createIfcFaceBound(edgeLoop, True)
|
||||
face = self.f.createIfcFaceSurface((faceBound,), plane, True)
|
||||
faceTopologyRep = self.f.createIfcTopologyRepresentation(
|
||||
self.reps["reference"], "Reference", "Face", (face,)
|
||||
)
|
||||
faceProdDefShape = self.f.createIfcProductDefinitionShape(None, None, (faceTopologyRep,))
|
||||
|
||||
return faceProdDefShape
|
||||
|
||||
def create_applied_conditions(self, bc, geometryType):
|
||||
for dof in ["dx", "dy", "dz"]:
|
||||
if isinstance(bc[dof], bool):
|
||||
bc[dof] = self.f.createIfcBoolean(bc[dof])
|
||||
else:
|
||||
if geometryType == "point":
|
||||
bc[dof] = self.f.createIfcLinearStiffnessMeasure(bc[dof])
|
||||
if geometryType == "line":
|
||||
bc[dof] = self.f.createIfcModulusOfLinearSubgradeReactionMeasure(bc[dof])
|
||||
if geometryType == "surface":
|
||||
bc[dof] = self.f.createIfcModulusOfSubgradeReactionMeasure(bc[dof])
|
||||
|
||||
for dof in ["drx", "dry", "drz"]:
|
||||
if isinstance(bc[dof], bool):
|
||||
bc[dof] = self.f.createIfcBoolean(bc[dof])
|
||||
else:
|
||||
if geometryType == "point":
|
||||
bc[dof] = self.f.createIfcRotationalStiffnessMeasure(bc[dof])
|
||||
if geometryType == "line":
|
||||
bc[dof] = self.f.createIfcModulusOfRotationalSubgradeReactionMeasure(bc[dof])
|
||||
|
||||
return bc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
inputFilename = "structure_01.json"
|
||||
outputFilename = "structure_01.ifc"
|
||||
|
||||
ca2ifc = CA2IFC(inputFilename, outputFilename)
|
||||
ca2ifc.convert()
|
||||
@@ -0,0 +1,34 @@
|
||||
## A change log of the ifc2ca files
|
||||
|
||||
##### 14/10/20
|
||||
- Add curve connections betweeen elements and as supports according to the defined orientation - stiffness is not yet considered
|
||||
|
||||
##### 17/08/20
|
||||
- Add rigid links for point connection to elements
|
||||
|
||||
##### 14/07/20
|
||||
- Add internal springs for point connection to elements according to the defined orientation
|
||||
- Add external springs for point connections according to the defined orientation
|
||||
|
||||
##### 27/05/20
|
||||
- Add orientation for point, curve and surface geometries
|
||||
- Calculate final geometry and orientation based on object placement transformation
|
||||
- Add warnings in the json file to show any corrections considered while parsing the ifc file from ETABS with a model with rigid links
|
||||
- Reduce file size by adding material and profile db in the json file and referencing them in the element objects
|
||||
- Implement internal releases for curve-to-point connections with any arbitrary orientation
|
||||
|
||||
##### 22/03/20
|
||||
- Calculation of section properties for I symmetric profile if not provided (assuming fillet radius is zero)
|
||||
- Added geometry of planar structural surface members
|
||||
- Refactored the ifc2ca.py script
|
||||
- Added support for rigid links
|
||||
- Added advanced meshing script to correctly simulate connections and independent meshing of structural elements (no common nodes)
|
||||
|
||||
##### 26/02/20
|
||||
- Changed section to profile and added I profile
|
||||
- Modified material and profile schema with mechanical and common properties
|
||||
- Added geometry identification for point connection from representation
|
||||
- Added connections along with supports based on the number of elements a connection is applied to
|
||||
|
||||
##### 09/02/20
|
||||
- First implementation for straight structural curve elements and point connections
|
||||
@@ -0,0 +1,547 @@
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
import json
|
||||
import ifcopenshell
|
||||
import numpy as np
|
||||
|
||||
|
||||
class IFC2CA:
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.file = None
|
||||
self.result = {}
|
||||
self.warnings = []
|
||||
self.tol = 1e-06
|
||||
|
||||
def convert(self):
|
||||
self.file = ifcopenshell.open(self.filename)
|
||||
for model in self.file.by_type("IfcStructuralAnalysisModel"):
|
||||
elements = self.get_structural_items(model, item_type="IfcStructuralMember")
|
||||
connections = self.get_structural_items(model, item_type="IfcStructuralConnection")
|
||||
|
||||
materialdb = []
|
||||
materials = list(dict.fromkeys([e["material"] for e in elements]))
|
||||
for mat in [mat for mat in materials if mat]:
|
||||
id = int(mat.split("|")[1])
|
||||
material = self.get_material_properties(self.file.by_id(id))
|
||||
material["relatedElements"] = [
|
||||
e["ifcName"] for e in elements if "material" in e and e["material"] == mat
|
||||
]
|
||||
materialdb.append(material)
|
||||
|
||||
profiledb = []
|
||||
profiles = list(dict.fromkeys([e["profile"] for e in elements if "profile" in e]))
|
||||
for prof in [prof for prof in profiles if prof]:
|
||||
id = int(prof.split("|")[1])
|
||||
profile = self.get_profile_properties(self.file.by_id(id))
|
||||
profile["relatedElements"] = [e["ifcName"] for e in elements if "profile" in e and e["profile"] == prof]
|
||||
profiledb.append(profile)
|
||||
|
||||
self.result = {
|
||||
"ifcName": model.is_a() + "|" + str(model.id()),
|
||||
"name": model.Name,
|
||||
"id": model.GlobalId,
|
||||
"elements": elements,
|
||||
"connections": connections,
|
||||
"db": {"materials": materialdb, "profiles": profiledb},
|
||||
"warnings": self.warnings,
|
||||
}
|
||||
|
||||
print('Model "%s" converted' % model.Name)
|
||||
print("Number of elements: ", len(elements))
|
||||
print("Number of connections: ", len(connections))
|
||||
print("Number of materials: ", len(materialdb))
|
||||
print("Number of profiles: ", len(profiledb))
|
||||
print("")
|
||||
|
||||
break
|
||||
|
||||
def get_structural_items(self, model, item_type="IfcStructuralItem"):
|
||||
items = []
|
||||
for group in model.IsGroupedBy:
|
||||
for item in group.RelatedObjects:
|
||||
if not item.is_a(item_type):
|
||||
continue
|
||||
data = self.get_item_data(item)
|
||||
if data:
|
||||
items.append(data)
|
||||
return items
|
||||
|
||||
def get_item_data(self, item):
|
||||
transformation = self.get_transformation(item.ObjectPlacement)
|
||||
|
||||
if item.is_a("IfcStructuralCurveMember"):
|
||||
representation = self.get_representation(item, "Edge")
|
||||
material_profile = self.get_material_profile(item)
|
||||
if not representation:
|
||||
self.warnings.append(
|
||||
"No representation defined for %s. Member excluded" % (item.is_a() + "|" + str(item.id()))
|
||||
)
|
||||
return
|
||||
if not material_profile:
|
||||
self.warnings.append("No material defined for in %s" % (item.is_a() + "|" + str(item.id())))
|
||||
self.warnings.append("No profile defined for in %s" % (item.is_a() + "|" + str(item.id())))
|
||||
materialId = None
|
||||
profileId = None
|
||||
else:
|
||||
material = material_profile.Material
|
||||
materialId = material.is_a() + "|" + str(material.id())
|
||||
profile = material_profile.Profile
|
||||
profileId = profile.is_a() + "|" + str(profile.id())
|
||||
|
||||
geometry = self.get_geometry(representation)
|
||||
orientation = self.get_1D_orientation(geometry, item.Axis)
|
||||
connections = self.get_connection_data(item.ConnectedBy)
|
||||
for conn in connections:
|
||||
if not conn["orientation"]:
|
||||
conn["orientation"] = orientation
|
||||
# --> Correct pointOnElement for eccentricity connection for ETABS files
|
||||
length = np.linalg.norm(np.array(geometry[1]) - np.array(geometry[0]))
|
||||
for c in connections:
|
||||
if c["eccentricity"]:
|
||||
if np.linalg.norm(np.array(c["eccentricity"]["pointOnElement"])) > length + self.tol:
|
||||
print(np.linalg.norm(np.array(c["eccentricity"]["pointOnElement"])), ">", length)
|
||||
self.warnings.append("Eccentricity in %s corrected" % (item.is_a() + "|" + str(item.id())))
|
||||
c["eccentricity"]["pointOnElement"][0] = length
|
||||
# End <--
|
||||
if transformation:
|
||||
geometry = self.transform_vectors(geometry, transformation)
|
||||
orientation = self.transform_vectors(orientation, transformation, include_translation=False)
|
||||
for c in connections:
|
||||
c["orientation"] = self.transform_vectors(
|
||||
c["orientation"], transformation, include_translation=False
|
||||
)
|
||||
if c["eccentricity"]:
|
||||
c["eccentricity"]["vector"] = self.transform_vectors(
|
||||
c["eccentricity"]["vector"], transformation, include_translation=False
|
||||
)
|
||||
|
||||
return {
|
||||
"ifcName": item.is_a() + "|" + str(item.id()),
|
||||
"name": item.Name,
|
||||
"id": item.GlobalId,
|
||||
"geometryType": "line",
|
||||
"predefinedType": item.PredefinedType,
|
||||
"geometry": geometry,
|
||||
"orientation": orientation,
|
||||
"material": materialId,
|
||||
"profile": profileId,
|
||||
"connections": connections,
|
||||
}
|
||||
|
||||
elif item.is_a("IfcStructuralSurfaceMember"):
|
||||
representation = self.get_representation(item, "Face")
|
||||
material = self.get_material_profile(item)
|
||||
if not representation:
|
||||
self.warnings.append(
|
||||
"No representation defined for %s. Member excluded" % (item.is_a() + "|" + str(item.id()))
|
||||
)
|
||||
return
|
||||
if not material:
|
||||
self.warnings.append("No material defined for in %s" % (item.is_a() + "|" + str(item.id())))
|
||||
materialId = None
|
||||
else:
|
||||
materialId = material.is_a() + "|" + str(material.id())
|
||||
|
||||
geometry = self.get_geometry(representation)
|
||||
orientation = self.get_2D_orientation(representation)
|
||||
connections = self.get_connection_data(item.ConnectedBy)
|
||||
for conn in connections:
|
||||
if not conn["orientation"]:
|
||||
conn["orientation"] = orientation
|
||||
if transformation:
|
||||
geometry = self.transform_vectors(geometry, transformation)
|
||||
orientation = self.transform_vectors(orientation, transformation, include_translation=False)
|
||||
for c in connections:
|
||||
c["orientation"] = self.transform_vectors(
|
||||
c["orientation"], transformation, include_translation=False
|
||||
)
|
||||
|
||||
return {
|
||||
"ifcName": item.is_a() + "|" + str(item.id()),
|
||||
"name": item.Name,
|
||||
"id": item.GlobalId,
|
||||
"geometryType": "surface",
|
||||
"predefinedType": item.PredefinedType,
|
||||
"thickness": item.Thickness,
|
||||
"geometry": geometry,
|
||||
"orientation": orientation,
|
||||
"material": materialId,
|
||||
"connections": connections,
|
||||
}
|
||||
|
||||
elif item.is_a("IfcStructuralPointConnection"):
|
||||
representation = self.get_representation(item, "Vertex")
|
||||
if not representation:
|
||||
self.warnings.append(
|
||||
"No representation defined for %s. Connection excluded" % (item.is_a() + "|" + str(item.id()))
|
||||
)
|
||||
return
|
||||
|
||||
geometry = self.get_geometry(representation)
|
||||
orientation = self.get_0D_orientation(item.ConditionCoordinateSystem)
|
||||
if not orientation:
|
||||
orientation = np.eye(3).tolist()
|
||||
if transformation:
|
||||
geometry = self.transform_vectors(geometry, transformation)
|
||||
orientation = self.transform_vectors(orientation, transformation, include_translation=False)
|
||||
|
||||
return {
|
||||
"ifcName": item.is_a() + "|" + str(item.id()),
|
||||
"name": item.Name,
|
||||
"id": item.GlobalId,
|
||||
"geometryType": "point",
|
||||
"geometry": geometry,
|
||||
"orientation": orientation,
|
||||
"appliedCondition": self.get_connection_input(item, "point"),
|
||||
"relatedElements": [con.is_a() + "|" + str(con.id()) for con in item.ConnectsStructuralMembers],
|
||||
}
|
||||
|
||||
elif item.is_a("IfcStructuralCurveConnection"):
|
||||
representation = self.get_representation(item, "Edge")
|
||||
if not representation:
|
||||
self.warnings.append(
|
||||
"No representation defined for %s. Connection excluded" % (item.is_a() + "|" + str(item.id()))
|
||||
)
|
||||
return
|
||||
|
||||
geometry = self.get_geometry(representation)
|
||||
orientation = self.get_1D_orientation(geometry, item.Axis)
|
||||
if not orientation:
|
||||
orientation = np.eye(3).tolist()
|
||||
if transformation:
|
||||
geometry = self.transform_vectors(geometry, transformation)
|
||||
orientation = self.transform_vectors(orientation, transformation, include_translation=False)
|
||||
|
||||
return {
|
||||
"ifcName": item.is_a() + "|" + str(item.id()),
|
||||
"name": item.Name,
|
||||
"id": item.GlobalId,
|
||||
"geometryType": "line",
|
||||
"geometry": geometry,
|
||||
"orientation": orientation,
|
||||
"appliedCondition": self.get_connection_input(item, "line"),
|
||||
"relatedElements": [con.is_a() + "|" + str(con.id()) for con in item.ConnectsStructuralMembers],
|
||||
}
|
||||
|
||||
def get_transformation(self, placement):
|
||||
if not placement:
|
||||
return None
|
||||
if placement.is_a("IfcLocalPlacement"):
|
||||
if placement.PlacementRelTo:
|
||||
print("Warning! Object Placement with PlacementRelTo attribute is not supported and will be neglected")
|
||||
axes = placement.RelativePlacement
|
||||
location = np.array(self.get_coordinate(axes.Location))
|
||||
if axes.Axis and axes.RefDirection:
|
||||
xAxis = np.array(axes.RefDirection.DirectionRatios) # this can be not accurate (in the xz plane)
|
||||
zAxis = np.array(axes.Axis.DirectionRatios)
|
||||
zAxis /= np.linalg.norm(zAxis)
|
||||
yAxis = np.cross(zAxis, xAxis)
|
||||
yAxis /= np.linalg.norm(yAxis)
|
||||
xAxis = np.cross(yAxis, zAxis)
|
||||
xAxis /= np.linalg.norm(xAxis)
|
||||
else:
|
||||
if np.allclose(location, np.array([0.0, 0.0, 0.0])):
|
||||
return None
|
||||
xAxis = np.array([1.0, 0.0, 0.0])
|
||||
yAxis = np.array([0.0, 1.0, 0.0])
|
||||
zAxis = np.array([0.0, 0.0, 1.0])
|
||||
if (
|
||||
np.allclose(location, np.array([0.0, 0.0, 0.0]))
|
||||
and np.allclose(xAxis, np.array([1.0, 0.0, 0.0]))
|
||||
and np.allclose(yAxis, np.array([0.0, 1.0, 0.0]))
|
||||
and np.allclose(zAxis, np.array([0.0, 0.0, 1.0]))
|
||||
):
|
||||
return None
|
||||
return {"location": location, "rotationMatrix": np.array([xAxis, yAxis, zAxis]).transpose()}
|
||||
else:
|
||||
print(
|
||||
"Warning! Object Placement is of type %s, which is not supported. Default considered" % placement.is_a()
|
||||
)
|
||||
return None
|
||||
|
||||
def get_representation(self, element, rep_type):
|
||||
if not element.Representation:
|
||||
return None
|
||||
for representation in element.Representation.Representations:
|
||||
rep = self.get_specific_representation(representation, "Reference", rep_type)
|
||||
if rep:
|
||||
return rep
|
||||
else:
|
||||
# print('Trying without rep identifier')
|
||||
for representation in element.Representation.Representations:
|
||||
rep = self.get_specific_representation(representation, None, rep_type)
|
||||
if rep:
|
||||
return rep
|
||||
|
||||
def get_specific_representation(self, representation, rep_id, rep_type):
|
||||
if (
|
||||
representation.RepresentationIdentifier == rep_id or rep_id is None
|
||||
) and representation.RepresentationType == rep_type:
|
||||
return representation
|
||||
if representation.RepresentationType == "MappedRepresentation":
|
||||
return self.get_specific_representation(
|
||||
representation.Items[0].MappingSource.MappedRepresentation, rep_id, rep_type
|
||||
)
|
||||
|
||||
def get_geometry(self, representation):
|
||||
# Maybe IfcOpenShell can use create_shape here to simplify this, but
|
||||
# supposedly structural models are very simple anyway, so perhaps we
|
||||
# can do without it.
|
||||
item = representation.Items[0]
|
||||
if item.is_a("IfcEdge"):
|
||||
return [
|
||||
self.get_coordinate(item.EdgeStart.VertexGeometry),
|
||||
self.get_coordinate(item.EdgeEnd.VertexGeometry),
|
||||
]
|
||||
|
||||
elif item.is_a("IfcFaceSurface"):
|
||||
edges = item.Bounds[0].Bound.EdgeList
|
||||
coords = []
|
||||
for edge in edges:
|
||||
coords.append(self.get_coordinate(edge.EdgeElement.EdgeStart.VertexGeometry))
|
||||
return coords
|
||||
|
||||
elif item.is_a("IfcVertexPoint"):
|
||||
return self.get_coordinate(item.VertexGeometry)
|
||||
|
||||
def get_coordinate(self, point):
|
||||
if point.is_a("IfcCartesianPoint"):
|
||||
return list(point.Coordinates)
|
||||
|
||||
def get_0D_orientation(self, axes):
|
||||
if axes and axes.Axis and axes.RefDirection:
|
||||
xAxis = np.array(
|
||||
axes.RefDirection.DirectionRatios
|
||||
) # this can be not strictly perpendicular (in the xz plane)
|
||||
zAxis = np.array(axes.Axis.DirectionRatios)
|
||||
zAxis /= np.linalg.norm(zAxis)
|
||||
yAxis = np.cross(zAxis, xAxis)
|
||||
yAxis /= np.linalg.norm(yAxis)
|
||||
xAxis = np.cross(yAxis, zAxis)
|
||||
xAxis /= np.linalg.norm(xAxis)
|
||||
|
||||
return [xAxis.tolist(), yAxis.tolist(), zAxis.tolist()]
|
||||
else: # return None and copy the elements orientation
|
||||
return None
|
||||
|
||||
def get_1D_orientation(self, geometry, zAxis):
|
||||
xAxis = np.array(geometry[1]) - np.array(geometry[0])
|
||||
xAxis /= np.linalg.norm(xAxis)
|
||||
zAxis = np.array(zAxis.DirectionRatios) # this can be not strictly perpendicular (in the xz plane)
|
||||
yAxis = np.cross(zAxis, xAxis)
|
||||
yAxis /= np.linalg.norm(yAxis)
|
||||
zAxis = np.cross(xAxis, yAxis)
|
||||
zAxis /= np.linalg.norm(zAxis)
|
||||
|
||||
return [xAxis.tolist(), yAxis.tolist(), zAxis.tolist()]
|
||||
|
||||
def get_2D_orientation(self, representation):
|
||||
item = representation.Items[0]
|
||||
if item.is_a("IfcFaceSurface"):
|
||||
item.SameSense
|
||||
axes = item.FaceSurface.Position
|
||||
orientation = self.get_0D_orientation(axes)
|
||||
if not item.SameSense:
|
||||
orientation = [[-v for v in vec] for vec in orientation]
|
||||
return orientation
|
||||
|
||||
def transform_vectors(self, geometry, trsf, include_translation=True):
|
||||
if not any(isinstance(el, list) for el in geometry): # single point which contains no list
|
||||
geometry = [geometry]
|
||||
globalGeometry = []
|
||||
|
||||
for p in geometry:
|
||||
gp = trsf["rotationMatrix"].dot(np.array(p))
|
||||
if include_translation:
|
||||
gp += trsf["location"]
|
||||
globalGeometry.append(gp.tolist())
|
||||
|
||||
if len(globalGeometry) == 1: # single point
|
||||
globalGeometry = globalGeometry[0]
|
||||
|
||||
return globalGeometry
|
||||
|
||||
def get_material_profile(self, element):
|
||||
if not element.HasAssociations:
|
||||
return None
|
||||
for association in element.HasAssociations:
|
||||
if not association.is_a("IfcRelAssociatesMaterial"):
|
||||
continue
|
||||
material = association.RelatingMaterial
|
||||
if material.is_a("IfcMaterialProfileSet"):
|
||||
# For now, we only deal with a single profile
|
||||
return material.MaterialProfiles[0]
|
||||
if material.is_a("IfcMaterialProfileSetUsage"):
|
||||
return material.ForProfileSet.MaterialProfiles[0]
|
||||
if material.is_a("IfcMaterial"):
|
||||
return material
|
||||
|
||||
def get_material_properties(self, material):
|
||||
psets = material.HasProperties
|
||||
|
||||
if self.get_pset_properties(psets, "Pset_MaterialMechanical"):
|
||||
mechProps = self.get_pset_properties(psets, "Pset_MaterialMechanical")
|
||||
else:
|
||||
mechProps = self.get_pset_properties(psets, None)
|
||||
|
||||
if self.get_pset_properties(psets, "Pset_MaterialCommon"):
|
||||
commonProps = self.get_pset_properties(psets, "Pset_MaterialCommon")
|
||||
else:
|
||||
commonProps = self.get_pset_properties(psets, None)
|
||||
|
||||
return {
|
||||
"ifcName": material.is_a() + "|" + str(material.id()),
|
||||
"name": material.Name,
|
||||
"category": material.Category,
|
||||
"mechProps": mechProps,
|
||||
"commonProps": commonProps,
|
||||
}
|
||||
|
||||
def get_pset_property(self, psets, pset_name, prop_name):
|
||||
for pset in psets:
|
||||
if pset.Name == pset_name or pset_name is None:
|
||||
for prop in pset.Properties:
|
||||
if prop.Name == prop_name:
|
||||
return prop.NominalValue.wrappedValue
|
||||
|
||||
def get_pset_properties(self, psets, pset_name):
|
||||
for pset in psets:
|
||||
if pset.Name == pset_name or pset_name is None:
|
||||
d = {}
|
||||
for prop in pset.Properties:
|
||||
propName = prop.Name[0].lower() + prop.Name[1:]
|
||||
d[propName] = prop.NominalValue.wrappedValue
|
||||
return d
|
||||
|
||||
def get_profile_properties(self, profile):
|
||||
if profile.is_a("IfcRectangleProfileDef"):
|
||||
return {
|
||||
"ifcName": profile.is_a() + "|" + str(profile.id()),
|
||||
"profileName": profile.ProfileName,
|
||||
"profileType": profile.ProfileType,
|
||||
"profileShape": "rectangular",
|
||||
"xDim": profile.XDim,
|
||||
"yDim": profile.YDim,
|
||||
}
|
||||
|
||||
if profile.is_a("IfcIShapeProfileDef"):
|
||||
psets = profile.HasProperties
|
||||
|
||||
if self.get_pset_properties(psets, "Pset_ProfileMechanical"):
|
||||
mechProps = self.get_pset_properties(psets, "Pset_ProfileMechanical")
|
||||
else:
|
||||
mechProps = self.get_i_section_properties(profile, "iSymmetrical")
|
||||
|
||||
return {
|
||||
"ifcName": profile.is_a() + "|" + str(profile.id()),
|
||||
"profileName": profile.ProfileName,
|
||||
"profileType": profile.ProfileType,
|
||||
"profileShape": "iSymmetrical",
|
||||
"mechProps": mechProps,
|
||||
"commonProps": {
|
||||
"flangeThickness": profile.FlangeThickness,
|
||||
"webThickness": profile.WebThickness,
|
||||
"overallDepth": profile.OverallDepth,
|
||||
"overallWidth": profile.OverallWidth,
|
||||
"filletRadius": profile.FilletRadius,
|
||||
},
|
||||
}
|
||||
|
||||
def get_connection_data(self, itemList):
|
||||
return [
|
||||
{
|
||||
"ifcName": rel.is_a() + "|" + str(rel.id()),
|
||||
"id": rel.GlobalId,
|
||||
"relatingElement": rel.RelatingStructuralMember.is_a() + "|" + str(rel.RelatingStructuralMember.id()),
|
||||
"relatedConnection": rel.RelatedStructuralConnection.is_a()
|
||||
+ "|"
|
||||
+ str(rel.RelatedStructuralConnection.id()),
|
||||
"orientation": self.get_0D_orientation(rel.ConditionCoordinateSystem),
|
||||
"appliedCondition": self.get_connection_input(
|
||||
rel, self.get_geometry_type_from_connection(rel.RelatedStructuralConnection)
|
||||
),
|
||||
"eccentricity": None
|
||||
if not rel.is_a("IfcRelConnectsWithEccentricity")
|
||||
else {
|
||||
"vector": [
|
||||
0.0
|
||||
if not rel.ConnectionConstraint.EccentricityInX
|
||||
else rel.ConnectionConstraint.EccentricityInX,
|
||||
0.0
|
||||
if not rel.ConnectionConstraint.EccentricityInY
|
||||
else rel.ConnectionConstraint.EccentricityInY,
|
||||
0.0
|
||||
if not rel.ConnectionConstraint.EccentricityInZ
|
||||
else rel.ConnectionConstraint.EccentricityInZ,
|
||||
],
|
||||
"pointOnElement": self.get_coordinate(rel.ConnectionConstraint.PointOnRelatingElement),
|
||||
},
|
||||
}
|
||||
for rel in itemList
|
||||
]
|
||||
|
||||
def get_geometry_type_from_connection(self, connection):
|
||||
if connection.is_a("IfcStructuralPointConnection"):
|
||||
return "point"
|
||||
if connection.is_a("IfcStructuralCurveConnection"):
|
||||
return "line"
|
||||
if connection.is_a("IfcStructuralSurfaceConnection"):
|
||||
return "surface"
|
||||
|
||||
def get_connection_input(self, connection, geometryType):
|
||||
if connection.AppliedCondition:
|
||||
if geometryType == "point":
|
||||
return {
|
||||
"dx": connection.AppliedCondition.TranslationalStiffnessX.wrappedValue,
|
||||
"dy": connection.AppliedCondition.TranslationalStiffnessY.wrappedValue,
|
||||
"dz": connection.AppliedCondition.TranslationalStiffnessZ.wrappedValue,
|
||||
"drx": connection.AppliedCondition.RotationalStiffnessX.wrappedValue,
|
||||
"dry": connection.AppliedCondition.RotationalStiffnessY.wrappedValue,
|
||||
"drz": connection.AppliedCondition.RotationalStiffnessZ.wrappedValue,
|
||||
}
|
||||
|
||||
if geometryType == "line":
|
||||
return {
|
||||
"dx": connection.AppliedCondition.TranslationalStiffnessByLengthX.wrappedValue,
|
||||
"dy": connection.AppliedCondition.TranslationalStiffnessByLengthY.wrappedValue,
|
||||
"dz": connection.AppliedCondition.TranslationalStiffnessByLengthZ.wrappedValue,
|
||||
"drx": connection.AppliedCondition.RotationalStiffnessByLengthX.wrappedValue,
|
||||
"dry": connection.AppliedCondition.RotationalStiffnessByLengthY.wrappedValue,
|
||||
"drz": connection.AppliedCondition.RotationalStiffnessByLengthZ.wrappedValue,
|
||||
}
|
||||
|
||||
if geometryType == "surface":
|
||||
return {
|
||||
"dx": connection.AppliedCondition.TranslationalStiffnessByAreaX.wrappedValue,
|
||||
"dy": connection.AppliedCondition.TranslationalStiffnessByAreaY.wrappedValue,
|
||||
"dz": connection.AppliedCondition.TranslationalStiffnessByAreaZ.wrappedValue,
|
||||
}
|
||||
|
||||
return connection.AppliedCondition
|
||||
|
||||
def get_i_section_properties(self, profile, profileShape):
|
||||
if profileShape == "iSymmetrical":
|
||||
tf = profile.FlangeThickness
|
||||
tw = profile.WebThickness
|
||||
h = profile.OverallDepth
|
||||
b = profile.OverallWidth
|
||||
|
||||
A = b * h - (b - tw) * (h - 2 * tf)
|
||||
Iy = b * (h ** 3) / 12 - (b - tw) * ((h - 2 * tf) ** 3) / 12
|
||||
Iz = (2 * tf) * (b ** 3) / 12 + (h - 2 * tf) * (tw ** 3) / 12
|
||||
Jx = 1 / 3 * ((h - tf) * (tw ** 3) + 2 * b * (tf ** 3))
|
||||
|
||||
return {"crossSectionArea": A, "momentOfInertiaY": Iy, "momentOfInertiaZ": Iz, "torsionalConstantX": Jx}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fileNames = ["cantilever_01", "portal_01", "grid_of_beams", "slab_01", "structure_01"]
|
||||
files = fileNames
|
||||
|
||||
for fileName in files:
|
||||
BASE_PATH = "/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/ifcFiles/"
|
||||
ifc2ca = IFC2CA(BASE_PATH + fileName + ".ifc")
|
||||
ifc2ca.convert()
|
||||
with open(BASE_PATH + fileName + ".json", "w") as f:
|
||||
f.write(json.dumps(ifc2ca.result, indent=4))
|
||||
@@ -0,0 +1,903 @@
|
||||
import json
|
||||
import numpy as np
|
||||
import itertools
|
||||
|
||||
flatten = itertools.chain.from_iterable
|
||||
|
||||
|
||||
class COMMANDFILE:
|
||||
def __init__(self, dataFilename, asterFilename):
|
||||
self.dataFilename = dataFilename
|
||||
self.asterFilename = asterFilename
|
||||
self.create()
|
||||
|
||||
def getGroupName(self, name):
|
||||
info = name.split("|")
|
||||
sortName = "".join(c for c in info[0] if c.isupper())
|
||||
return str(sortName + "_" + info[1])
|
||||
|
||||
def create(self):
|
||||
|
||||
AccelOfGravity = 9.806 # m/sec^2
|
||||
|
||||
# Read data from input file
|
||||
with open(self.dataFilename) as dataFile:
|
||||
data = json.load(dataFile)
|
||||
|
||||
elements = data["elements"]
|
||||
connections = data["connections"]
|
||||
# --> Delete this reference data and repopulate it with the objects
|
||||
# while going through elements
|
||||
for conn in connections:
|
||||
conn["relatedElements"] = []
|
||||
self.calculateRestraints(conn)
|
||||
for el in elements:
|
||||
for rel in el["connections"]:
|
||||
conn = [c for c in connections if c["ifcName"] == rel["relatedConnection"]][0]
|
||||
rel["conn_string"] = None
|
||||
if conn["geometryType"] == "point":
|
||||
rel["conn_string"] = "_0DC_"
|
||||
rel["springGroupName"] = (
|
||||
self.getGroupName(rel["relatingElement"])
|
||||
+ "_1DS_"
|
||||
+ self.getGroupName(rel["relatedConnection"])
|
||||
)
|
||||
if conn["geometryType"] == "line":
|
||||
rel["conn_string"] = "_1DC_"
|
||||
rel["springGroupName"] = None
|
||||
if conn["geometryType"] == "surface":
|
||||
rel["conn_string"] = "_2DC_"
|
||||
rel["springGroupName"] = None
|
||||
|
||||
rel["groupName1"] = (
|
||||
self.getGroupName(rel["relatingElement"])
|
||||
+ rel["conn_string"]
|
||||
+ self.getGroupName(rel["relatedConnection"])
|
||||
)
|
||||
if rel["eccentricity"]:
|
||||
rel["groupName2"] = (
|
||||
self.getGroupName(rel["relatedConnection"])
|
||||
+ "_0DC_"
|
||||
+ self.getGroupName(rel["relatingElement"])
|
||||
)
|
||||
rel["index"] = len(conn["relatedElements"]) + 1
|
||||
rel["unifiedGroupName"] = self.getGroupName(rel["relatedConnection"]) + "_0DC_%g" % rel["index"]
|
||||
else:
|
||||
rel["groupName2"] = self.getGroupName(rel["relatedConnection"])
|
||||
self.calculateConstraints(rel)
|
||||
conn["relatedElements"].append(rel)
|
||||
# End <--
|
||||
|
||||
materials = data["db"]["materials"]
|
||||
profiles = data["db"]["profiles"]
|
||||
|
||||
edgeGroupNames = tuple([self.getGroupName(el["ifcName"]) for el in elements if el["geometryType"] == "line"])
|
||||
faceGroupNames = tuple([self.getGroupName(el["ifcName"]) for el in elements if el["geometryType"] == "surface"])
|
||||
point0DGroupNames = tuple(
|
||||
[self.getGroupName(el["ifcName"]) + "_0D" for el in connections if el["geometryType"] == "point"]
|
||||
)
|
||||
spring1DGroupNames = tuple(
|
||||
flatten(
|
||||
[[rel["springGroupName"] for rel in el["connections"] if rel["springGroupName"]] for el in elements]
|
||||
)
|
||||
)
|
||||
point1DGroupNames = tuple(
|
||||
[self.getGroupName(el["ifcName"]) + "_0D" for el in connections if el["geometryType"] == "line"]
|
||||
)
|
||||
|
||||
unifiedConnection = False
|
||||
rigidLinkGroupNames = []
|
||||
for conn in connections:
|
||||
conn["unifiedGroupNames"] = [
|
||||
rel["unifiedGroupName"] for rel in conn["relatedElements"] if rel["eccentricity"]
|
||||
]
|
||||
# if not conn['appliedCondition'] and len(conn['unifiedGroupNames']) == 1:
|
||||
# conn['appliedCondition'] = {
|
||||
# 'dx': True,
|
||||
# 'dy': True,
|
||||
# 'dz': True
|
||||
# }
|
||||
if len(conn["unifiedGroupNames"]) >= 1:
|
||||
conn["unifiedGroupNames"].insert(0, self.getGroupName(conn["ifcName"]))
|
||||
conn["unifiedGroupNames"] = tuple(conn["unifiedGroupNames"])
|
||||
unifiedConnection = True
|
||||
rigidLinkGroupNames.extend(
|
||||
[
|
||||
self.getGroupName(rel["relatingElement"]) + "_1DR_" + self.getGroupName(conn["ifcName"])
|
||||
for rel in conn["relatedElements"]
|
||||
if rel["eccentricity"]
|
||||
]
|
||||
)
|
||||
rigidLinkGroupNames = tuple(rigidLinkGroupNames)
|
||||
|
||||
# Define file to write command file for code_aster
|
||||
f = open(self.asterFilename, "w")
|
||||
|
||||
f.write("# Command file generated by IfcOpenShell/ifc2ca scripts\n")
|
||||
f.write("\n")
|
||||
|
||||
f.write("# Linear Static Analysis With Self-Weight\n")
|
||||
|
||||
f.write(
|
||||
"""
|
||||
# STEP: INITIALIZE STUDY
|
||||
DEBUT(
|
||||
PAR_LOT = 'NON'
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
# STEP: READ MED FILE
|
||||
mesh = LIRE_MAILLAGE(
|
||||
FORMAT = 'MED',
|
||||
UNITE = 20
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
# STEP: DEFINE MODEL
|
||||
model = AFFE_MODELE(
|
||||
MAILLAGE = mesh,
|
||||
AFFE = (
|
||||
_F(
|
||||
TOUT = 'OUI',
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = '3D'
|
||||
),"""
|
||||
)
|
||||
|
||||
if faceGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = 'DKT'
|
||||
),"""
|
||||
|
||||
context = {"groupNames": faceGroupNames}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if edgeGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = 'POU_D_E'
|
||||
),"""
|
||||
|
||||
context = {"groupNames": edgeGroupNames}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if point0DGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = 'DIS_TR'
|
||||
),"""
|
||||
|
||||
context = {"groupNames": tuple(flatten([point0DGroupNames, spring1DGroupNames]))}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if point1DGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = 'DIS_TR'
|
||||
),"""
|
||||
|
||||
context = {"groupNames": point1DGroupNames}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if rigidLinkGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = 'POU_D_E'
|
||||
),"""
|
||||
|
||||
context = {"groupNames": rigidLinkGroupNames}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
)
|
||||
)\n
|
||||
"""
|
||||
)
|
||||
|
||||
f.write("# STEP: DEFINE MATERIALS")
|
||||
|
||||
for i, material in enumerate(materials):
|
||||
template = """
|
||||
{matNameID} = DEFI_MATERIAU(
|
||||
ELAS = _F(
|
||||
E = {youngModulus},
|
||||
NU = {poissonRatio},
|
||||
RHO = {massDensity}
|
||||
)
|
||||
)
|
||||
"""
|
||||
if "poissonRatio" in material["mechProps"]:
|
||||
poissonRatio = material["mechProps"]["poissonRatio"]
|
||||
else:
|
||||
if "shearModulus" in material["mechProps"]:
|
||||
poissonRatio = (
|
||||
material["mechProps"]["youngModulus"] / 2.0 / material["mechProps"]["shearModulus"]
|
||||
) - 1
|
||||
else:
|
||||
poissonRatio = 0.0
|
||||
|
||||
context = {
|
||||
"matNameID": "mat" + "_%s" % i,
|
||||
"youngModulus": float(material["mechProps"]["youngModulus"]),
|
||||
"poissonRatio": float(poissonRatio),
|
||||
"massDensity": float(material["commonProps"]["massDensity"]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
material = AFFE_MATERIAU(
|
||||
MAILLAGE = mesh,
|
||||
AFFE = ("""
|
||||
)
|
||||
|
||||
for i, material in enumerate(materials):
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
MATER = {matNameID},
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupNames": tuple([self.getGroupName(rel) for rel in material["relatedElements"]]),
|
||||
"matNameID": "mat" + "_%s" % i,
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if rigidLinkGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
MATER = {matNameID},
|
||||
),"""
|
||||
|
||||
context = {"groupNames": rigidLinkGroupNames, "matNameID": "mat_0"}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
# STEP: DEFINE ELEMENTS
|
||||
element = AFFE_CARA_ELEM(
|
||||
MODELE = model,
|
||||
POUTRE = ("""
|
||||
)
|
||||
|
||||
for profile in profiles:
|
||||
if profile["profileShape"] == "rectangular" and profile["profileType"] == "AREA":
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
SECTION = 'RECTANGLE',
|
||||
CARA = ('HY', 'HZ'),
|
||||
VALE = {profileDimensions}
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupNames": tuple([self.getGroupName(rel) for rel in profile["relatedElements"]]),
|
||||
"profileDimensions": (profile["xDim"], profile["yDim"]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
elif profile["profileShape"] == "iSymmetrical" and profile["profileType"] == "AREA":
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
SECTION = 'GENERALE',
|
||||
CARA = ('A', 'IY', 'IZ', 'JX'),
|
||||
VALE = {profileProperties}
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupNames": tuple([self.getGroupName(rel) for rel in profile["relatedElements"]]),
|
||||
"profileProperties": (
|
||||
profile["mechProps"]["crossSectionArea"],
|
||||
profile["mechProps"]["momentOfInertiaY"],
|
||||
profile["mechProps"]["momentOfInertiaZ"],
|
||||
profile["mechProps"]["torsionalConstantX"],
|
||||
),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if rigidLinkGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
SECTION = 'RECTANGLE',
|
||||
CARA = ('HY', 'HZ'),
|
||||
VALE = {profileDimensions}
|
||||
),"""
|
||||
|
||||
context = {"groupNames": rigidLinkGroupNames, "profileDimensions": (1, 1)}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),
|
||||
COQUE = ("""
|
||||
)
|
||||
|
||||
for el in [el for el in elements if el["geometryType"] == "surface"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
EPAIS = {thickness},
|
||||
VECTEUR = {localAxisX}
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupName": self.getGroupName(el["ifcName"]),
|
||||
"thickness": el["thickness"],
|
||||
"localAxisX": tuple(el["orientation"][0]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
f.write(
|
||||
"""
|
||||
DISCRET = ("""
|
||||
)
|
||||
|
||||
for conn in [conn for conn in connections if conn["geometryType"] == "point"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'K_TR_D_N',
|
||||
VALE = {stiffnesses},
|
||||
REPERE = 'LOCAL'
|
||||
),"""
|
||||
|
||||
context = {"groupName": self.getGroupName(conn["ifcName"]) + "_0D", "stiffnesses": conn["stiffnesses"]}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for rel in conn["relatedElements"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'K_TR_D_L',
|
||||
VALE = {stiffnesses},
|
||||
REPERE = 'LOCAL'
|
||||
),"""
|
||||
|
||||
context = {"groupName": rel["springGroupName"], "stiffnesses": rel["stiffnesses"]}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for conn in [conn for conn in connections if conn["geometryType"] == "line"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'K_TR_D_N',
|
||||
VALE = {stiffnesses},
|
||||
REPERE = 'LOCAL'
|
||||
),"""
|
||||
|
||||
context = {"groupName": self.getGroupName(conn["ifcName"]) + "_0D", "stiffnesses": conn["stiffnesses"]}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
ORIENTATION = ("""
|
||||
)
|
||||
|
||||
for el in [el for el in elements if el["geometryType"] == "line"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'VECT_Y',
|
||||
VALE = {localAxisY}
|
||||
),"""
|
||||
|
||||
context = {"groupName": self.getGroupName(el["ifcName"]), "localAxisY": tuple(el["orientation"][1])}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for conn in [conn for conn in connections if conn["geometryType"] == "point"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'VECT_X_Y',
|
||||
VALE = {localAxesXY}
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupName": self.getGroupName(conn["ifcName"]) + "_0D",
|
||||
"localAxesXY": tuple(conn["orientation"][0] + conn["orientation"][1]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for rel in conn["relatedElements"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'VECT_X_Y',
|
||||
VALE = {localAxesXY}
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupName": rel["springGroupName"],
|
||||
"localAxesXY": tuple(rel["orientation"][0] + rel["orientation"][1]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for conn in [conn for conn in connections if conn["geometryType"] == "line"]:
|
||||
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'VECT_X_Y',
|
||||
VALE = {localAxesXY}
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupName": self.getGroupName(conn["ifcName"]) + "_0D",
|
||||
"localAxesXY": tuple(conn["orientation"][0] + conn["orientation"][1]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
)\n
|
||||
"""
|
||||
)
|
||||
|
||||
f.write("# STEP: DEFINE SUPPORTS AND CONSTRAINTS")
|
||||
|
||||
f.write(
|
||||
"""
|
||||
liaisons = AFFE_CHAR_MECA(
|
||||
MODELE = model,
|
||||
LIAISON_DDL = ("""
|
||||
)
|
||||
|
||||
for conn in [conn for conn in connections if conn["geometryType"] == "point"]:
|
||||
if conn["appliedCondition"]:
|
||||
for i in range(len(conn["liaisons"]["coeffs"])):
|
||||
template = """
|
||||
_F(
|
||||
GROUP_NO = {groupNames},
|
||||
DDL = {dofs},
|
||||
COEF_MULT = {coeffs},
|
||||
COEF_IMPO = 0.0
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupNames": conn["liaisons"]["groupNames"],
|
||||
"dofs": conn["liaisons"]["dofs"][i],
|
||||
"coeffs": conn["liaisons"]["coeffs"][i],
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for rel in conn["relatedElements"]:
|
||||
for i in range(len(rel["liaisons"]["coeffs"])):
|
||||
template = """
|
||||
_F(
|
||||
GROUP_NO = {groupNames},
|
||||
DDL = {dofs},
|
||||
COEF_MULT = {coeffs},
|
||||
COEF_IMPO = 0.0
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupNames": rel["liaisons"]["groupNames"],
|
||||
"dofs": rel["liaisons"]["dofs"][i],
|
||||
"coeffs": rel["liaisons"]["coeffs"][i],
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
LIAISON_GROUP = ("""
|
||||
)
|
||||
|
||||
for conn in [conn for conn in connections if conn["geometryType"] == "line"]:
|
||||
if conn["appliedCondition"]:
|
||||
for i in range(len(conn["liaisons"]["coeffs"])):
|
||||
template = """
|
||||
_F(
|
||||
GROUP_NO_1 = {groupName_1},
|
||||
GROUP_NO_2 = {groupName_1},
|
||||
DDL_1 = {dofs},
|
||||
DDL_2 = {dofs},
|
||||
COEF_MULT_1 = {coeffs},
|
||||
COEF_MULT_2 = (0.0, 0.0, 0.0),
|
||||
COEF_IMPO = 0.0
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupName_1": tuple([conn["liaisons"]["groupNames"][0]]),
|
||||
"dofs": conn["liaisons"]["dofs"][i],
|
||||
"coeffs": conn["liaisons"]["coeffs"][i],
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for rel in conn["relatedElements"]:
|
||||
for i in range(len(rel["liaisons"]["coeffs"])):
|
||||
template = """
|
||||
_F(
|
||||
GROUP_NO_1 = {groupName_1},
|
||||
GROUP_NO_2 = {groupName_2},
|
||||
DDL_1 = {dofs},
|
||||
DDL_2 = {dofs},
|
||||
COEF_MULT_1 = {coeffs_1},
|
||||
COEF_MULT_2 = {coeffs_2},
|
||||
COEF_IMPO = 0.0
|
||||
),"""
|
||||
|
||||
context = {
|
||||
"groupName_1": tuple([rel["liaisons"]["groupNames"][0]]),
|
||||
"groupName_2": tuple([rel["liaisons"]["groupNames"][3]]),
|
||||
"dofs": tuple(list(rel["liaisons"]["dofs"][i])[:3]),
|
||||
"coeffs_1": tuple(list(rel["liaisons"]["coeffs"][i])[:3]),
|
||||
"coeffs_2": tuple(list(rel["liaisons"]["coeffs"][i])[3:]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
|
||||
if unifiedConnection:
|
||||
f.write(
|
||||
"""
|
||||
LIAISON_UNIF = ("""
|
||||
)
|
||||
|
||||
for conn in [conn for conn in connections if len(conn["unifiedGroupNames"]) > 1]:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_NO = {groupNames},
|
||||
DDL = ('DX', 'DY', 'DZ', 'DRX', 'DRY', 'DRZ')
|
||||
),"""
|
||||
|
||||
context = {"groupNames": conn["unifiedGroupNames"]}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
|
||||
if rigidLinkGroupNames:
|
||||
f.write(
|
||||
"""
|
||||
LIAISON_SOLIDE = ("""
|
||||
)
|
||||
|
||||
for groupName in rigidLinkGroupNames:
|
||||
template = """
|
||||
_F(
|
||||
GROUP_MA = '{groupName}'
|
||||
),"""
|
||||
|
||||
context = {"groupName": groupName}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
),"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
template = """
|
||||
# STEP: DEFINE LOAD
|
||||
gravLoad = AFFE_CHAR_MECA(
|
||||
MODELE = model,
|
||||
PESANTEUR = _F(
|
||||
GRAVITE = {AccelOfGravity},
|
||||
DIRECTION = (0.0, 0.0, -1.0)
|
||||
)
|
||||
)
|
||||
"""
|
||||
context = {
|
||||
"AccelOfGravity": AccelOfGravity,
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
"""
|
||||
# STEP: RUN ANALYSIS
|
||||
res_Bld = MECA_STATIQUE(
|
||||
MODELE = model,
|
||||
CHAM_MATER = material,
|
||||
CARA_ELEM = element,
|
||||
EXCIT = (
|
||||
_F(
|
||||
CHARGE = liaisons
|
||||
),
|
||||
_F(
|
||||
CHARGE = gravLoad
|
||||
)
|
||||
)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
# f.write(
|
||||
# '''
|
||||
# # STEP: POST-PROCESSING
|
||||
# res_Bld = CALC_CHAMP(
|
||||
# reuse = res_Bld,
|
||||
# RESULTAT = res_Bld,
|
||||
# # CONTRAINTE = ('SIEF_ELNO', 'SIGM_ELNO', 'EFGE_ELNO',),
|
||||
# FORCE = ('REAC_NODA', 'FORC_NODA',)
|
||||
# )
|
||||
# '''
|
||||
# )
|
||||
#
|
||||
# template = \
|
||||
# '''
|
||||
# # STEP: MASS EXTRACTION FOR EACH ASSEMBLE
|
||||
# FaceMass = POST_ELEM(
|
||||
# TITRE = 'TotMass',
|
||||
# MODELE = model,
|
||||
# CARA_ELEM = element,
|
||||
# CHAM_MATER = material,
|
||||
# MASS_INER = _F(
|
||||
# GROUP_MA = {massList},
|
||||
# ),
|
||||
# )\n'''
|
||||
#
|
||||
# context = {
|
||||
# 'massList': massList,
|
||||
# }
|
||||
#
|
||||
# f.write(template.format(**context))
|
||||
#
|
||||
# f.write(
|
||||
# '''
|
||||
# IMPR_TABLE(
|
||||
# UNITE = 10,
|
||||
# TABLE = FaceMass,
|
||||
# SEPARATEUR = ',',
|
||||
# NOM_PARA = ('LIEU', 'MASSE', 'CDG_X', 'CDG_Y', 'CDG_Z'),
|
||||
# # FORMAT_R = '1PE15.6',
|
||||
# )
|
||||
# '''
|
||||
# )
|
||||
#
|
||||
# template = \
|
||||
# '''
|
||||
# # STEP: REACTION EXTRACTION AT THE BASE
|
||||
# Reacs = POST_RELEVE_T(
|
||||
# ACTION = _F(
|
||||
# INTITULE = 'sumReac',
|
||||
# GROUP_NO = {groupNames},
|
||||
# RESULTAT = res_Bld,
|
||||
# NOM_CHAM = 'REAC_NODA',
|
||||
# RESULTANTE = ('DX','DY','DZ',),
|
||||
# MOMENT = ('DRX','DRY','DRZ',),
|
||||
# POINT = (0,0,0,),
|
||||
# OPERATION = 'EXTRACTION'
|
||||
# )
|
||||
# )
|
||||
# '''
|
||||
#
|
||||
# context = {
|
||||
# 'groupNames': point0DGroupNames,
|
||||
# }
|
||||
#
|
||||
# f.write(template.format(**context))
|
||||
#
|
||||
# f.write(
|
||||
# '''
|
||||
# IMPR_TABLE(
|
||||
# UNITE = 10,
|
||||
# TABLE = Reacs,
|
||||
# SEPARATEUR = ',',
|
||||
# # NOM_PARA = ('INTITULE', 'RESU', 'NOM_CHAM', 'INST', 'DX','DY','DZ'),
|
||||
# FORMAT_R = '1PE12.3',
|
||||
# )
|
||||
# '''
|
||||
# )
|
||||
#
|
||||
f.write(
|
||||
"""
|
||||
# STEP: DEFORMED SHAPE EXTRACTION
|
||||
IMPR_RESU(
|
||||
FORMAT = 'MED',
|
||||
UNITE = 80,
|
||||
RESU = _F(
|
||||
RESULTAT = res_Bld,
|
||||
NOM_CHAM = ('DEPL',), # 'REAC_NODA', 'FORC_NODA',
|
||||
NOM_CHAM_MED = ('Bld_DISP',), # 'Bld_REAC', 'Bld_FORC'
|
||||
)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
f.write(
|
||||
"""
|
||||
# STEP: CONCLUDE STUDY
|
||||
FIN()
|
||||
"""
|
||||
)
|
||||
|
||||
f.close()
|
||||
|
||||
def calculateConstraints(self, rel):
|
||||
gr1 = rel["groupName1"]
|
||||
gr2 = rel["groupName2"]
|
||||
o = np.array(rel["orientation"]).transpose().tolist()
|
||||
liaisons = {"groupNames": (gr1, gr1, gr1, gr2, gr2, gr2), "coeffs": [], "dofs": []}
|
||||
stiffnesses = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
if not rel["appliedCondition"]:
|
||||
rel["appliedCondition"] = {"dx": True, "dy": True, "dz": True, "drx": True, "dry": True, "drz": True}
|
||||
if isinstance(rel["appliedCondition"]["dx"], bool) and rel["appliedCondition"]["dx"]:
|
||||
liaisons["coeffs"].append((o[0][0], o[1][0], o[2][0], -o[0][0], -o[1][0], -o[2][0]))
|
||||
liaisons["dofs"].append(("DX", "DY", "DZ", "DX", "DY", "DZ"))
|
||||
elif isinstance(rel["appliedCondition"]["dx"], float) and rel["appliedCondition"]["dx"] > 0:
|
||||
stiffnesses[0] = rel["appliedCondition"]["dx"]
|
||||
|
||||
if isinstance(rel["appliedCondition"]["dy"], bool) and rel["appliedCondition"]["dy"]:
|
||||
liaisons["coeffs"].append((o[0][1], o[1][1], o[2][1], -o[0][1], -o[1][1], -o[2][1]))
|
||||
liaisons["dofs"].append(("DX", "DY", "DZ", "DX", "DY", "DZ"))
|
||||
elif isinstance(rel["appliedCondition"]["dy"], float) and rel["appliedCondition"]["dy"] > 0:
|
||||
stiffnesses[1] = rel["appliedCondition"]["dy"]
|
||||
|
||||
if isinstance(rel["appliedCondition"]["dz"], bool) and rel["appliedCondition"]["dz"]:
|
||||
liaisons["coeffs"].append((o[0][2], o[1][2], o[2][2], -o[0][2], -o[1][2], -o[2][2]))
|
||||
liaisons["dofs"].append(("DX", "DY", "DZ", "DX", "DY", "DZ"))
|
||||
elif isinstance(rel["appliedCondition"]["dz"], float) and rel["appliedCondition"]["dz"] > 0:
|
||||
stiffnesses[2] = rel["appliedCondition"]["dz"]
|
||||
|
||||
if isinstance(rel["appliedCondition"]["drx"], bool) and rel["appliedCondition"]["drx"]:
|
||||
liaisons["coeffs"].append((o[0][0], o[1][0], o[2][0], -o[0][0], -o[1][0], -o[2][0]))
|
||||
liaisons["dofs"].append(("DRX", "DRY", "DRZ", "DRX", "DRY", "DRZ"))
|
||||
elif isinstance(rel["appliedCondition"]["drx"], float) and rel["appliedCondition"]["drx"] > 0:
|
||||
stiffnesses[3] = rel["appliedCondition"]["drx"]
|
||||
|
||||
if isinstance(rel["appliedCondition"]["dry"], bool) and rel["appliedCondition"]["dry"]:
|
||||
liaisons["coeffs"].append((o[0][1], o[1][1], o[2][1], -o[0][1], -o[1][1], -o[2][1]))
|
||||
liaisons["dofs"].append(("DRX", "DRY", "DRZ", "DRX", "DRY", "DRZ"))
|
||||
elif isinstance(rel["appliedCondition"]["dry"], float) and rel["appliedCondition"]["dry"] > 0:
|
||||
stiffnesses[4] = rel["appliedCondition"]["dry"]
|
||||
|
||||
if isinstance(rel["appliedCondition"]["drz"], bool) and rel["appliedCondition"]["drz"]:
|
||||
liaisons["coeffs"].append((o[0][2], o[1][2], o[2][2], -o[0][2], -o[1][2], -o[2][2]))
|
||||
liaisons["dofs"].append(("DRX", "DRY", "DRZ", "DRX", "DRY", "DRZ"))
|
||||
elif isinstance(rel["appliedCondition"]["drz"], float) and rel["appliedCondition"]["drz"] > 0:
|
||||
stiffnesses[5] = rel["appliedCondition"]["drz"]
|
||||
|
||||
rel["liaisons"] = liaisons
|
||||
rel["stiffnesses"] = tuple(stiffnesses)
|
||||
|
||||
def calculateRestraints(self, conn):
|
||||
group = self.getGroupName(conn["ifcName"])
|
||||
o = np.array(conn["orientation"]).transpose().tolist()
|
||||
liaisons = {"groupNames": (group, group, group), "coeffs": [], "dofs": []}
|
||||
stiffnesses = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
|
||||
if not conn["appliedCondition"]:
|
||||
conn["liaisons"] = liaisons
|
||||
conn["stiffnesses"] = tuple(stiffnesses)
|
||||
return
|
||||
|
||||
if isinstance(conn["appliedCondition"]["dx"], bool) and conn["appliedCondition"]["dx"]:
|
||||
liaisons["coeffs"].append((o[0][0], o[1][0], o[2][0]))
|
||||
liaisons["dofs"].append(("DX", "DY", "DZ"))
|
||||
elif isinstance(conn["appliedCondition"]["dx"], float) and conn["appliedCondition"]["dx"] > 0:
|
||||
stiffnesses[0] = conn["appliedCondition"]["dx"]
|
||||
|
||||
if isinstance(conn["appliedCondition"]["dy"], bool) and conn["appliedCondition"]["dy"]:
|
||||
liaisons["coeffs"].append((o[0][1], o[1][1], o[2][1]))
|
||||
liaisons["dofs"].append(("DX", "DY", "DZ"))
|
||||
elif isinstance(conn["appliedCondition"]["dy"], float) and conn["appliedCondition"]["dy"] > 0:
|
||||
stiffnesses[1] = conn["appliedCondition"]["dy"]
|
||||
|
||||
if isinstance(conn["appliedCondition"]["dz"], bool) and conn["appliedCondition"]["dz"]:
|
||||
liaisons["coeffs"].append((o[0][2], o[1][2], o[2][2]))
|
||||
liaisons["dofs"].append(("DX", "DY", "DZ"))
|
||||
elif isinstance(conn["appliedCondition"]["dz"], float) and conn["appliedCondition"]["dz"] > 0:
|
||||
stiffnesses[2] = conn["appliedCondition"]["dz"]
|
||||
|
||||
if isinstance(conn["appliedCondition"]["drx"], bool) and conn["appliedCondition"]["drx"]:
|
||||
liaisons["coeffs"].append((o[0][0], o[1][0], o[2][0]))
|
||||
liaisons["dofs"].append(("DRX", "DRY", "DRZ"))
|
||||
elif isinstance(conn["appliedCondition"]["drx"], float) and conn["appliedCondition"]["drx"] > 0:
|
||||
stiffnesses[3] = conn["appliedCondition"]["drx"]
|
||||
|
||||
if isinstance(conn["appliedCondition"]["dry"], bool) and conn["appliedCondition"]["dry"]:
|
||||
liaisons["coeffs"].append((o[0][1], o[1][1], o[2][1]))
|
||||
liaisons["dofs"].append(("DRX", "DRY", "DRZ"))
|
||||
elif isinstance(conn["appliedCondition"]["dry"], float) and conn["appliedCondition"]["dry"] > 0:
|
||||
stiffnesses[4] = conn["appliedCondition"]["dry"]
|
||||
|
||||
if isinstance(conn["appliedCondition"]["drz"], bool) and conn["appliedCondition"]["drz"]:
|
||||
liaisons["coeffs"].append((o[0][2], o[1][2], o[2][2]))
|
||||
liaisons["dofs"].append(("DRX", "DRY", "DRZ"))
|
||||
elif isinstance(conn["appliedCondition"]["drz"], float) and conn["appliedCondition"]["drz"] > 0:
|
||||
stiffnesses[5] = conn["appliedCondition"]["drz"]
|
||||
|
||||
conn["liaisons"] = liaisons
|
||||
conn["stiffnesses"] = tuple(stiffnesses)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fileNames = ["cantilever_01", "portal_01", "grid_of_beams", "slab_01", "structure_01"]
|
||||
files = fileNames
|
||||
|
||||
for fileName in files:
|
||||
BASE_PATH = "/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/"
|
||||
DATAFILENAME = BASE_PATH + fileName + "/" + fileName + ".json"
|
||||
ASTERFILENAME = BASE_PATH + fileName + "/" + fileName + ".comm"
|
||||
COMMANDFILE(DATAFILENAME, ASTERFILENAME)
|
||||
@@ -0,0 +1,467 @@
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import salome
|
||||
import salome_notebook
|
||||
import salome_version
|
||||
import numpy as np
|
||||
import itertools
|
||||
|
||||
flatten = itertools.chain.from_iterable
|
||||
|
||||
|
||||
class MODEL:
|
||||
def __init__(self, dataFilename, medFilename, meshSize):
|
||||
self.dataFilename = dataFilename
|
||||
self.medFilename = medFilename
|
||||
self.meshSize = meshSize
|
||||
self.tolLoc = 0
|
||||
self.mesh = None
|
||||
self.meshNodes = None
|
||||
self.create()
|
||||
|
||||
def getGroupName(self, name):
|
||||
info = name.split("|")
|
||||
sortName = "".join(c for c in info[0] if c.isupper())
|
||||
return str(sortName + "_" + info[1])
|
||||
|
||||
def makePoint(self, pl):
|
||||
"""Function to define a Point from
|
||||
a polyline (list of 1 point)"""
|
||||
|
||||
(x, y, z) = pl
|
||||
return self.geompy.MakeVertex(x, y, z)
|
||||
|
||||
def makeLine(self, pl):
|
||||
"""Function to define a Line from
|
||||
a polyline (list of 2 points)"""
|
||||
|
||||
(x, y, z) = pl[0]
|
||||
P1 = self.geompy.MakeVertex(x, y, z)
|
||||
(x, y, z) = pl[1]
|
||||
P2 = self.geompy.MakeVertex(x, y, z)
|
||||
|
||||
return self.geompy.MakeLineTwoPnt(P1, P2)
|
||||
|
||||
def makeFace(self, pl):
|
||||
"""Function to define a Face from
|
||||
a polyline (list of points)"""
|
||||
|
||||
pointList = [None for _ in range(len(pl))]
|
||||
for ip, (x, y, z) in enumerate(pl):
|
||||
pointList[ip] = self.geompy.MakeVertex(x, y, z)
|
||||
|
||||
LineList = [None for _ in range(len(pl))]
|
||||
for ip, P2 in enumerate(pointList):
|
||||
P1 = pointList[ip - 1]
|
||||
LineList[ip] = self.geompy.MakeLineTwoPnt(P1, P2)
|
||||
|
||||
return self.geompy.MakeFaceWires(LineList, 1)
|
||||
|
||||
def makeObject(self, geometry, geometryType):
|
||||
if geometryType == "point":
|
||||
return self.makePoint(geometry)
|
||||
if geometryType == "line":
|
||||
return self.makeLine(geometry)
|
||||
if geometryType == "surface":
|
||||
return self.makeFace(geometry)
|
||||
|
||||
def makePartition(self, objects, geometryType):
|
||||
if geometryType == "point":
|
||||
shapeType = "VERTEX"
|
||||
if geometryType == "line":
|
||||
shapeType = "EDGE"
|
||||
if geometryType == "surface":
|
||||
shapeType = "FACE"
|
||||
return self.geompy.MakePartition(objects, [], [], [], self.geompy.ShapeType[shapeType], 0, [], 1)
|
||||
|
||||
def getLinkGeometry(self, ecc, orientation, finalPoint):
|
||||
vector = np.array(orientation).transpose().dot(ecc["vector"])
|
||||
initialPoint = (np.array(finalPoint) - vector).tolist()
|
||||
return [initialPoint, finalPoint]
|
||||
|
||||
def length(self, geometry):
|
||||
return (
|
||||
(geometry[1][0] - geometry[0][0]) ** 2
|
||||
+ (geometry[1][1] - geometry[0][1]) ** 2
|
||||
+ (geometry[1][2] - geometry[0][2]) ** 2
|
||||
) ** 0.5
|
||||
|
||||
def create(self):
|
||||
# Read data from input file
|
||||
with open(self.dataFilename) as dataFile:
|
||||
data = json.load(dataFile)
|
||||
|
||||
elements = data["elements"]
|
||||
connections = data["connections"]
|
||||
# --> Delete this reference data and repopulate it with the objects
|
||||
# while going through elements
|
||||
for conn in connections:
|
||||
conn["relatedElements"] = []
|
||||
# End <--
|
||||
|
||||
meshSize = self.meshSize
|
||||
|
||||
dec = 7 # 4 decimals for length in mm
|
||||
tol = 10 ** (-dec - 3 + 1)
|
||||
|
||||
self.tolLoc = tol * 10 * 2
|
||||
tolLoc = self.tolLoc
|
||||
|
||||
NEW_SALOME = int(salome_version.getVersion()[0]) >= 9
|
||||
salome.salome_init()
|
||||
theStudy = salome.myStudy
|
||||
notebook = salome_notebook.NoteBook(theStudy)
|
||||
|
||||
###
|
||||
### GEOM component
|
||||
###
|
||||
import GEOM
|
||||
from salome.geom import geomBuilder
|
||||
import math
|
||||
import SALOMEDS
|
||||
|
||||
gg = salome.ImportComponentGUI("GEOM")
|
||||
if NEW_SALOME:
|
||||
geompy = geomBuilder.New()
|
||||
else:
|
||||
geompy = geomBuilder.New(theStudy)
|
||||
self.geompy = geompy
|
||||
|
||||
O = geompy.MakeVertex(0, 0, 0)
|
||||
OX = geompy.MakeVectorDXDYDZ(1, 0, 0)
|
||||
OY = geompy.MakeVectorDXDYDZ(0, 1, 0)
|
||||
OZ = geompy.MakeVectorDXDYDZ(0, 0, 1)
|
||||
geompy.addToStudy(O, "O")
|
||||
geompy.addToStudy(OX, "OX")
|
||||
geompy.addToStudy(OY, "OY")
|
||||
geompy.addToStudy(OZ, "OZ")
|
||||
|
||||
if len([e for e in elements if e["geometryType"] == "line"]) > 0:
|
||||
buildingShapeType = "EDGE"
|
||||
if len([e for e in elements if e["geometryType"] == "surface"]) > 0:
|
||||
buildingShapeType = "FACE"
|
||||
|
||||
### Define entities ###
|
||||
start_time = time.time()
|
||||
print("Defining Object Geometry")
|
||||
init_time = start_time
|
||||
|
||||
# Loop 1
|
||||
for el in elements:
|
||||
el["elemObj"] = self.makeObject(el["geometry"], el["geometryType"])
|
||||
|
||||
el["connObjs"] = [None for _ in el["connections"]]
|
||||
el["linkObjs"] = [None for _ in el["connections"]]
|
||||
el["linkPointObjs"] = [[None, None] for _ in el["connections"]]
|
||||
for j, rel in enumerate(el["connections"]):
|
||||
conn = [c for c in connections if c["ifcName"] == rel["relatedConnection"]][0]
|
||||
if rel["eccentricity"]:
|
||||
rel["index"] = len(conn["relatedElements"]) + 1
|
||||
conn["relatedElements"].append(rel)
|
||||
|
||||
if not rel["eccentricity"]:
|
||||
el["connObjs"][j] = self.makeObject(conn["geometry"], conn["geometryType"])
|
||||
else:
|
||||
if conn["geometryType"] == "point":
|
||||
geometry = self.getLinkGeometry(rel["eccentricity"], el["orientation"], conn["geometry"])
|
||||
el["connObjs"][j] = self.makeObject(geometry[0], conn["geometryType"])
|
||||
|
||||
el["linkPointObjs"][j][0] = self.geompy.MakeVertex(
|
||||
geometry[0][0], geometry[0][1], geometry[0][2]
|
||||
)
|
||||
el["linkPointObjs"][j][1] = self.geompy.MakeVertex(
|
||||
geometry[1][0], geometry[1][1], geometry[1][2]
|
||||
)
|
||||
el["linkObjs"][j] = self.geompy.MakeLineTwoPnt(
|
||||
el["linkPointObjs"][j][0], el["linkPointObjs"][j][1]
|
||||
)
|
||||
else:
|
||||
print("Eccentricity defined for a %s geometryType" % conn["geometryType"])
|
||||
|
||||
el["partObj"] = self.makePartition([el["elemObj"]] + el["connObjs"], el["geometryType"])
|
||||
|
||||
el["elemObj"] = geompy.GetInPlace(el["partObj"], el["elemObj"])
|
||||
for j, rel in enumerate(el["connections"]):
|
||||
el["connObjs"][j] = geompy.GetInPlace(el["partObj"], el["connObjs"][j])
|
||||
|
||||
for conn in connections:
|
||||
conn["connObj"] = self.makeObject(conn["geometry"], conn["geometryType"])
|
||||
|
||||
# Make assemble of Building Object
|
||||
bldObjs = []
|
||||
bldObjs.extend([el["partObj"] for el in elements])
|
||||
bldObjs.extend(flatten([[link for link in el["linkObjs"] if link] for el in elements]))
|
||||
bldObjs.extend([conn["connObj"] for conn in connections])
|
||||
|
||||
bldComp = geompy.MakeCompound(bldObjs)
|
||||
# bldComp = geompy.MakePartition(bldObjs, [], [], [], self.geompy.ShapeType[buildingShapeType], 0, [], 1)
|
||||
geompy.addToStudy(bldComp, "bldComp")
|
||||
|
||||
# Loop 2
|
||||
for el in elements:
|
||||
# geompy.addToStudy(el['partObj'], self.getGroupName(el['ifcName']))
|
||||
geompy.addToStudyInFather(el["partObj"], el["elemObj"], self.getGroupName(el["ifcName"]))
|
||||
for j, rel in enumerate(el["connections"]):
|
||||
conn = [c for c in connections if c["ifcName"] == rel["relatedConnection"]][0]
|
||||
rel["conn_string"] = None
|
||||
if conn["geometryType"] == "point":
|
||||
rel["conn_string"] = "_0DC_"
|
||||
if conn["geometryType"] == "line":
|
||||
rel["conn_string"] = "_1DC_"
|
||||
if conn["geometryType"] == "surface":
|
||||
rel["conn_string"] = "_2DC_"
|
||||
geompy.addToStudyInFather(
|
||||
el["partObj"],
|
||||
el["connObjs"][j],
|
||||
self.getGroupName(el["ifcName"]) + rel["conn_string"] + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
if rel["eccentricity"]:
|
||||
pass
|
||||
# geompy.addToStudy(el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DR_' + self.getGroupName(rel['relatedConnection']))
|
||||
# geompy.addToStudyInFather(el['linkObjs'][j], el['linkPointObjs'][j][0], self.getGroupName(rel['relatedConnection']) + '_0DC_' + self.getGroupName(el['ifcName']))
|
||||
# geompy.addToStudyInFather(el['linkObjs'][j], el['linkPointObjs'][j][0], self.getGroupName(rel['relatedConnection']) + '_0DC_%g' % rel['index'])
|
||||
|
||||
for conn in connections:
|
||||
# geompy.addToStudy(conn['connObj'], self.getGroupName(conn['ifcName']))
|
||||
geompy.addToStudyInFather(conn["connObj"], conn["connObj"], self.getGroupName(conn["ifcName"]))
|
||||
|
||||
elapsed_time = time.time() - init_time
|
||||
init_time += elapsed_time
|
||||
print("Building Geometry Defined in %g sec" % (elapsed_time))
|
||||
|
||||
if len([e for e in elements if e["geometryType"] == "line"]) > 0:
|
||||
buildingShapeType = "EDGE"
|
||||
if len([e for e in elements if e["geometryType"] == "surface"]) > 0:
|
||||
buildingShapeType = "FACE"
|
||||
|
||||
# Define and add groups for all curve and surface members
|
||||
if len([e for e in elements if e["geometryType"] == "line"]) > 0:
|
||||
# Make compound of requested group
|
||||
compoundTemp = geompy.MakeCompound([e["elemObj"] for e in elements if e["geometryType"] == "line"])
|
||||
# Define group object and add to study
|
||||
curveCompound = geompy.GetInPlace(bldComp, compoundTemp)
|
||||
geompy.addToStudyInFather(bldComp, curveCompound, "CurveMembers")
|
||||
|
||||
if len([e for e in elements if e["geometryType"] == "surface"]) > 0:
|
||||
# Make compound of requested group
|
||||
compoundTemp = geompy.MakeCompound([e["elemObj"] for e in elements if e["geometryType"] == "surface"])
|
||||
# Define group object and add to study
|
||||
surfaceCompound = geompy.GetInPlace(bldComp, compoundTemp)
|
||||
geompy.addToStudyInFather(bldComp, surfaceCompound, "SurfaceMembers")
|
||||
|
||||
# Loop 3
|
||||
for el in elements:
|
||||
# el['partObj'] = geompy.RestoreGivenSubShapes(bldComp, [el['partObj']], GEOM.FSM_GetInPlace, False, False)[0]
|
||||
geompy.addToStudyInFather(bldComp, el["elemObj"], self.getGroupName(el["ifcName"]))
|
||||
|
||||
for j, rel in enumerate(el["connections"]):
|
||||
geompy.addToStudyInFather(
|
||||
bldComp,
|
||||
el["connObjs"][j],
|
||||
self.getGroupName(el["ifcName"]) + rel["conn_string"] + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
if rel["eccentricity"]: # point geometry
|
||||
geompy.addToStudyInFather(
|
||||
bldComp,
|
||||
el["linkObjs"][j],
|
||||
self.getGroupName(el["ifcName"]) + "_1DR_" + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
geompy.addToStudyInFather(
|
||||
bldComp,
|
||||
el["linkPointObjs"][j][0],
|
||||
self.getGroupName(rel["relatedConnection"]) + "_0DC_" + self.getGroupName(el["ifcName"]),
|
||||
)
|
||||
geompy.addToStudyInFather(
|
||||
bldComp,
|
||||
el["linkPointObjs"][j][1],
|
||||
self.getGroupName(rel["relatedConnection"]) + "_0DC_%g" % rel["index"],
|
||||
)
|
||||
|
||||
for conn in connections:
|
||||
# conn['connObj'] = geompy.RestoreGivenSubShapes(bldComp, [conn['connObj']], GEOM.FSM_GetInPlace, False, False)[0]
|
||||
geompy.addToStudyInFather(bldComp, conn["connObj"], self.getGroupName(conn["ifcName"]))
|
||||
|
||||
elapsed_time = time.time() - init_time
|
||||
init_time += elapsed_time
|
||||
print("Building Geometry Groups Defined in %g sec" % (elapsed_time))
|
||||
|
||||
###
|
||||
### SMESH component
|
||||
###
|
||||
|
||||
import SMESH
|
||||
from salome.smesh import smeshBuilder
|
||||
|
||||
print("Defining Mesh Components")
|
||||
|
||||
if NEW_SALOME:
|
||||
smesh = smeshBuilder.New()
|
||||
else:
|
||||
smesh = smeshBuilder.New(theStudy)
|
||||
bldMesh = smesh.Mesh(bldComp)
|
||||
Regular_1D = bldMesh.Segment()
|
||||
Local_Length_1 = Regular_1D.LocalLength(meshSize, None, tolLoc)
|
||||
|
||||
if buildingShapeType == "FACE":
|
||||
NETGEN2D_ONLY = bldMesh.Triangle(algo=smeshBuilder.NETGEN_2D)
|
||||
NETGEN2D_Pars = NETGEN2D_ONLY.Parameters()
|
||||
NETGEN2D_Pars.SetMaxSize(meshSize)
|
||||
NETGEN2D_Pars.SetOptimize(1)
|
||||
NETGEN2D_Pars.SetFineness(2)
|
||||
NETGEN2D_Pars.SetMinSize(meshSize / 5.0)
|
||||
NETGEN2D_Pars.SetUseSurfaceCurvature(1)
|
||||
NETGEN2D_Pars.SetQuadAllowed(1)
|
||||
NETGEN2D_Pars.SetSecondOrder(0)
|
||||
NETGEN2D_Pars.SetFuseEdges(254)
|
||||
|
||||
isDone = bldMesh.Compute()
|
||||
|
||||
## Set names of Mesh objects
|
||||
smesh.SetName(Regular_1D.GetAlgorithm(), "Regular_1D")
|
||||
smesh.SetName(Local_Length_1, "Local_Length_1")
|
||||
|
||||
if buildingShapeType == "FACE":
|
||||
smesh.SetName(NETGEN2D_ONLY.GetAlgorithm(), "NETGEN2D_ONLY")
|
||||
smesh.SetName(NETGEN2D_Pars, "NETGEN2D_Pars")
|
||||
|
||||
smesh.SetName(bldMesh.GetMesh(), "bldMesh")
|
||||
|
||||
elapsed_time = time.time() - init_time
|
||||
init_time += elapsed_time
|
||||
print("Meshing Operations Completed in %g sec" % (elapsed_time))
|
||||
|
||||
# Define and add groups for all curve and surface members
|
||||
if len([e for e in elements if e["geometryType"] == "line"]) > 0:
|
||||
tempgroup = bldMesh.GroupOnGeom(curveCompound, "CurveMembers", SMESH.EDGE)
|
||||
smesh.SetName(tempgroup, "CurveMembers")
|
||||
|
||||
if len([e for e in elements if e["geometryType"] == "surface"]) > 0:
|
||||
tempgroup = bldMesh.GroupOnGeom(surfaceCompound, "SurfaceMembers", SMESH.FACE)
|
||||
smesh.SetName(tempgroup, "SurfaceMembers")
|
||||
|
||||
# Define groups in Mesh
|
||||
for el in elements:
|
||||
if el["geometryType"] == "line":
|
||||
shapeType = SMESH.EDGE
|
||||
if el["geometryType"] == "surface":
|
||||
shapeType = SMESH.FACE
|
||||
tempgroup = bldMesh.GroupOnGeom(el["elemObj"], self.getGroupName(el["ifcName"]), shapeType)
|
||||
smesh.SetName(tempgroup, self.getGroupName(el["ifcName"]))
|
||||
|
||||
for j, rel in enumerate(el["connections"]):
|
||||
tempgroup = bldMesh.GroupOnGeom(
|
||||
el["connObjs"][j],
|
||||
self.getGroupName(el["ifcName"]) + rel["conn_string"] + self.getGroupName(rel["relatedConnection"]),
|
||||
SMESH.NODE,
|
||||
)
|
||||
smesh.SetName(
|
||||
tempgroup,
|
||||
self.getGroupName(el["ifcName"]) + rel["conn_string"] + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
rel["node"] = (bldMesh.GetIDSource(tempgroup.GetNodeIDs(), SMESH.NODE)).GetIDs()[0]
|
||||
if rel["eccentricity"]:
|
||||
tempgroup = bldMesh.GroupOnGeom(
|
||||
el["linkObjs"][j],
|
||||
self.getGroupName(el["ifcName"]) + "_1DR_" + self.getGroupName(rel["relatedConnection"]),
|
||||
SMESH.EDGE,
|
||||
)
|
||||
smesh.SetName(
|
||||
tempgroup,
|
||||
self.getGroupName(el["ifcName"]) + "_1DR_" + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
|
||||
tempgroup = bldMesh.GroupOnGeom(
|
||||
el["linkPointObjs"][j][0],
|
||||
self.getGroupName(rel["relatedConnection"]) + "_0DC_" + self.getGroupName(el["ifcName"]),
|
||||
SMESH.NODE,
|
||||
)
|
||||
smesh.SetName(
|
||||
tempgroup,
|
||||
self.getGroupName(rel["relatedConnection"]) + "_0DC_" + self.getGroupName(el["ifcName"]),
|
||||
)
|
||||
rel["eccNode"] = (bldMesh.GetIDSource(tempgroup.GetNodeIDs(), SMESH.NODE)).GetIDs()[0]
|
||||
|
||||
tempgroup = bldMesh.GroupOnGeom(
|
||||
el["linkPointObjs"][j][1],
|
||||
self.getGroupName(rel["relatedConnection"])
|
||||
+ "_0DC_"
|
||||
+ self.getGroupName(rel["relatedConnection"]),
|
||||
SMESH.NODE,
|
||||
)
|
||||
smesh.SetName(tempgroup, self.getGroupName(rel["relatedConnection"]) + "_0DC_%g" % rel["index"])
|
||||
|
||||
for conn in connections:
|
||||
tempgroup = bldMesh.GroupOnGeom(conn["connObj"], self.getGroupName(conn["ifcName"]), SMESH.NODE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn["ifcName"]))
|
||||
nodesId = bldMesh.GetIDSource(tempgroup.GetNodeIDs(), SMESH.NODE)
|
||||
tempgroup = bldMesh.Add0DElementsToAllNodes(nodesId, self.getGroupName(conn["ifcName"]))
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn["ifcName"] + "_0D"))
|
||||
if conn["geometryType"] == "point":
|
||||
conn["node"] = nodesId.GetIDs()[0]
|
||||
if conn["geometryType"] == "line":
|
||||
tempgroup = bldMesh.GroupOnGeom(conn["connObj"], self.getGroupName(conn["ifcName"]), SMESH.EDGE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn["ifcName"]))
|
||||
if conn["geometryType"] == "surface":
|
||||
tempgroup = bldMesh.GroupOnGeom(conn["connObj"], self.getGroupName(conn["ifcName"]), SMESH.FACE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn["ifcName"]))
|
||||
|
||||
# create 1D SEG2 spring elements
|
||||
for el in elements:
|
||||
for j, rel in enumerate(el["connections"]):
|
||||
conn = [c for c in connections if c["ifcName"] == rel["relatedConnection"]][0]
|
||||
if conn["geometryType"] == "point":
|
||||
grpName = bldMesh.CreateEmptyGroup(
|
||||
SMESH.EDGE,
|
||||
self.getGroupName(el["ifcName"]) + "_1DS_" + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
smesh.SetName(
|
||||
grpName,
|
||||
self.getGroupName(el["ifcName"]) + "_1DS_" + self.getGroupName(rel["relatedConnection"]),
|
||||
)
|
||||
if not rel["eccentricity"]:
|
||||
conn = [conn for conn in connections if conn["ifcName"] == rel["relatedConnection"]][0]
|
||||
grpName.Add([bldMesh.AddEdge([conn["node"], rel["node"]])])
|
||||
else:
|
||||
grpName.Add([bldMesh.AddEdge([rel["eccNode"], rel["node"]])])
|
||||
|
||||
self.mesh = bldMesh
|
||||
self.meshNodes = bldMesh.GetNodesId()
|
||||
|
||||
elapsed_time = time.time() - init_time
|
||||
init_time += elapsed_time
|
||||
print("Mesh Groups Defined in %g sec" % (elapsed_time))
|
||||
|
||||
try:
|
||||
if NEW_SALOME:
|
||||
bldMesh.ExportMED(
|
||||
self.medFilename, auto_groups=0, minor=40, overwrite=1, meshPart=None, autoDimension=0
|
||||
)
|
||||
else:
|
||||
bldMesh.ExportMED(self.medFilename, 0, SMESH.MED_V2_2, 1, None, 0)
|
||||
except:
|
||||
print("ExportMED() failed. Invalid file name?")
|
||||
|
||||
if salome.sg.hasDesktop():
|
||||
if NEW_SALOME:
|
||||
salome.sg.updateObjBrowser()
|
||||
else:
|
||||
salome.sg.updateObjBrowser(1)
|
||||
|
||||
elapsed_time = init_time - start_time
|
||||
print("ALL Operations Completed in %g sec" % (elapsed_time))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fileNames = ["structure_01"]
|
||||
files = fileNames
|
||||
|
||||
meshSize = 0.1
|
||||
|
||||
for fileName in files:
|
||||
BASE_PATH = "/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/"
|
||||
DATAFILENAME = BASE_PATH + fileName + "/" + fileName + ".json"
|
||||
MEDFILENAME = BASE_PATH + fileName + "/" + fileName + ".med"
|
||||
model = MODEL(DATAFILENAME, MEDFILENAME, meshSize)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Dependency and build folders created by the build scripts
|
||||
/build/
|
||||
/dist/
|
||||
@@ -0,0 +1,122 @@
|
||||
# BIMTester
|
||||
|
||||
BIMTester lets you specify a set of exchange requirements, and automatically check whether or not BIM data, typically an
|
||||
IFC file, complies with the requirements. You can run it automatically from a server, integrate it to your own
|
||||
application, or use a GUI. It can generate reports in various formats including:
|
||||
|
||||
* HTML
|
||||
* JSON
|
||||
* XUnit
|
||||
* BCF
|
||||
* Zoom Smart View
|
||||
|
||||
Your exchange requirements are written in plain language which you can use to communicate to project teams and include
|
||||
in contracts. Multiple languages are supported. A series of exchange requirement templates are provided to get started,
|
||||
but you can, and are encouraged to, write your own tailored to your project. An example of a requirement looks like
|
||||
this:
|
||||
|
||||
```
|
||||
Feature: Project setup
|
||||
|
||||
In order to view the BIM data
|
||||
As any interested stakeholder
|
||||
We need an IFC file
|
||||
|
||||
Scenario: Receiving a file
|
||||
* IFC data must use the IFC4 schema
|
||||
```
|
||||
|
||||
Languages supported include (in alphabetical order):
|
||||
|
||||
* Dutch
|
||||
* English
|
||||
* French
|
||||
* German
|
||||
* Italian
|
||||
|
||||
If you are not a developer, we highly recommend simply installing an integrated version of BIMTester.
|
||||
|
||||
* If you use Blender, install the [BlenderBIM Add-on](https://blenderbim.org)
|
||||
* If you use FreeCAD, install the [FreeCAD BIMTester Workbench](https://github.com/bimtester/bimtesterfc)
|
||||
|
||||
If you are a developer, read on!
|
||||
|
||||
## Installation
|
||||
|
||||
The following packages are required for BIMTester to function:
|
||||
|
||||
* behave
|
||||
* pystache
|
||||
* ifcopenshell
|
||||
* PySide2 (optional: needed for GUI)
|
||||
|
||||
The repository does not contain translation files. You can generate them as shown.
|
||||
|
||||
```
|
||||
cd IfcOpenShell/src/ifcbimtester/bimtester/locale
|
||||
pybabel compile -d .
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
BIMTester has a command line application. Check it out:
|
||||
|
||||
```
|
||||
$ python cli.py -h
|
||||
usage: cli.py [-h] [-a ACTION] [--advanced-arguments ADVANCED_ARGUMENTS] [-c]
|
||||
-f FEATURE -i IFC [-p PATH] [-r REPORT] [--lang LANG]
|
||||
|
||||
Runs unit tests for BIM data
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-a ACTION, --action ACTION
|
||||
Action to perform, from run/purge
|
||||
--advanced-arguments ADVANCED_ARGUMENTS
|
||||
Specify arguments to Behave
|
||||
-c, --console Show results in the console
|
||||
-f FEATURE, --feature FEATURE
|
||||
Specify a feature file to test
|
||||
-i IFC, --ifc IFC Specify a ifc file
|
||||
-p PATH, --path PATH Define a path for use in tests
|
||||
-r REPORT, --report REPORT
|
||||
Specify an output file for a HTML report
|
||||
--lang LANG Specify a language
|
||||
```
|
||||
|
||||
You can turn it into a regular command by symlinking it to your bin folder.
|
||||
|
||||
```
|
||||
$ ln -s /path/to/IfcOpenShell/src/ifcbimtester/cli.py /usr/local/bin/bimtester
|
||||
$ bimtester -h
|
||||
```
|
||||
|
||||
To run a test, we need an IFC to check and a feature file filled with requirements. The feature file is plaintext. Feel
|
||||
free to copy the minimal example above.
|
||||
|
||||
```
|
||||
# To see output in the console
|
||||
$ bimtester -i test.ifc -f test.feature -c
|
||||
# Or, if you want to generate a HTML report
|
||||
$ bimtester -i test.ifc -f test.feature -r report.html
|
||||
```
|
||||
|
||||
```
|
||||
python ./gui.py
|
||||
```
|
||||
|
||||
## Create a binary out of the Python package
|
||||
|
||||
TODO: Check if this works
|
||||
|
||||
Unix:
|
||||
|
||||
```
|
||||
$ pyinstaller --onefile --clean --icon=icon.ico --add-data "features:features" bimtester.py
|
||||
```
|
||||
|
||||
Windows:
|
||||
|
||||
```
|
||||
$ pyinstaller --onefile --clean --icon=icon.ico --add-data "features;features" bimtester.py
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
from os.path import dirname
|
||||
from os.path import realpath
|
||||
|
||||
|
||||
package_path = dirname(realpath(__file__))
|
||||
@@ -0,0 +1,47 @@
|
||||
import ifcopenshell
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestPurger:
|
||||
def __init__(self):
|
||||
self.file = None
|
||||
|
||||
def purge(self):
|
||||
filenames = []
|
||||
if os.path.exists("features"):
|
||||
for filename in Path("features/").glob("*.feature"):
|
||||
filenames.append(filename)
|
||||
for f in os.listdir("."):
|
||||
if f.endswith(".feature"):
|
||||
filenames.append(f)
|
||||
|
||||
for filename in filenames:
|
||||
with open(filename, "r") as feature_file:
|
||||
old_file = feature_file.readlines()
|
||||
with open(filename, "w") as new_file:
|
||||
for line in old_file:
|
||||
is_purged = False
|
||||
if 'The IFC file "' in line and '" must be provided' in line:
|
||||
filename = line.split('"')[1]
|
||||
print("Loading file {} ...".format(filename))
|
||||
self.file = ifcopenshell.open(filename)
|
||||
if line.strip()[0:2] == "* ":
|
||||
words = line.strip().split()
|
||||
for word in words:
|
||||
if self.is_a_global_id(word):
|
||||
if not self.does_global_id_exist(word):
|
||||
print("Test for {} purged ...".format(word))
|
||||
is_purged = True
|
||||
if not is_purged:
|
||||
new_file.write(line)
|
||||
|
||||
def is_a_global_id(self, word):
|
||||
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
|
||||
|
||||
def does_global_id_exist(self, global_id):
|
||||
try:
|
||||
self.file.by_guid(global_id)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
|
||||
from behave.model import Scenario
|
||||
from logfile import create_logfile
|
||||
from logfile import append_logfile
|
||||
from zoom_smart_view import append_zoom_smartview
|
||||
from zoom_smart_view import create_zoom_smartview
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import switch_locale
|
||||
|
||||
|
||||
this_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
def before_all(context):
|
||||
userdata = context.config.userdata
|
||||
|
||||
if context.config.lang:
|
||||
switch_locale(userdata.get("localedir"), context.config.lang)
|
||||
|
||||
continue_after_failed = userdata.getbool("runner.continue_after_failed_step", True)
|
||||
Scenario.continue_after_failed_step = continue_after_failed
|
||||
|
||||
# TODO: refactor smart view support into a decoupled module
|
||||
# context.ifc_path = userdata.get("ifc", "")
|
||||
# context.ifc_basename = os.path.basename(
|
||||
# os.path.splitext(context.ifc_path)[0]
|
||||
# )
|
||||
|
||||
# context.outpath = os.path.join(this_path, "..")
|
||||
|
||||
# context.thelogfile = os.path.join(context.outpath, context.ifc_basename + ".log")
|
||||
# create_logfile(
|
||||
# context.thelogfile,
|
||||
# context.ifc_basename,
|
||||
# )
|
||||
|
||||
# # set up smart view file
|
||||
# context.smview_file = os.path.join(context.outpath, context.ifc_basename + ".bcsv")
|
||||
# create_zoom_smartview(
|
||||
# context.smview_file,
|
||||
# context.ifc_basename,
|
||||
# )
|
||||
|
||||
|
||||
def after_step(context, step):
|
||||
pass
|
||||
# TODO: refactor smart view support into a decoupled module
|
||||
#if step.status == "failed":
|
||||
# # append log file
|
||||
# append_logfile(context, step)
|
||||
# # extend smart view
|
||||
# if hasattr(context, "falseguids"):
|
||||
# append_zoom_smartview(context.smview_file, step.name, context.falseguids)
|
||||
@@ -0,0 +1,30 @@
|
||||
import json
|
||||
|
||||
|
||||
def create_logfile(thelogfile, ifcbasename):
|
||||
|
||||
logfile = open(thelogfile, "w")
|
||||
logfile.write("BIMTester log file\n")
|
||||
logfile.write("------------------\n\n")
|
||||
logfile.write("ifc base file name: {}\n".format(ifcbasename))
|
||||
logfile.close()
|
||||
|
||||
|
||||
def append_logfile(thecontext, step):
|
||||
|
||||
# step attributes (also these set by user) scope is the scenario
|
||||
# https://behave.readthedocs.io/en/latest/thecontext_attributes.html
|
||||
print("Step '{}' failed".format(step.name))
|
||||
|
||||
# log file
|
||||
logfile = open(thecontext.thelogfile, "a")
|
||||
logfile.write("\n\nStep '{}' failed\n".format(step.name))
|
||||
if hasattr(thecontext, "falseelems"):
|
||||
logfile.write("{}\n".format(
|
||||
json.dumps(thecontext.falseelems, indent=4)
|
||||
))
|
||||
if hasattr(thecontext, "falseprops"):
|
||||
logfile.write("{}\n".format(
|
||||
json.dumps(thecontext.falseprops, indent=4)
|
||||
))
|
||||
logfile.close()
|
||||
@@ -0,0 +1,15 @@
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.classification import en
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.element_classes import en
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.geocoding import en
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.geolocation import en
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.geometric_detail import en
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.model_federation import en
|
||||
use_step_matcher("parse")
|
||||
from bimtester.features.steps.project_setup import de, en, fr, it, nl
|
||||
use_step_matcher("parse")
|
||||
@@ -0,0 +1,70 @@
|
||||
import json
|
||||
from behave import step
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
def get_classification(name):
|
||||
classifications = [c for c in IfcStore.file.by_type("IfcClassification") if c.Name == name]
|
||||
if len(classifications) != 1:
|
||||
assert False, f'The classification "{name}" was not found'
|
||||
return classifications[0]
|
||||
|
||||
|
||||
@step('The classification "{name}" must be used')
|
||||
def step_impl(context, name):
|
||||
get_classification(name)
|
||||
|
||||
|
||||
@step('The classification "{name}" is published by "{source}"')
|
||||
def step_impl(context, name, source):
|
||||
util.assert_attribute(get_classification(name), "Source", source)
|
||||
|
||||
|
||||
@step('The classification "{name}" is the edition "{edition}" on "{edition_date}"')
|
||||
def step_impl(context, name, edition, edition_date):
|
||||
element = get_classification(name)
|
||||
util.assert_attribute(element, "Edition", edition)
|
||||
util.assert_attribute(element, "EditionDate", edition_date)
|
||||
|
||||
|
||||
@step('The classification "{name}" has the description "{description}"')
|
||||
def step_impl(context, name, description):
|
||||
util.assert_attribute(get_classification(name), "Description", description)
|
||||
|
||||
|
||||
@step('The classification "{name}" is referenced by the website "{location}"')
|
||||
def step_impl(context, name, location):
|
||||
util.assert_attribute(get_classification(name), "Location", location)
|
||||
|
||||
|
||||
@step('The classification "{name}" has a hierarchy denoted by the tokens "{tokens}"')
|
||||
def step_impl(context, name, tokens):
|
||||
try:
|
||||
tokens = json.loads(tokens)
|
||||
except:
|
||||
assert False, _("Tokens {} are not specified as a JSON list").format(tokens)
|
||||
util.assert_attribute(get_classification(name), "ReferenceTokens", tokens)
|
||||
|
||||
|
||||
@step('The element "{guid}" is classified as a "{identification}" with name "{reference_name}"')
|
||||
def step_impl(context, guid, identification, reference_name):
|
||||
element = util.assert_guid(IfcStore.file, guid)
|
||||
if not hasattr(element, "HasAssociations") or not element.HasAssociations:
|
||||
assert False, _("The element {} has no associations.").format(element)
|
||||
references = [a.RelatingClassification for a in element.HasAssociations if a.is_a("IfcRelAssociatesClassification")]
|
||||
if not references:
|
||||
assert False, _("The element {element} has no associated classification references.").format(element)
|
||||
is_success = False
|
||||
for reference in references:
|
||||
try:
|
||||
util.assert_attribute(reference, "Identification", identification)
|
||||
util.assert_attribute(reference, "Name", reference_name)
|
||||
is_success = True
|
||||
except:
|
||||
pass
|
||||
if not is_success:
|
||||
assert False, _(
|
||||
"No classification references met the requirement for an identification {} and name {} for the element {}. The references we found were: {}"
|
||||
).format(identification, reference_name, element, references)
|
||||
@@ -0,0 +1,40 @@
|
||||
from behave import step
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
@step('The element "{guid}" is an "{ifc_class}" only')
|
||||
def step_impl(context, guid, ifc_class):
|
||||
element = util.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(element, ifc_class, is_exact=True)
|
||||
|
||||
|
||||
@step('The element "{guid}" is an "{ifc_class}"')
|
||||
def step_impl(context, guid, ifc_class):
|
||||
element = util.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(element, ifc_class)
|
||||
|
||||
|
||||
@step('The element "{guid}" is further defined as a "{predefined_type}"')
|
||||
def step_impl(context, guid, predefined_type):
|
||||
element = util.assert_guid(IfcStore.file, guid)
|
||||
if (
|
||||
hasattr(element, "PredefinedType")
|
||||
and element.PredefinedType == "USERDEFINED"
|
||||
and hasattr(element, "ObjectType")
|
||||
):
|
||||
util.assert_attribute(element, "ObjectType", predefined_type)
|
||||
elif hasattr(element, "PredefinedType"):
|
||||
util.assert_attribute(element, "PredefinedType", predefined_type)
|
||||
else:
|
||||
assert False, _("The element {} does not have a PredefinedType or ObjectType attribute").format(element)
|
||||
|
||||
|
||||
@step('The element "{guid}" should not exist because "{reason}"')
|
||||
def step_impl(context, guid, reason):
|
||||
try:
|
||||
element = IfcStore.file.by_id(guid)
|
||||
except:
|
||||
return
|
||||
assert False, _("This element {} should be reevaluated.").format(element)
|
||||
@@ -0,0 +1,83 @@
|
||||
from behave import step, use_step_matcher
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
def get_ifc_class_from_spatial_type(spatial_type):
|
||||
if spatial_type == "site":
|
||||
return "IfcSite"
|
||||
elif spatial_type == "building":
|
||||
return "IfcBuilding"
|
||||
return "IfcFacility"
|
||||
|
||||
|
||||
def check_geocode_attribute(guid, spatial_type, name, value):
|
||||
element = util.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(element, get_ifc_class_from_spatial_type(spatial_type))
|
||||
util.assert_attribute(element, name, value)
|
||||
|
||||
|
||||
def check_geocode_address(guid, spatial_type, name, value):
|
||||
element = util.assert_guid(IfcStore.file, guid)
|
||||
ifc_class = get_ifc_class_from_spatial_type(spatial_type)
|
||||
util.assert_type(element, ifc_class)
|
||||
if ifc_class == "IfcSite":
|
||||
address_name = "SiteAddress"
|
||||
elif ifc_class == "IfcBuilding":
|
||||
address_name = "BuildingAddress"
|
||||
util.assert_attribute(element, address_name)
|
||||
util.assert_attribute(getattr(element, address_name), name, value)
|
||||
|
||||
|
||||
use_step_matcher("re")
|
||||
|
||||
|
||||
@step('The (site|building|facility) "(?P<guid>.*)" has a name of "(?P<name>.*)"')
|
||||
def step_impl(context, spatial_type, guid, name):
|
||||
check_geocode_attribute(guid, spatial_type, "Name", name)
|
||||
|
||||
|
||||
@step('The (site|building|facility) "(?P<guid>.*)" has a description of "(?P<description>.*)"')
|
||||
def step_impl(context, spatial_type, guid, description):
|
||||
check_geocode_attribute(guid, spatial_type, "Description", description)
|
||||
|
||||
|
||||
@step('The site "(?P<guid>.*)" has a land title number of "(?P<land_title_number>.*)"')
|
||||
def step_impl(context, guid, land_title_number):
|
||||
check_geocode_attribute(guid, "site", "LandTitleNumber", land_title_number)
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" has the address "(?P<address_lines>.*)"')
|
||||
def step_impl(context, spatial_type, guid, address_lines):
|
||||
check_geocode_address(guid, spatial_type, "AddressLines", address_lines.split("\\n"))
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" has a postal box of "(?P<postal_box>.*)"')
|
||||
def step_impl(context, spatial_type, guid, postal_box):
|
||||
check_geocode_address(guid, spatial_type, "PostalBox", postal_box)
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" is in the town "(?P<town>.*)"')
|
||||
def step_impl(context, spatial_type, guid, town):
|
||||
check_geocode_address(guid, spatial_type, "Town", town)
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" is in the region "(?P<region>.*)"')
|
||||
def step_impl(context, spatial_type, guid, region):
|
||||
check_geocode_address(guid, spatial_type, "Region", region)
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" has a post code of "(?P<post_code>.*)"')
|
||||
def step_impl(context, spatial_type, guid, post_code):
|
||||
check_geocode_address(guid, spatial_type, "PostalCode", post_code)
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" is in the country "(?P<country>.*)"')
|
||||
def step_impl(context, spatial_type, guid, country):
|
||||
check_geocode_address(guid, spatial_type, "Country", country)
|
||||
|
||||
|
||||
@step('The (site|building) "(?P<guid>.*)" has an address description of "(?P<description>.*)"')
|
||||
def step_impl(context, spatial_type, guid, description):
|
||||
check_geocode_address(guid, spatial_type, "Description", description)
|
||||
@@ -0,0 +1,214 @@
|
||||
import math
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.geolocation
|
||||
from behave import step
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
@step(u'There must be at least one "{ifc_class}" element')
|
||||
def step_impl(context, ifc_class):
|
||||
assert len(IfcStore.file.by_type(ifc_class)) >= 1, _("An element of {} could not be found").format(ifc_class)
|
||||
|
||||
|
||||
def check_ifc4_geolocation(entity_name, prop_name=None, value=None, should_assert=True):
|
||||
if entity_name not in IfcStore.bookmarks:
|
||||
has_entity = False
|
||||
project = IfcStore.file.by_type("IfcProject")[0]
|
||||
for context in project.RepresentationContexts:
|
||||
if entity_name == "IfcMapConversion":
|
||||
if (
|
||||
context.is_a("IfcGeometricRepresentationContext")
|
||||
and context.ContextType == "Model"
|
||||
and context.HasCoordinateOperation
|
||||
):
|
||||
IfcStore.bookmarks[entity_name] = context.HasCoordinateOperation[0]
|
||||
has_entity = True
|
||||
elif entity_name == "IfcProjectedCRS":
|
||||
if (
|
||||
context.is_a("IfcGeometricRepresentationContext")
|
||||
and context.ContextType == "Model"
|
||||
and context.HasCoordinateOperation
|
||||
and context.HasCoordinateOperation[0].TargetCRS
|
||||
):
|
||||
IfcStore.bookmarks[entity_name] = context.HasCoordinateOperation[0].TargetCRS
|
||||
has_entity = True
|
||||
if not has_entity:
|
||||
assert False, _("No model geometric representation contexts refer to an {}").format(entity_name)
|
||||
if not prop_name:
|
||||
return
|
||||
actual_value = getattr(IfcStore.bookmarks[entity_name], prop_name)
|
||||
if should_assert:
|
||||
assert actual_value == value, _('We expected a value of "{}" but instead got "{}"').format(value, actual_value)
|
||||
else:
|
||||
return actual_value
|
||||
|
||||
|
||||
@step(u"The project must have coordinate reference system data")
|
||||
def step_impl(context):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS")
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS")
|
||||
|
||||
|
||||
@step(u'The name of the CRS must be "{coordinate_reference_name}"')
|
||||
def step_impl(context, coordinate_reference_name):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "Name", coordinate_reference_name)
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS", "Name", coordinate_reference_name)
|
||||
|
||||
|
||||
@step(u'The description of the CRS must be "{value}"')
|
||||
def step_impl(context, value):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "Description", value)
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS", "Description", value)
|
||||
|
||||
|
||||
@step(u'The geodetic datum must be "{coordinate_reference_name}"')
|
||||
def step_impl(context, coordinate_reference_name):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "GeodeticDatum", coordinate_reference_name)
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS", "GeodeticDatum", coordinate_reference_name)
|
||||
|
||||
|
||||
@step(u'The vertical datum must be "{coordinate_reference_name}"')
|
||||
def step_impl(context, coordinate_reference_name):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "VerticalDatum", coordinate_reference_name)
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS", "VerticalDatum", coordinate_reference_name)
|
||||
|
||||
|
||||
@step(u'The map projection must be "{coordinate_reference_name}"')
|
||||
def step_impl(context, coordinate_reference_name):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "MapProjection", coordinate_reference_name)
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS", "MapProjection", coordinate_reference_name)
|
||||
|
||||
|
||||
@step(u'The map zone must be "{coordinate_reference_name}"')
|
||||
def step_impl(context, coordinate_reference_name):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "MapZone", coordinate_reference_name)
|
||||
return
|
||||
check_ifc4_geolocation("IfcProjectedCRS", "MapZone", coordinate_reference_name)
|
||||
|
||||
|
||||
@step(u'The map unit must be "{unit}"')
|
||||
def step_impl(context, unit):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_ProjectedCRS", "MapUnit", unit)
|
||||
return
|
||||
actual_value = check_ifc4_geolocation("IfcProjectedCRS", "MapUnit", should_assert=False)
|
||||
if not actual_value:
|
||||
assert False, _("A unit was not provided in the projected CRS")
|
||||
if actual_value.is_a("IfcSIUnit"):
|
||||
prefix = actual_value.Prefix if actual_value.Prefix else ""
|
||||
actual_value = prefix + actual_value.Name
|
||||
elif actual_value.is_a("IfcConversionBasedUnit"):
|
||||
actual_value = actual_value.Name
|
||||
assert actual_value == unit, _('We expected a value of "{}" but instead got "{}"').format(unit, actual_value)
|
||||
|
||||
|
||||
@step(u"The project must have coordinate transformations to convert from local to global coordinates")
|
||||
def step_impl(context):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_MapConversion")
|
||||
check_ifc4_geolocation("IfcMapConversion")
|
||||
|
||||
|
||||
@step(u'The eastings of the model must be offset by "{number}" to derive its global coordinates')
|
||||
def step_impl(context, number):
|
||||
number = util.assert_number(number)
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_MapConversion", "Eastings", number)
|
||||
return
|
||||
check_ifc4_geolocation("IfcMapConversion", "Eastings", number)
|
||||
|
||||
|
||||
@step(u'The northings of the model must be offset by "{number}" to derive its global coordinates')
|
||||
def step_impl(context, number):
|
||||
number = util.assert_number(number)
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_MapConversion", "Northings", number)
|
||||
return
|
||||
check_ifc4_geolocation("IfcMapConversion", "Northings", number)
|
||||
|
||||
|
||||
@step(u'The height of the model must be offset by "{number}" to derive its global coordinates')
|
||||
def step_impl(context, number):
|
||||
number = util.assert_number(number)
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_MapConversion", "OrthogonalHeight", number)
|
||||
return
|
||||
check_ifc4_geolocation("IfcMapConversion", "OrthogonalHeight", number)
|
||||
|
||||
|
||||
@step(u'The model must be rotated clockwise by "{number}" to derive its global coordinates')
|
||||
def step_impl(context, number):
|
||||
number = util.assert_number(number)
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
return check_ifc2x3_geolocation("EPset_MapConversion", "Height", number)
|
||||
abscissa = check_ifc4_geolocation("IfcMapConversion", "XAxisAbscissa", should_assert=False)
|
||||
ordinate = check_ifc4_geolocation("IfcMapConversion", "XAxisOrdinate", should_assert=False)
|
||||
actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate), 3)
|
||||
value = round(number, 3)
|
||||
assert actual_value == value, _('We expected a value of "{}" but instead got "{}"').format(value, actual_value)
|
||||
|
||||
|
||||
@step(u'The model must be scaled along the horizontal axis by "{number}" to derive its global coordinates')
|
||||
def step_impl(context, number):
|
||||
number = util.assert_number(number)
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
for site in IfcStore.file.by_type("IfcSite"):
|
||||
util.assert_pset(site, "EPset_MapConversion", "Scale", number)
|
||||
return
|
||||
check_ifc4_geolocation("IfcMapConversion", "Scale", number)
|
||||
|
||||
|
||||
@step(u'The site "{guid}" has a longitude of "{number}"')
|
||||
def step_impl(context, guid, number):
|
||||
number = util.assert_number(number)
|
||||
site = util.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(site, "IfcSite")
|
||||
ref = util.assert_attribute(site, "RefLongitude")
|
||||
number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4))
|
||||
util.assert_attribute(site, "RefLongitude", number)
|
||||
|
||||
|
||||
@step(u'The site "{guid}" has a latitude of "{number}"')
|
||||
def step_impl(context, guid, number):
|
||||
number = util.assert_number(number)
|
||||
site = util.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(site, "IfcSite")
|
||||
ref = util.assert_attribute(site, "RefLatitude")
|
||||
number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4))
|
||||
util.assert_attribute(site, "RefLatitude", number)
|
||||
|
||||
|
||||
@step(u'The site "{guid}" has an elevation of "{number}"')
|
||||
def step_impl(context, guid, number):
|
||||
number = util.assert_number(number)
|
||||
site = util.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(site, "IfcSite")
|
||||
util.assert_attribute(site, "RefElevation", number)
|
||||
@@ -0,0 +1,29 @@
|
||||
from behave import step
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
@step('All elements must be under "{number}" polygons')
|
||||
def step_impl(context, number):
|
||||
number = int(number)
|
||||
errors = []
|
||||
for element in IfcStore.file.by_type("IfcElement"):
|
||||
if not element.Representation:
|
||||
continue
|
||||
total_polygons = 0
|
||||
tree = IfcStore.file.traverse(element.Representation)
|
||||
for e in tree:
|
||||
if e.is_a("IfcFace"):
|
||||
total_polygons += 1
|
||||
elif e.is_a("IfcPolygonalFaceSet"):
|
||||
total_polygons += len(e.Faces)
|
||||
elif e.is_a("IfcTriangulatedFaceSet"):
|
||||
total_polygons += len(e.CoordIndex)
|
||||
if total_polygons > number:
|
||||
errors.append((total_polygons, element))
|
||||
if errors:
|
||||
message = "The following {} elements are over 500 polygons:\n".format(len(errors))
|
||||
for error in errors:
|
||||
message += "Polygons: {} - {}\n".format(error[0], error[1])
|
||||
assert False, message
|
||||
@@ -0,0 +1,95 @@
|
||||
import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.placement
|
||||
from behave import step
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
def get_decimal_points(value):
|
||||
try:
|
||||
return len(value.split(".")[1])
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
def get_containing_spatial_elements(element):
|
||||
results = []
|
||||
if element.is_a("IfcSpatialElement"):
|
||||
results.append(element)
|
||||
for rel in element.Decomposes:
|
||||
if rel.is_a("IfcRelAggregates"):
|
||||
results.append(get_containing_spatial_elements(rel.RelatingObject))
|
||||
elif element.is_a("IfcElement"):
|
||||
for rel in element.ContainedInStructure:
|
||||
if rel.is_a("ifcRelContainedInSpatialStructure"):
|
||||
results.append(get_containing_spatial_elements(rel.RelatingStructure))
|
||||
return results
|
||||
|
||||
|
||||
@step('There is a datum element "{guid}" as an "{ifc_class}"')
|
||||
def step_impl(context, guid, ifc_class):
|
||||
element = utils.assert_guid(IfcStore.file, guid)
|
||||
util.assert_type(element, ifc_class)
|
||||
|
||||
|
||||
@step(
|
||||
'The element "{guid}" has a global easting, northing, and elevation of "{easting}", "{northing}", and "{elevation}" respectively'
|
||||
)
|
||||
def step_impl(context, guid, easting, northing, elevation):
|
||||
if IfcStore.file.schema == "IFC2X3":
|
||||
if element.is_a("IfcSite"):
|
||||
site = element
|
||||
else:
|
||||
potential_sites = [s for s in get_containing_spatial_elements(element) if s.is_a("IfcSite")]
|
||||
if potential_sites:
|
||||
site = potential_sites[0]
|
||||
else:
|
||||
assert False, _("The datum element does not belong to a geolocated site")
|
||||
map_conversion = assert_pset(site, "EPset_MapConversion")
|
||||
else:
|
||||
map_conversion = IfcStore.file.by_type("IfcMapConversion")
|
||||
if map_conversion:
|
||||
map_conversion = map_conversion[0].get_info()
|
||||
else:
|
||||
assert False, _("No map conversion was found in the file")
|
||||
|
||||
element = utils.assert_guid(IfcStore.file, guid)
|
||||
if not element.ObjectPlacement:
|
||||
assert False, _("The element does not have an object placement: {}").format(element)
|
||||
m = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
e, n, h = ifcopenshell.util.geolocation.xyz2enh(
|
||||
m[0][3],
|
||||
m[1][3],
|
||||
m[2][3],
|
||||
float(map_conversion["Eastings"]),
|
||||
float(map_conversion["Northings"]),
|
||||
float(map_conversion["OrthogonalHeight"]),
|
||||
float(map_conversion["XAxisAbscissa"]),
|
||||
float(map_conversion["XAxisOrdinate"]),
|
||||
float(map_conversion["Scale"]),
|
||||
)
|
||||
element_x = round(e, get_decimal_points(easting))
|
||||
element_y = round(n, get_decimal_points(northing))
|
||||
element_z = round(h, get_decimal_points(elevation))
|
||||
expected_placement = (util.assert_number(easting), util.assert_number(northing), util.assert_number(elevation))
|
||||
if (element_x, element_y, element_z) != expected_placement:
|
||||
assert False, _("The element {} is meant to have a location of {} but instead we found {}").format(
|
||||
element, expected_placement, (element_x, element_y, element_z)
|
||||
)
|
||||
|
||||
|
||||
@step('The element "{guid}" has a local X, Y, and Z coordinate of "{x}", "{y}", and "{z}" respectively')
|
||||
def step_impl(context, guid, x, y, z):
|
||||
element = utils.assert_guid(IfcStore.file, guid)
|
||||
if not element.ObjectPlacement:
|
||||
assert False, _("The element does not have an object placement: {}").format(element)
|
||||
m = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
element_x = round(m[0][3], get_decimal_points(x))
|
||||
element_y = round(m[1][3], get_decimal_points(y))
|
||||
element_z = round(m[2][3], get_decimal_points(z))
|
||||
expected_placement = (util.assert_number(x), util.assert_number(y), util.assert_number(z))
|
||||
if (element_x, element_y, element_z) != expected_placement:
|
||||
assert False, _("The element {} is meant to have a location of {} but instead we found {}").format(
|
||||
element, expected_placement, (element_x, element_y, element_z)
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
from behave import step
|
||||
|
||||
|
||||
@step('Die IFC-Daten müssen das "{schema}" Schema benutzen')
|
||||
def step_impl(context, schema):
|
||||
context.execute_steps(f'* IFC data must use the "{schema}" schema')
|
||||
|
||||
|
||||
@step('Die Globale Identifikationskennung (Globally Unique Identifier = GUID) des Projektes ist "{guid}"')
|
||||
def step_impl(context, guid):
|
||||
context.execute_steps(f'* The project must have an identifier of "{guid}"')
|
||||
|
||||
|
||||
@step('Der Name, die Abkürzung oder die Kurzkennung des Projektes ist "{value}"')
|
||||
def step_impl(context, value):
|
||||
context.execute_steps(f'* The project name, code, or short identifier must be "{value}"')
|
||||
@@ -0,0 +1,91 @@
|
||||
from behave import step
|
||||
from bimtester import util
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
@step('IFC data must use the "{schema}" schema')
|
||||
def step_impl(context, schema):
|
||||
real_schema = IfcStore.file.schema
|
||||
assert real_schema == schema, _("We expected a schema of {} but instead got {}").format(schema, real_schema)
|
||||
|
||||
|
||||
@step('The IFC file "{file}" is exempt from being provided')
|
||||
def step_impl(context, file):
|
||||
pass
|
||||
|
||||
|
||||
@step('No further requirements are specified because "{reason}"')
|
||||
def step_impl(context, reason):
|
||||
pass
|
||||
|
||||
|
||||
@step('The project must have an identifier of "{guid}"')
|
||||
def step_impl(context, guid):
|
||||
util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "GlobalId", guid)
|
||||
|
||||
|
||||
@step('The project name, code, or short identifier must be "{value}"')
|
||||
def step_impl(context, value):
|
||||
util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Name", value)
|
||||
|
||||
|
||||
@step('The project must have a longer form name of "{value}"')
|
||||
def step_impl(context, value):
|
||||
util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "LongName", value)
|
||||
|
||||
|
||||
@step('The project must be described as "{value}"')
|
||||
def step_impl(context, value):
|
||||
util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Description", value)
|
||||
|
||||
|
||||
@step('The project must be categorised under "{value}"')
|
||||
def step_impl(context, value):
|
||||
util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "ObjectType", value)
|
||||
|
||||
|
||||
@step('The project must contain information about the "{value}" phase')
|
||||
def step_impl(context, value):
|
||||
util.assert_attribute(IfcStore.file.by_type("IfcProject")[0], "Phase", value)
|
||||
|
||||
|
||||
@step("The project must contain 3D geometry representing the shape of objects")
|
||||
def step_impl(context):
|
||||
assert get_subcontext("Body", "Model", "MODEL_VIEW")
|
||||
|
||||
|
||||
@step("The project must contain 3D geometry representing clearance zones")
|
||||
def step_impl(context):
|
||||
assert get_subcontext("Clearance", "Model", "MODEL_VIEW")
|
||||
|
||||
|
||||
@step("The project must contain 3D geometry representing the center of gravity of objects")
|
||||
def step_impl(context):
|
||||
assert get_subcontext("CoG", "Model", "MODEL_VIEW")
|
||||
|
||||
|
||||
@step("The project must contain 3D geometry representing the object bounding boxes")
|
||||
def step_impl(context):
|
||||
assert get_subcontext("Box", "Model", "MODEL_VIEW")
|
||||
|
||||
|
||||
def get_subcontext(identifier, type, target_view):
|
||||
project = IfcStore.file.by_type("IfcProject")[0]
|
||||
for rep_context in project.RepresentationContexts:
|
||||
for subcontext in rep_context.HasSubContexts:
|
||||
if (
|
||||
subcontext.ContextIdentifier == identifier
|
||||
and subcontext.ContextType == type
|
||||
and subcontext.TargetView == target_view
|
||||
):
|
||||
return True
|
||||
assert False, "The subcontext with identifier {}, type {}, and target view {} could not be found".format(
|
||||
identifier, type, target_view
|
||||
)
|
||||
|
||||
|
||||
@step('the project has a {attribute_name} attribute with a value of "{attribute_value}"')
|
||||
def step_impl(context, attribute_name, attribute_value):
|
||||
project = IfcStore.file.by_type("IfcProject")[0]
|
||||
assert getattr(project, attribute_name) == attribute_value
|
||||
@@ -0,0 +1,6 @@
|
||||
from behave import step
|
||||
|
||||
|
||||
@step('Les données IFC doivent utiliser le schéma "{schema}"')
|
||||
def step_impl(context, schema):
|
||||
context.execute_steps(f'* IFC data must use the "{schema}" schema')
|
||||
@@ -0,0 +1,11 @@
|
||||
from behave import step
|
||||
|
||||
|
||||
@step('I dati IFC devono seguire lo schema "{schema}"')
|
||||
def step_impl(context, schema):
|
||||
context.execute_steps(f'* IFC data must use the "{schema}" schema')
|
||||
|
||||
|
||||
@step('Il nome del progetto, codice o identificatore breve deve essere "{value}"')
|
||||
def step_impl(context, value):
|
||||
context.execute_steps(f'* "The project name, code, or short identifier must be "{value}"')
|
||||
@@ -0,0 +1,11 @@
|
||||
from behave import step
|
||||
|
||||
|
||||
@step('IFC-gegevens moeten het "{schema}" -schema gebruiken')
|
||||
def step_impl(context, schema):
|
||||
context.execute_steps(f'* IFC data must use the "{schema}" schema')
|
||||
|
||||
|
||||
@step('De projectnaam, code of korte ID moet "{value}"')
|
||||
def step_impl(context, value):
|
||||
context.execute_steps(f'* "The project name, code, or short identifier must be "{value}"')
|
||||
@@ -0,0 +1,138 @@
|
||||
import fileinput
|
||||
|
||||
|
||||
def create_zoom_smartview(sm_file, ifcbasename):
|
||||
|
||||
smf = open(sm_file, "w")
|
||||
smf.write('<?xml version="1.0"?>\n')
|
||||
smf.write("<bimcollabsmartviewfile>\n")
|
||||
smf.write(" <version>5</version>\n")
|
||||
smf.write(" <applicationversion>Win - Version: ")
|
||||
# next line belongs to last, because of line length
|
||||
smf.write("3.4 (build 3.4.13.559)</applicationversion>\n")
|
||||
smf.write("</bimcollabsmartviewfile>\n")
|
||||
smf.write("\n")
|
||||
smf.write("<SMARTVIEWSETS>\n")
|
||||
smf.write(" <SMARTVIEWSET>\n")
|
||||
smf.write(" <TITLE>BIMTester {}</TITLE>\n".format(ifcbasename))
|
||||
smf.write(" <DESCRIPTION></DESCRIPTION>\n")
|
||||
smf.write(" <GUID>a2ddfaf7-97f2-4519-aabd-f2d94f6b4d6b</GUID>\n")
|
||||
smf.write(" <MODIFICATIONDATE>2020-10-30T13:23:30")
|
||||
# next line belongs to last, because of line length
|
||||
smf.write("</MODIFICATIONDATE>\n")
|
||||
smf.write(" <SMARTVIEWS>\n")
|
||||
smf.write(" </SMARTVIEWS>\n")
|
||||
smf.write(" </SMARTVIEWSET>\n")
|
||||
smf.write("</SMARTVIEWSETS>\n")
|
||||
smf.close()
|
||||
|
||||
|
||||
def append_zoom_smartview(sm_file, step_name, false_elements_guid):
|
||||
|
||||
# build the smartview string
|
||||
smview_string = " <SMARTVIEW>\n"
|
||||
smview_string += (
|
||||
" <TITLE>GUID filter, {}</TITLE>\n"
|
||||
.format(step_name)
|
||||
)
|
||||
smview_string += "{}\n".format(each_smartview_string_before)
|
||||
for guid in false_elements_guid:
|
||||
smview_string += (
|
||||
"{}{}{}\n".format(
|
||||
rule_string_before,
|
||||
guid,
|
||||
rule_string_after)
|
||||
)
|
||||
smview_string += "{}\n".format(each_smartview_string_after)
|
||||
|
||||
# insert smartview string into file
|
||||
theline = " </SMARTVIEWS>"
|
||||
newtext = smview_string + theline
|
||||
for line in fileinput.FileInput(sm_file, inplace=True):
|
||||
# the print replaces the line in the file
|
||||
# and add the line afterwards
|
||||
print(line.replace(theline, newtext), end="")
|
||||
|
||||
|
||||
each_smartview_string_title = """ <SMARTVIEW>
|
||||
<TITLE>Filter GUID</TITLE>
|
||||
<DESCRIPTION></DESCRIPTION>"""
|
||||
|
||||
|
||||
each_smartview_string_before = """ <CREATOR>bernd@bimstatik.ch</CREATOR>
|
||||
<CREATIONDATE>2020-10-30T13:18:45</CREATIONDATE>
|
||||
<MODIFIER>bernd@bimstatik.ch</MODIFIER>
|
||||
<MODIFICATIONDATE>2020-10-30T13:23:30</MODIFICATIONDATE>
|
||||
<GUID>15fda94f-b4bf-43be-8ef4-15d3121137e1</GUID>
|
||||
<RULES>
|
||||
<RULE>
|
||||
<IFCTYPE>Any</IFCTYPE>
|
||||
<PROPERTY>
|
||||
<NAME>None</NAME>
|
||||
<PROPERTYSETNAME>None</PROPERTYSETNAME>
|
||||
<TYPE>None</TYPE>
|
||||
<VALUETYPE>None</VALUETYPE>
|
||||
<UNIT>None</UNIT>
|
||||
</PROPERTY>
|
||||
<CONDITION>
|
||||
<TYPE>Is</TYPE>
|
||||
<VALUE></VALUE>
|
||||
</CONDITION>
|
||||
<ACTION>
|
||||
<TYPE>AddSetColored</TYPE>
|
||||
<R>187</R>
|
||||
<G>187</G>
|
||||
<B>187</B>
|
||||
</ACTION>
|
||||
</RULE>
|
||||
<RULE>
|
||||
<IFCTYPE>Any</IFCTYPE>
|
||||
<PROPERTY>
|
||||
<NAME>None</NAME>
|
||||
<PROPERTYSETNAME>None</PROPERTYSETNAME>
|
||||
<TYPE>None</TYPE>
|
||||
<VALUETYPE>None</VALUETYPE>
|
||||
<UNIT>None</UNIT>
|
||||
</PROPERTY>
|
||||
<CONDITION>
|
||||
<TYPE>Is</TYPE>
|
||||
<VALUE></VALUE>
|
||||
</CONDITION>
|
||||
<ACTION>
|
||||
<TYPE>SetTransparent</TYPE>
|
||||
</ACTION>
|
||||
</RULE>"""
|
||||
|
||||
|
||||
each_smartview_string_after = """ </RULES>
|
||||
<INFORMATIONTAKEOFF>
|
||||
<PROPERTYSETNAME>None</PROPERTYSETNAME>
|
||||
<PROPERTYNAME>None</PROPERTYNAME>
|
||||
<OPERATION>0</OPERATION>
|
||||
</INFORMATIONTAKEOFF>
|
||||
</SMARTVIEW>"""
|
||||
|
||||
|
||||
rule_string_before = """ <RULE>
|
||||
<IFCTYPE>Any</IFCTYPE>
|
||||
<PROPERTY>
|
||||
<NAME>GUID</NAME>
|
||||
<PROPERTYSETNAME>Summary</PROPERTYSETNAME>
|
||||
<TYPE>Summary</TYPE>
|
||||
<VALUETYPE>StringValue</VALUETYPE>
|
||||
<UNIT>None</UNIT>
|
||||
</PROPERTY>
|
||||
<CONDITION>
|
||||
<TYPE>Is</TYPE>
|
||||
<VALUE>"""
|
||||
|
||||
|
||||
rule_string_after = """</VALUE>
|
||||
</CONDITION>
|
||||
<ACTION>
|
||||
<TYPE>SetColored</TYPE>
|
||||
<R>255</R>
|
||||
<G>10</G>
|
||||
<B>10</B>
|
||||
</ACTION>
|
||||
</RULE>"""
|
||||
@@ -0,0 +1,153 @@
|
||||
import os
|
||||
import sys
|
||||
import bimtester.run
|
||||
from PySide2 import QtCore
|
||||
from PySide2 import QtGui
|
||||
from PySide2 import QtWidgets
|
||||
|
||||
|
||||
def run():
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
form = GuiWidgetBimTester()
|
||||
form.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
class GuiWidgetBimTester(QtWidgets.QWidget):
|
||||
def __init__(self, args=[]):
|
||||
super(GuiWidgetBimTester, self).__init__()
|
||||
self.args = args
|
||||
|
||||
self._setup_ui()
|
||||
|
||||
# http://forum.freecadweb.org/viewtopic.php?f=18&t=10732&start=10#p86493
|
||||
def __del__(self):
|
||||
return
|
||||
|
||||
def _setup_ui(self):
|
||||
package_path = os.path.dirname(os.path.realpath(__file__))
|
||||
iconpath = os.path.join(package_path, "resources", "icons", "bimtester.ico")
|
||||
|
||||
"""
|
||||
# as svg
|
||||
# https://stackoverflow.com/a/35138314
|
||||
theicon = QtSvg.QSvgWidget(iconpath)
|
||||
# none works ...
|
||||
#theicon.setGeometry(20,20,200,200)
|
||||
#theicon.setSizePolicy(
|
||||
# QtGui.QSizePolicy.Policy.Maximum,
|
||||
# QtGui.QSizePolicy.Policy.Maximum
|
||||
#)
|
||||
#theicon.sizeHint()
|
||||
"""
|
||||
|
||||
# as pixmap
|
||||
theicon = QtWidgets.QLabel(self)
|
||||
iconpixmap = QtGui.QPixmap(iconpath)
|
||||
iconpixmap = iconpixmap.scaled(100, 100, QtCore.Qt.KeepAspectRatio)
|
||||
theicon.setPixmap(iconpixmap)
|
||||
|
||||
# ifc file
|
||||
_ifcfile_label = QtWidgets.QLabel("IFC file", self)
|
||||
self.ifcfile_text = QtWidgets.QLineEdit()
|
||||
_ifcfile_browse_btn = QtWidgets.QToolButton()
|
||||
_ifcfile_browse_btn.setText("...")
|
||||
_ifcfile_browse_btn.clicked.connect(self.select_ifcfile)
|
||||
|
||||
# feature files path
|
||||
# use a layout with a frame and a title, see solver framework tp
|
||||
# beside button
|
||||
ffifc_str = "Feature files in a directory 'features' beside the IFC file."
|
||||
featuredirfromifc_label = QtWidgets.QLabel(ffifc_str, self)
|
||||
|
||||
# path browser and line edit
|
||||
_ffdir_str = "Feature files directory. " "'features' directory has to be in there."
|
||||
_featurefilesdir_label = QtWidgets.QLabel(_ffdir_str, self)
|
||||
self.featurefilesdir_text = QtWidgets.QLineEdit()
|
||||
self.feafilesdir_browse_btn = QtWidgets.QToolButton()
|
||||
self.feafilesdir_browse_btn.setText("...")
|
||||
self.feafilesdir_browse_btn.clicked.connect(self.select_featurefilesdir)
|
||||
|
||||
# buttons
|
||||
self.run_button = QtWidgets.QPushButton(QtGui.QIcon.fromTheme("document-new"), "Run")
|
||||
self.close_button = QtWidgets.QPushButton(QtGui.QIcon.fromTheme("window-close"), "Close")
|
||||
self.run_button.clicked.connect(self.run_bimtester)
|
||||
self.close_button.clicked.connect(self.close_widget)
|
||||
_buttons = QtWidgets.QHBoxLayout()
|
||||
_buttons.addWidget(self.run_button)
|
||||
_buttons.addWidget(self.close_button)
|
||||
|
||||
# Layout:
|
||||
layout = QtWidgets.QGridLayout()
|
||||
layout.addWidget(theicon, 1, 0, alignment=QtCore.Qt.AlignRight)
|
||||
|
||||
layout.addWidget(featuredirfromifc_label, 2, 0)
|
||||
|
||||
layout.addWidget(_featurefilesdir_label, 3, 0)
|
||||
layout.addWidget(self.featurefilesdir_text, 4, 0)
|
||||
layout.addWidget(self.feafilesdir_browse_btn, 4, 1)
|
||||
|
||||
layout.addWidget(_ifcfile_label, 5, 0)
|
||||
layout.addWidget(self.ifcfile_text, 6, 0)
|
||||
layout.addWidget(_ifcfile_browse_btn, 6, 1)
|
||||
|
||||
layout.addLayout(_buttons, 7, 0)
|
||||
# row stretches by 10 compared to the others, std is 0
|
||||
# first parameter is the row number
|
||||
# second is the stretch factor.
|
||||
layout.setRowStretch(0, 10)
|
||||
self.setLayout(layout)
|
||||
|
||||
def select_ifcfile(self):
|
||||
ifcfile = QtWidgets.QFileDialog.getOpenFileName(self, dir=self.get_ifcfile())[0]
|
||||
self.set_ifcfile(ifcfile)
|
||||
|
||||
def set_ifcfile(self, a_file):
|
||||
self.ifcfile_text.setText(a_file)
|
||||
|
||||
def get_ifcfile(self):
|
||||
return self.ifcfile_text.text()
|
||||
|
||||
def select_featurefilesdir(self):
|
||||
thedir = self.featurefilesdir_text.text()
|
||||
features_path = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
caption="Choose features directory ...",
|
||||
dir=thedir,
|
||||
options=QtWidgets.QFileDialog.HideNameFilterDetails,
|
||||
)
|
||||
self.set_featurefilesdir(features_path)
|
||||
|
||||
def set_featurefilesdir(self, a_directory):
|
||||
self.featurefilesdir_text.setText(a_directory)
|
||||
|
||||
def get_featurefilesdir(self):
|
||||
return self.featurefilesdir_text.text()
|
||||
|
||||
def run_bimtester(self):
|
||||
print("Run BIMTester by the GUI")
|
||||
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
|
||||
|
||||
the_features_path = self.get_featurefilesdir()
|
||||
print(the_features_path)
|
||||
|
||||
# get ifc file
|
||||
the_ifcfile = self.get_ifcfile()
|
||||
print(the_ifcfile)
|
||||
|
||||
# overwrite the_features_path and ifcfile in args
|
||||
patched_args = self.args
|
||||
patched_args["featuresdir"] = the_features_path
|
||||
patched_args["ifcfile"] = the_ifcfile
|
||||
|
||||
bimtester.run.TestRunner("file.ifc").run({})
|
||||
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
def close_widget(self):
|
||||
self.close()
|
||||
|
||||
def closeEvent(self, ev):
|
||||
pw = self.parentWidget()
|
||||
if pw and pw.inherits("QDockWidget"):
|
||||
pw.deleteLater()
|
||||
@@ -0,0 +1,4 @@
|
||||
class IfcStore:
|
||||
path = ""
|
||||
file = None
|
||||
bookmarks = {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import gettext
|
||||
|
||||
translation = None
|
||||
|
||||
def _(message):
|
||||
if translation:
|
||||
return translation(message)
|
||||
return message
|
||||
|
||||
|
||||
def switch_locale(locale_dir, locale_id="en"):
|
||||
global translation
|
||||
newlang = gettext.translation("messages", localedir=locale_dir, languages=[locale_id])
|
||||
newlang.install()
|
||||
translation = newlang.gettext
|
||||
@@ -0,0 +1,200 @@
|
||||
# German translations for PROJECT.
|
||||
# Copyright (C) 2020 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||
"PO-Revision-Date: 2020-12-23 11:20+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: de\n"
|
||||
"Language-Team: de <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr "de"
|
||||
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr "Bestanden"
|
||||
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr "Durchgefallen"
|
||||
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr "Erfolgreiche Tests"
|
||||
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr "Dauer"
|
||||
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr "OpenBIM auditing ist eine Funktionalität von"
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
msgstr "und"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:26
|
||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:34
|
||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr "Fehler in falsecount, es ist etwas falsch gelaufen."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:84
|
||||
msgid ""
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:85
|
||||
msgid ""
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:86
|
||||
#: features/steps/attributes_eleclasses_methods.py:112
|
||||
#: features/steps/attributes_eleclasses_methods.py:139
|
||||
#: features/steps/attributes_psets_methods.py:33
|
||||
#: features/steps/geometric_detail_methods.py:56
|
||||
#: features/steps/geometric_detail_methods.py:134
|
||||
msgid "There are no {ifc_class} elements in the IFC file."
|
||||
msgstr "Es sind keine {ifc_class} Bauteile in der IFC-Datei."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:110
|
||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:31
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||
" in the pset."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:32
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||
"missing the property {parameter} in the pset: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
"Alle {elemcount} {ifc_class} Bauteile haben keine geometrische "
|
||||
"Repräsentation der Klasse {parameter}."
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
"Die Anzahl Bauteile {falsecount} von allen {elemcount} {ifc_class} "
|
||||
"Bauteilen haben keine geometrische Repräsentation der Klasse {parameter}:"
|
||||
" {falseelems}"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:132
|
||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||
msgstr ""
|
||||
"Die geometrischen Repräsentationen von allen {elemcount} {ifc_class} "
|
||||
"Bauteilen haben Fehler."
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
"Die geometrischen Repräsentationen von {falsecount} von allen {elemcount}"
|
||||
" {ifc_class} Bauteilen haben Fehler: {falseelems}"
|
||||
|
||||
#: features/steps/ifcdata_methods.py:9
|
||||
msgid "The IFC {} file could not be loaded"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:17
|
||||
msgid "We expected a schema of {} but instead got {}"
|
||||
msgstr "Wir haben das Schema {} erwartet, aber die Daten nutzen das Schema {}"
|
||||
|
||||
#~ msgid "The geometry of all {} {} elements have errors."
|
||||
#~ msgstr ""
|
||||
#~ "Die geometrischen Repräsentationen von allen"
|
||||
#~ " {} {} Bauteilen haben Fehler."
|
||||
|
||||
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||
#~ msgstr ""
|
||||
#~ "Die geometrischen Repräsentationen von {} "
|
||||
#~ "von allen {} {} Bauteilen haben "
|
||||
#~ "Fehler: {}"
|
||||
|
||||
#~ msgid "There are no {} elements in the IFC file."
|
||||
#~ msgstr "Es sind keine {} Bauteile in der IFC-Datei."
|
||||
|
||||
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
#~ "Alle {} {} Bauteile haben keine "
|
||||
#~ "geometrische Repräsentation der Klasse "
|
||||
#~ "IfcFacetedBrep."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {} of {} {} elements"
|
||||
#~ " are not a IfcFacetedBrep representation:"
|
||||
#~ " {}"
|
||||
#~ msgstr ""
|
||||
#~ "Die Anzahl Bauteile {} von allen "
|
||||
#~ "{} {} Bauteilen haben keine geometrische"
|
||||
#~ " Repräsentation der Klasse IfcFacetedBrep: "
|
||||
#~ "{}"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "All {elemcount} {ifc_class} elements are "
|
||||
#~ "not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
#~ "Alle {elemcount} {ifc_class} Bauteile haben"
|
||||
#~ " keine geometrische Repräsentation der "
|
||||
#~ "Klasse IfcFacetedBrep."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {falsecount} of {elemcount} "
|
||||
#~ "{ifc_class} elements are not a "
|
||||
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||
#~ msgstr ""
|
||||
#~ "Die Anzahl Bauteile {falsecount} von "
|
||||
#~ "allen {elemcount} {ifc_class} Bauteilen haben"
|
||||
#~ " keine geometrische Repräsentation der "
|
||||
#~ "Klasse IfcFacetedBrep: {falseelems}"
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
# German translations for PROJECT.
|
||||
# Copyright (C) 2020 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: en\n"
|
||||
"Language-Team: en <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:26
|
||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:34
|
||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:84
|
||||
msgid ""
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:85
|
||||
msgid ""
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:86
|
||||
#: features/steps/attributes_eleclasses_methods.py:112
|
||||
#: features/steps/attributes_eleclasses_methods.py:139
|
||||
#: features/steps/attributes_psets_methods.py:33
|
||||
#: features/steps/geometric_detail_methods.py:56
|
||||
#: features/steps/geometric_detail_methods.py:134
|
||||
msgid "There are no {ifc_class} elements in the IFC file."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:110
|
||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:31
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||
" in the pset."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:32
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||
"missing the property {parameter} in the pset: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:132
|
||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:9
|
||||
msgid "The IFC {} file could not be loaded"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:17
|
||||
msgid "We expected a schema of {} but instead got {}"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "The geometry of all {} {} elements have errors."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "There are no {} elements in the IFC file."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {} of {} {} elements"
|
||||
#~ " are not a IfcFacetedBrep representation:"
|
||||
#~ " {}"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "All {elemcount} {ifc_class} elements are "
|
||||
#~ "not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {falsecount} of {elemcount} "
|
||||
#~ "{ifc_class} elements are not a "
|
||||
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||
#~ msgstr ""
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
# French translations for PROJECT.
|
||||
# Copyright (C) 2020 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||
"PO-Revision-Date: 2020-12-23 11:21+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: fr\n"
|
||||
"Language-Team: fr <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr "fr"
|
||||
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr "Succès"
|
||||
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr "Échec"
|
||||
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr "Tests réussis"
|
||||
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr "Durée"
|
||||
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr "L'audit OpenBIM auditing est une fonctionnalité de"
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
msgstr "et"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:26
|
||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:34
|
||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:84
|
||||
msgid ""
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:85
|
||||
msgid ""
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:86
|
||||
#: features/steps/attributes_eleclasses_methods.py:112
|
||||
#: features/steps/attributes_eleclasses_methods.py:139
|
||||
#: features/steps/attributes_psets_methods.py:33
|
||||
#: features/steps/geometric_detail_methods.py:56
|
||||
#: features/steps/geometric_detail_methods.py:134
|
||||
msgid "There are no {ifc_class} elements in the IFC file."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:110
|
||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:31
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||
" in the pset."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:32
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||
"missing the property {parameter} in the pset: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:132
|
||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:9
|
||||
msgid "The IFC {} file could not be loaded"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:17
|
||||
msgid "We expected a schema of {} but instead got {}"
|
||||
msgstr ""
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# Italian translations for PROJECT.
|
||||
# Copyright (C) 2020 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: it\n"
|
||||
"Language-Team: it <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr "it"
|
||||
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr "Successo"
|
||||
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr "Errore"
|
||||
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr "Test superati"
|
||||
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr "Durata"
|
||||
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr "L'auditing OpenBIM è una caratteristica di"
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
msgstr "e"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:26
|
||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||
msgstr "Tutti {elemcount} elementi nel file sono classificati {ifc_class}"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:34
|
||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||
msgstr ""
|
||||
"{falsecount} di {elemcount} elementi sono elementi {ifc_class}: "
|
||||
"{falseelems}"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr "Errore in falsecount, qualcosa è andato storto."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:84
|
||||
msgid ""
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
"Per tutti {elemcount} elementi {ifc_class} almeno uno di questa "
|
||||
"classedegli attributi {parameter} non contiene alcun valore"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:85
|
||||
msgid ""
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
"Per i seguenti {falsecount} di {elemcount} elementi {ifc_class}almeno uno"
|
||||
" degli attributi {parameter} non contiene alcun valore"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:86
|
||||
#: features/steps/attributes_eleclasses_methods.py:112
|
||||
#: features/steps/attributes_eleclasses_methods.py:139
|
||||
#: features/steps/attributes_psets_methods.py:33
|
||||
#: features/steps/geometric_detail_methods.py:56
|
||||
#: features/steps/geometric_detail_methods.py:134
|
||||
msgid "There are no {ifc_class} elements in the IFC file."
|
||||
msgstr "Il file IFC non contiene elementi della classe {ifc_class}."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:110
|
||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr "Il nome di {elemcount} {elemcount} elementi non è impostato"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
msgstr "Il nome di {falsecount} su {elemcount} elementi {ifc_class} non èimpostato"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr "La descrizione di tutti {elemcount} {elemcount} elementi non èimpostato "
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
"la descrizione di {falsecount} su {elemcount} elementi {ifc_class} non "
|
||||
"èimpostato"
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:31
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||
" in the pset."
|
||||
msgstr ""
|
||||
"A {elemcount} gli elementi {ifc_class} manca la proprietà "
|
||||
"{parameter}nello pset."
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:32
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||
"missing the property {parameter} in the pset: {falseelems}"
|
||||
msgstr ""
|
||||
"Ai seguenti {falsecount} di {elemcount} elementi {ifc_class} manca la "
|
||||
"proprietà{parameter} nello pset: {falseelems}"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr "Tutti {elemcount} elementi {ifc_class} non rappresentano {parameter}"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
"I seguenti {falsecount} di {elemcount} elementi {ifc_class} non "
|
||||
"rappresentano{parameter}: {falseelems}"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:132
|
||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||
msgstr "La geometria di tutti {elemcount} elementi {ifc_class} contiene errori"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
"La geometria di {falsecount} su {elemcount} elementi {ifc_class} "
|
||||
"contieneerrori: {falseelems}"
|
||||
|
||||
#: features/steps/ifcdata_methods.py:9
|
||||
msgid "The IFC {} file could not be loaded"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:17
|
||||
msgid "We expected a schema of {} but instead got {}"
|
||||
msgstr "Ci aspettavamo uno schema di {} ma invece abbiamo ottenuto {}"
|
||||
|
||||
#~ msgid "The geometry of all {} {} elements have errors."
|
||||
#~ msgstr "La geometria di tutti gli elementi {} {} contiene errori."
|
||||
|
||||
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||
#~ msgstr "La geometria di {} su {} {} elementi contiene errori."
|
||||
|
||||
#~ msgid "There are no {} elements in the IFC file."
|
||||
#~ msgstr "Non ci sono {} elementi nel file IFC."
|
||||
|
||||
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||
#~ msgstr "Tutti gli elementi {} {} non sono una rappresentazioneIfcFacetedBrep."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {} of {} {} elements"
|
||||
#~ " are not a IfcFacetedBrep representation:"
|
||||
#~ " {}"
|
||||
#~ msgstr ""
|
||||
#~ "I seguenti {} di {} {} elementi"
|
||||
#~ " non sono una rappresentazioneIfcFacetedBrep. "
|
||||
#~ "{}"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "All {elemcount} {ifc_class} elements are "
|
||||
#~ "not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
#~ "Tutti {elemcount} elementi {ifc_class} non"
|
||||
#~ " sono una rappresentazioneIfcFacetedBrep."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {falsecount} of {elemcount} "
|
||||
#~ "{ifc_class} elements are not a "
|
||||
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||
#~ msgstr ""
|
||||
#~ "I seguenti {falsecount} su {elemcount} "
|
||||
#~ "elementi {ifc_class} non sonouna "
|
||||
#~ "rappresentazione IfcFacetedBrep."
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# Translations template for PROJECT.
|
||||
# Copyright (C) 2021 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr ""
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:26
|
||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:34
|
||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:84
|
||||
msgid ""
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:85
|
||||
msgid ""
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:86
|
||||
#: features/steps/attributes_eleclasses_methods.py:112
|
||||
#: features/steps/attributes_eleclasses_methods.py:139
|
||||
#: features/steps/attributes_psets_methods.py:33
|
||||
#: features/steps/geometric_detail_methods.py:56
|
||||
#: features/steps/geometric_detail_methods.py:134
|
||||
msgid "There are no {ifc_class} elements in the IFC file."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:110
|
||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:31
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||
" in the pset."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:32
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||
"missing the property {parameter} in the pset: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:132
|
||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:9
|
||||
msgid "The IFC {} file could not be loaded"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:17
|
||||
msgid "We expected a schema of {} but instead got {}"
|
||||
msgstr ""
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# Dutch translations for Bimtester.
|
||||
# Copyright (C) 2021 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# Marcel Plomp , 2021.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSIE\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRES\n"
|
||||
"POT-Creation-Date: 2021-01-17 10:55+0100\n"
|
||||
"PO-Revision-Date: 2020-12-18 11:41+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: nl\n"
|
||||
"Language-Team: nl <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: reports.py:158
|
||||
msgid "en"
|
||||
msgstr "nl"
|
||||
|
||||
#: reports.py:159
|
||||
msgid "Success"
|
||||
msgstr "Succes"
|
||||
|
||||
#: reports.py:160
|
||||
msgid "Failure"
|
||||
msgstr "Fout"
|
||||
|
||||
#: reports.py:161
|
||||
msgid "Tests passed"
|
||||
msgstr "Tests geslaagd"
|
||||
|
||||
#: reports.py:162
|
||||
msgid "Duration"
|
||||
msgstr "Tijdsduur"
|
||||
|
||||
#: reports.py:163
|
||||
msgid "OpenBIM auditing is a feature of"
|
||||
msgstr "OpenBIM-auditing is een kenmerk van"
|
||||
|
||||
#: reports.py:164
|
||||
msgid "and"
|
||||
msgstr "en"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:26
|
||||
msgid "All {elemcount} elements in the file are {ifc_class}."
|
||||
msgstr "Alle {elemcount} de elementen in het bestand zijn {ifc_class}"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:34
|
||||
msgid "{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}"
|
||||
msgstr ""
|
||||
"{falsecount} van de {elemcount} elementen zijn {ifc_class} elementen: "
|
||||
"{falseelems}"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:43
|
||||
#: features/steps/utils.py:158
|
||||
msgid "Error in falsecount, something went wrong."
|
||||
msgstr "Fout in de telling, er is iets misgegaan."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:84
|
||||
msgid ""
|
||||
"For all {elemcount} {ifc_class} elements at least one of these class "
|
||||
"attributes {parameter} has no value."
|
||||
msgstr ""
|
||||
"Voor alle {elemcount} {ifc_class} elementen heeft ten minste één van deze"
|
||||
" class attributen {parameter} heeft geen waarde."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:85
|
||||
msgid ""
|
||||
"For the following {falsecount} out of {elemcount} {ifc_class} elements at"
|
||||
" least one of these class attributes {parameter} has no value: "
|
||||
"{falseelems}"
|
||||
msgstr ""
|
||||
"Van de {falsecount} uit {elemcount} {ifc_class} elementen op ten minste "
|
||||
"een van deze class-attributen {parameter} heeft geen waarde:{falseelems}"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:86
|
||||
#: features/steps/attributes_eleclasses_methods.py:112
|
||||
#: features/steps/attributes_eleclasses_methods.py:139
|
||||
#: features/steps/attributes_psets_methods.py:33
|
||||
#: features/steps/geometric_detail_methods.py:56
|
||||
#: features/steps/geometric_detail_methods.py:134
|
||||
msgid "There are no {ifc_class} elements in the IFC file."
|
||||
msgstr "Er zijn geen {ifc_class} elementen in het IFC-bestand."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:110
|
||||
msgid "The name of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr "De naam van alle {elemcount} {elemcount} elementen is niet ingesteld."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:111
|
||||
msgid ""
|
||||
"The name of {falsecount} out of {elemcount} {ifc_class} elements is not "
|
||||
"set: {falseelems}"
|
||||
msgstr ""
|
||||
"De naam van {falsecount} van de {elemcount} {ifc_class} elementen is "
|
||||
"nietset: {falseelems}"
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:137
|
||||
msgid "The description of all {elemcount} {elemcount} elements is not set."
|
||||
msgstr ""
|
||||
"De beschrijving van alle {elemcount} {elemcount} elementen is niet "
|
||||
"ingesteld."
|
||||
|
||||
#: features/steps/attributes_eleclasses_methods.py:138
|
||||
msgid ""
|
||||
"The description of {falsecount} out of {elemcount} {ifc_class} elements "
|
||||
"is not set: {falseelems}"
|
||||
msgstr ""
|
||||
"De beschrijving van {falsecount} uit {elemcount} {ifc_class} elementen is"
|
||||
" niet ingesteld: {falseelems}"
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:31
|
||||
msgid ""
|
||||
"All {elemcount} {ifc_class} elements are missing the property {parameter}"
|
||||
" in the pset."
|
||||
msgstr ""
|
||||
"Alle {elemcount} {ifc_class} elementen missen de eigenschap {parameter} "
|
||||
"in de pset."
|
||||
|
||||
#: features/steps/attributes_psets_methods.py:32
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are "
|
||||
"missing the property {parameter} in the pset: {falseelems}"
|
||||
msgstr ""
|
||||
"De volgende {falsecount} van {elemcount} {ifc_class} elementen zijn "
|
||||
"ontbreekt de eigenschap {parameter} in de pset: {falseelems}"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:54
|
||||
msgid "All {elemcount} {ifc_class} elements are not a {parameter} representation."
|
||||
msgstr ""
|
||||
"Alle {elemcount} {ifc_class} elementen zijn geen {parameter} "
|
||||
"representatie."
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:55
|
||||
msgid ""
|
||||
"The following {falsecount} of {elemcount} {ifc_class} elements are not a "
|
||||
"{parameter} representation: {falseelems}"
|
||||
msgstr ""
|
||||
"De volgende {falsecount} van {elemcount} {ifc_class} -elementen zijn geen"
|
||||
" {parameter} representatie: {falseelems}"
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:132
|
||||
msgid "The geometry of all {elemcount} {ifc_class} elements have errors."
|
||||
msgstr ""
|
||||
"De geometrie van alle {elemcount} de {ifc_class} elementen bevatten "
|
||||
"fouten."
|
||||
|
||||
#: features/steps/geometric_detail_methods.py:133
|
||||
msgid ""
|
||||
"The geometry of {falsecount} out of all {elemcount} {ifc_class} elements "
|
||||
"have errors: {falseelems}"
|
||||
msgstr ""
|
||||
"De geometrie van {falsecount} van alle {elemcount} {ifc_class} elementen "
|
||||
"hebben fouten: {falseelems}"
|
||||
|
||||
#: features/steps/ifcdata_methods.py:9
|
||||
msgid "The IFC {} file could not be loaded"
|
||||
msgstr ""
|
||||
|
||||
#: features/steps/ifcdata_methods.py:17
|
||||
msgid "We expected a schema of {} but instead got {}"
|
||||
msgstr "We verwachtten een schema van {} maar kregen in plaats daarvan {}"
|
||||
|
||||
#~ msgid "The geometry of all {} {} elements have errors."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "The geometry of {} out of all {} {} elements have errors: {}"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "There are no {} elements in the IFC file."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "All {} {} elements are not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {} of {} {} elements"
|
||||
#~ " are not a IfcFacetedBrep representation:"
|
||||
#~ " {}"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "All {elemcount} {ifc_class} elements are "
|
||||
#~ "not a IfcFacetedBrep representation."
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The following {falsecount} of {elemcount} "
|
||||
#~ "{ifc_class} elements are not a "
|
||||
#~ "IfcFacetedBrep representation: {falseelems}"
|
||||
#~ msgstr ""
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import pystache
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
class ReportGenerator:
|
||||
def __init__(self):
|
||||
try:
|
||||
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
||||
self.base_path = sys._MEIPASS
|
||||
except Exception:
|
||||
self.base_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
def generate(self, report_json, output_file):
|
||||
print("# Generating HTML reports.")
|
||||
|
||||
report = json.loads(open(report_json).read())
|
||||
for feature in report:
|
||||
self.generate_feature_report(feature, output_file)
|
||||
|
||||
def generate_feature_report(self, feature, output_file):
|
||||
file_name = os.path.basename(feature["location"]).split(":")[0]
|
||||
data = {
|
||||
"file_name": file_name,
|
||||
"time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"name": feature["name"],
|
||||
"description": feature["description"],
|
||||
"is_success": feature["status"] == "passed",
|
||||
"scenarios": [],
|
||||
}
|
||||
|
||||
if "elements" not in feature:
|
||||
if "status" in feature and feature["status"] == "skipped":
|
||||
print("Feature was skipped. No html report will be created.")
|
||||
else:
|
||||
print("For a unknown reason no html report well be created.")
|
||||
# happens if the feature file does not consist of any valid Scenario
|
||||
return
|
||||
|
||||
for scenario in feature["elements"]:
|
||||
scenario_data = self.process_scenario(scenario)
|
||||
if scenario_data:
|
||||
data["scenarios"].append(scenario_data)
|
||||
|
||||
data["total_passes"] = sum([s["total_passes"] for s in data["scenarios"]])
|
||||
data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]])
|
||||
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100)
|
||||
|
||||
data.update(self.get_template_strings())
|
||||
|
||||
with open(output_file, "w", encoding="utf8") as out:
|
||||
with open(
|
||||
os.path.join(self.base_path, "resources", "reports", "template.html"), encoding="utf8"
|
||||
) as template:
|
||||
out.write(pystache.render(template.read(), data))
|
||||
|
||||
def process_scenario(self, scenario):
|
||||
if len(scenario["steps"]) == 0:
|
||||
print("Scenario '{}' in feature '{}' has no steps.".format(scenario["name"], feature["name"]))
|
||||
return
|
||||
|
||||
steps = []
|
||||
total_duration = 0
|
||||
|
||||
for step in scenario["steps"]:
|
||||
step_data = self.process_step(step)
|
||||
total_duration += step_data["time_raw"]
|
||||
steps.append(step_data)
|
||||
|
||||
total_passes = len([s for s in steps if s["is_success"] is True])
|
||||
total_steps = len(steps)
|
||||
pass_rate = round((total_passes / total_steps) * 100)
|
||||
|
||||
return {
|
||||
"name": scenario["name"],
|
||||
# on behave < 1.2.6 there is no 'status' thus report fails
|
||||
"is_success": scenario["status"] == "passed",
|
||||
"time": round(total_duration, 2),
|
||||
"steps": steps,
|
||||
"total_passes": total_passes,
|
||||
"total_steps": total_steps,
|
||||
"pass_rate": pass_rate,
|
||||
}
|
||||
|
||||
def process_step(self, step):
|
||||
name = step["name"]
|
||||
if "match" in step and "arguments" in step["match"]:
|
||||
for a in step["match"]["arguments"]:
|
||||
name = name.replace(a["value"], "<b>" + a["value"] + "</b>")
|
||||
if "result" not in step:
|
||||
step["result"] = {}
|
||||
step["result"]["status"] = "skipped"
|
||||
step["result"]["duration"] = 0
|
||||
step["result"][
|
||||
"error_message"
|
||||
] = "This requirement has been skipped due to a previous failing step."
|
||||
elif step["result"]["status"] == "undefined":
|
||||
step["result"] = {}
|
||||
step["result"]["status"] = "undefined"
|
||||
step["result"]["duration"] = 0
|
||||
step["result"]["error_message"] = "This requirement has not yet been specified."
|
||||
data = {
|
||||
"name": name,
|
||||
"time_raw": step["result"]["duration"],
|
||||
"time": round(step["result"]["duration"], 2),
|
||||
"is_success": step["result"]["status"] == "passed",
|
||||
"is_unspecified": step["result"]["status"] == "undefined",
|
||||
"is_skipped": step["result"]["status"] == "skipped",
|
||||
"error_message": None
|
||||
if step["result"]["status"] == "passed"
|
||||
else step["result"]["error_message"],
|
||||
}
|
||||
|
||||
# TODO: there is probably a better way of doing this
|
||||
if isinstance(data["error_message"], list):
|
||||
data["error_message"] = data["error_message"][1]
|
||||
return data
|
||||
|
||||
def get_template_strings(self):
|
||||
return {
|
||||
"_lang": _("en"),
|
||||
"_success": _("Success"),
|
||||
"_failure": _("Failure"),
|
||||
"_tests_passed": _("Tests passed"),
|
||||
"_duration": _("Duration"),
|
||||
"_auditing": _("OpenBIM auditing is a feature of"),
|
||||
"_and": _("and"),
|
||||
}
|
||||
|
After Width: | Height: | Size: 104 KiB |
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang={{_lang}}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="foobaro">
|
||||
<title>{{name}}</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Comfortaa|Inconsolata|Open+Sans&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Arial', sans-serif; padding: 10px 40px; }
|
||||
span.time { color: #999; font-style: italic; float: right; }
|
||||
span.step-time { float: right; color: #555; font-size: 0.8em; font-style: italic; }
|
||||
span.success { background-color: #97cc64; padding: 5px; border-radius: 5px; color: #FFF; font-weight: bold; }
|
||||
span.failure { background-color: #fb5a3e; padding: 5px; border-radius: 5px; color: #FFF; font-weight: bold; }
|
||||
p.failure { background-color: #fb5a3e; padding: 5px; border-radius: 5px; color: #fff; }
|
||||
p.unspecified { background-color: #994f00; padding: 5px; border-radius: 5px; color: #fff; }
|
||||
p.skipped { background-color: #8b8d8f; padding: 5px; border-radius: 5px; color: #fff; }
|
||||
p.description { background-color: #eee; border-radius: 5px; padding: 20px; margin-left: auto; margin-right: auto; display: inline-block; font-weight: bold;}
|
||||
li { padding: 10px; font-family: monospace; }
|
||||
li.success { background-color: #b6cca1; color: #333; }
|
||||
li.failure { background-color: #fbb4a8; color: #900; }
|
||||
li.unspecified { background-color: #ffd37f; color: #a30; }
|
||||
li.skipped { background-color: #f5f5f5; color: #333; }
|
||||
li p { margin-bottom: 0px; }
|
||||
footer { color: #999; font-size: 0.8em; }
|
||||
header { text-align: center; }
|
||||
hr { margin: 20px; margin-left: 0px; margin-right: 0px; border: none; border-top: 1px solid #ccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{name}}</h1>
|
||||
<p><strong>{{time}} {{file_name}}</strong></p>
|
||||
<hr>
|
||||
<span class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}">{{#is_success}}{{_success}}{{/is_success}}{{^is_success}}{{_failure}}{{/is_success}}</span>
|
||||
{{_tests_passed}}: <strong>{{total_passes}} / {{total_steps}}</strong> ({{pass_rate}}%)
|
||||
<br />
|
||||
<p class="description">
|
||||
{{#description}}
|
||||
{{.}}<br />
|
||||
{{/description}}
|
||||
</p>
|
||||
<hr>
|
||||
</header>
|
||||
{{#scenarios}}
|
||||
<section>
|
||||
<h2>{{name}}</h2>
|
||||
<p>
|
||||
<span class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}">{{#is_success}}{{_success}}{{/is_success}}{{^is_success}}{{_failure}}{{/is_success}}</span>
|
||||
{{_tests_passed}}: <strong>{{total_passes}} / {{total_steps}}</strong> ({{pass_rate}}%)
|
||||
<span class="time">
|
||||
{{_duration}}: {{time}}s
|
||||
</span>
|
||||
</p>
|
||||
<ol>
|
||||
{{#steps}}
|
||||
<li class="{{#is_success}}success{{/is_success}}{{^is_success}}failure{{/is_success}}{{#is_unspecified}} unspecified{{/is_unspecified}}{{#is_skipped}} skipped{{/is_skipped}}">
|
||||
{{{name}}}
|
||||
<span class="step-time">{{time}}s</span>
|
||||
{{^is_success}}
|
||||
<p class="failure{{#is_unspecified}} unspecified{{/is_unspecified}}{{#is_skipped}} skipped{{/is_skipped}}">
|
||||
{{#error_message}}
|
||||
{{.}}<br />
|
||||
{{/error_message}}
|
||||
</p>
|
||||
{{/is_success}}
|
||||
</li>
|
||||
{{/steps}}
|
||||
</ol>
|
||||
</section>
|
||||
{{/scenarios}}
|
||||
<hr>
|
||||
<footer>
|
||||
<p>
|
||||
{{_auditing}} <a href="https://blenderbim.org/">BlenderBIM</a> {{_and}} <a href="http://ifcopenshell.org/">IfcOpenShell</a>.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,62 @@
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import ifcopenshell
|
||||
try:
|
||||
import ifcopenshell.express
|
||||
except:
|
||||
pass # They are using an old version of IfcOpenShell. Gracefully degrade for now.
|
||||
import behave.formatter.pretty # Needed for pyinstaller to package it
|
||||
from bimtester.ifc import IfcStore
|
||||
from distutils.dir_util import copy_tree
|
||||
from behave.__main__ import main as behave_main
|
||||
|
||||
|
||||
class TestRunner:
|
||||
def __init__(self, ifc_path, ifc=None):
|
||||
IfcStore.path = ifc_path
|
||||
IfcStore.file = ifc if ifc else ifcopenshell.open(ifc_path)
|
||||
|
||||
try:
|
||||
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
||||
self.base_path = sys._MEIPASS
|
||||
except Exception:
|
||||
self.base_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
self.locale_path = os.path.join(self.base_path, "locale")
|
||||
|
||||
def run(self, args):
|
||||
if args["schema_file"]:
|
||||
schema = ifcopenshell.express.parse(args["schema_file"])
|
||||
ifcopenshell.register_schema(args["schema_name"])
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
features_path = os.path.join(tmpdir, "features")
|
||||
steps_path = os.path.join(features_path, "steps")
|
||||
report_json = os.path.join(tmpdir, "report.json")
|
||||
shutil.copytree(os.path.join(self.base_path, "features"), features_path)
|
||||
shutil.copy(args["feature"], features_path)
|
||||
if args["steps"]:
|
||||
if os.path.isfile(args["steps"]):
|
||||
shutil.copy(args["steps"], steps_path)
|
||||
elif os.path.isdir(args["steps"]):
|
||||
copy_tree(args["steps"], steps_path)
|
||||
behave_main(self.get_behave_args(args, features_path, report_json))
|
||||
return report_json
|
||||
|
||||
def get_behave_args(self, args, features_path, report_json):
|
||||
behave_args = [features_path]
|
||||
behave_args.extend(["--define", "localedir={}".format(self.locale_path)])
|
||||
if args["advanced_arguments"]:
|
||||
behave_args.extend(args["advanced_arguments"].split())
|
||||
if args["ifc"]:
|
||||
behave_args.extend(["--define", "ifc={}".format(args["ifc"])])
|
||||
if args["path"]:
|
||||
behave_args.extend(["--define", "path={}".format(args["path"])])
|
||||
if args["lang"]:
|
||||
behave_args.extend(["--lang={}".format(args["lang"])])
|
||||
if not args["console"]:
|
||||
# https://github.com/behave/behave/issues/346
|
||||
behave_args.extend(["--no-capture", "--format", "json.pretty", "--outfile", report_json])
|
||||
return behave_args
|
||||
@@ -0,0 +1,105 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
def assert_guid(ifc, guid):
|
||||
try:
|
||||
return ifc.by_guid(guid)
|
||||
except:
|
||||
assert False, _("An element with the ID {} could not be found.").format(guid)
|
||||
|
||||
|
||||
def assert_number(number):
|
||||
try:
|
||||
return float(number)
|
||||
except ValueError:
|
||||
assert False, _("A number should be specified, not {}").format(number)
|
||||
|
||||
|
||||
def assert_type(element, ifc_class, is_exact=False):
|
||||
if is_exact:
|
||||
assert element.is_a() == ifc_class, _("The element {} is an {} instead of {}.").format(
|
||||
element, element.is_a(), ifc_class
|
||||
)
|
||||
else:
|
||||
assert element.is_a(ifc_class), _("The element {} is an {} instead of {}.").format(
|
||||
element, element.is_a(), ifc_class
|
||||
)
|
||||
|
||||
|
||||
def assert_attribute(element, name, value=None):
|
||||
if not hasattr(element, name):
|
||||
assert False, _("The element {} does not have the attribute {}").format(element, name)
|
||||
if not value:
|
||||
if getattr(element, name) is None:
|
||||
assert False, _("The element {} does not have a value for the attribute {}").format(element, name)
|
||||
return getattr(element, name)
|
||||
if value == "NULL":
|
||||
value = None
|
||||
actual_value = getattr(element, name)
|
||||
if isinstance(value, list) and actual_value:
|
||||
actual_value = list(actual_value)
|
||||
assert actual_value == value, _('We expected a value of "{}" but instead got "{}" for the element {}').format(
|
||||
value, actual_value, element
|
||||
)
|
||||
|
||||
|
||||
def assert_pset(element, pset_name, prop_name=None, value=None):
|
||||
if value == "NULL":
|
||||
value = None
|
||||
psets = ifcopenshell.util.element.get_psets(site)
|
||||
if pset_name not in psets:
|
||||
assert False, _("The element {} does not have a property set named {}").format(element, pset_name)
|
||||
if prop_name is None:
|
||||
return psets[pset_name]
|
||||
if prop_name not in psets[pset_name]:
|
||||
assert False, _('The element {} does not have a property named "{}" in the pset "{}"').format(
|
||||
element, prop_name, pset_name
|
||||
)
|
||||
if value is None:
|
||||
return psets[pset_name][prop_name]
|
||||
actual_value = psets[pset_name][prop_name]
|
||||
assert actual_value == value, _('We expected a value of "{}" but instead got "{}" for the element {}').format(
|
||||
value, actual_value, element
|
||||
)
|
||||
|
||||
|
||||
# TODO: what is this?
|
||||
def assert_elements(
|
||||
ifc_class,
|
||||
elemcount,
|
||||
falsecount,
|
||||
falseelems,
|
||||
message_all_falseelems,
|
||||
message_some_falseelems,
|
||||
message_no_elems,
|
||||
parameter=None,
|
||||
):
|
||||
if elemcount > 0 and falsecount == 0:
|
||||
return # Test OK
|
||||
elif elemcount == 0:
|
||||
assert False, message_no_elems.format(ifc_class=ifc_class)
|
||||
elif falsecount == elemcount:
|
||||
if parameter is None:
|
||||
assert False, message_all_falseelems.format(elemcount=elemcount, ifc_class=ifc_class)
|
||||
else:
|
||||
assert False, message_all_falseelems.format(elemcount=elemcount, ifc_class=ifc_class, parameter=parameter)
|
||||
elif falsecount > 0 and falsecount < elemcount:
|
||||
if parameter is None:
|
||||
assert False, message_some_falseelems.format(
|
||||
falsecount=falsecount,
|
||||
elemcount=elemcount,
|
||||
ifc_class=ifc_class,
|
||||
falseelems=falseelems,
|
||||
)
|
||||
else:
|
||||
assert False, message_some_falseelems.format(
|
||||
falsecount=falsecount,
|
||||
elemcount=elemcount,
|
||||
ifc_class=ifc_class,
|
||||
falseelems=falseelems,
|
||||
parameter=parameter,
|
||||
)
|
||||
else:
|
||||
assert False, _("Error in falsecount, something went wrong.")
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import bimtester.clean
|
||||
import bimtester.reports
|
||||
import bimtester.run
|
||||
|
||||
parser = argparse.ArgumentParser(description="Runs unit tests for BIM data")
|
||||
parser.add_argument("-a", "--action", type=str, help="Action to perform, from run/purge", default="run")
|
||||
parser.add_argument("--advanced-arguments", type=str, help="Specify arguments to Behave", default="")
|
||||
parser.add_argument("-c", "--console", action="store_true", help="Show results in the console")
|
||||
parser.add_argument("-f", "--feature", type=str, help="Specify a feature file to test", required=True)
|
||||
parser.add_argument("-i", "--ifc", type=str, help="Specify an IFC file to test", required=True)
|
||||
parser.add_argument("-p", "--path", type=str, help="Define a path for use in test steps that use relative paths")
|
||||
parser.add_argument("-r", "--report", type=str, help="Specify an output file for a HTML report")
|
||||
parser.add_argument("--steps", type=str, help="Specify a custom step definition Python file or directory")
|
||||
parser.add_argument("--schema-file", type=str, help="Path to a custom IFC schema, used with --schema-name")
|
||||
parser.add_argument("--schema-name", type=str, help="The name of a custom IFC schema, used with --schema-file")
|
||||
parser.add_argument("--lang", type=str, help="Specify a language e.g. en/de/fr/it", default="")
|
||||
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
if args["action"] == "run":
|
||||
report_json = bimtester.run.TestRunner(args["ifc"]).run(args)
|
||||
if args["report"]:
|
||||
bimtester.reports.ReportGenerator().generate(report_json, args["report"])
|
||||
elif args["action"] == "purge":
|
||||
bimtester.clean.TestPurger().purge()
|
||||
@@ -0,0 +1,54 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||
FILE_NAME('col.ifc','2021-01-11T05:16:43',('',''),(''),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','');
|
||||
FILE_SCHEMA(('IFC2X3'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||
#2=IFCORGANIZATION($,'',$,$,$);
|
||||
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||
#4=IFCAPPLICATION(#2,'0.19 build 23652 (Git)','FreeCAD','118df2cf_ed21_438e_a41');
|
||||
#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,1610342203,#3,#4,1610342203);
|
||||
#6=IFCDIRECTION((1.,0.,0.));
|
||||
#7=IFCDIRECTION((0.,0.,1.));
|
||||
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||
#10=IFCDIRECTION((0.,1.,0.));
|
||||
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||
#20=IFCDIRECTION((0.,1.));
|
||||
#21=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#20);
|
||||
#22=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#21,$,.MODEL_VIEW.,$);
|
||||
#23=IFCPROJECT('2iAYrakL9FABNNwZfj$CbO',#5,'BIMTester Example 1 - IFC2X3',$,$,$,$,(#21),#19);
|
||||
#24=IFCDIRECTION((1.,0.));
|
||||
#25=IFCCARTESIANPOINT((0.,0.));
|
||||
#26=IFCAXIS2PLACEMENT2D(#25,#24);
|
||||
#27=IFCCIRCLEPROFILEDEF(.AREA.,$,#26,0.2);
|
||||
#28=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#29=IFCAXIS2PLACEMENT3D(#28,#7,#6);
|
||||
#30=IFCEXTRUDEDAREASOLID(#27,#29,#7,5.);
|
||||
#31=IFCCOLOURRGB($,1.,0.5,1.);
|
||||
#32=IFCSURFACESTYLERENDERING(#31,$,$,$,$,$,$,$,.FLAT.);
|
||||
#33=IFCSURFACESTYLE($,.BOTH.,(#32));
|
||||
#34=IFCPRESENTATIONSTYLEASSIGNMENT((#33));
|
||||
#35=IFCSTYLEDITEM(#30,(#34),$);
|
||||
#36=IFCLOCALPLACEMENT($,#9);
|
||||
#37=IFCSHAPEREPRESENTATION(#22,'Body','SweptSolid',(#30));
|
||||
#38=IFCPRODUCTDEFINITIONSHAPE($,$,(#37));
|
||||
#39=IFCBUILDINGELEMENTPROXY('3JNmm1CUH9H9P6lVsx1y3W',#5,'Structure','',$,#36,#38,$,.ELEMENT.);
|
||||
#40=IFCSITE('2PJ1ax1HL4SgHFFReEEwE$',#5,'Default Site','',$,$,$,$,.ELEMENT.,$,$,$,$,$);
|
||||
#41=IFCRELAGGREGATES('1J6GQExT511x6QRu5FmkD2',#5,'ProjectLink','',#23,(#40));
|
||||
#42=IFCBUILDING('1tIoXRzCXF3vuIMrF6RVcd',#5,'Default Building','',$,$,$,$,.ELEMENT.,$,$,$);
|
||||
#43=IFCRELAGGREGATES('2GkPanCgnAzQY_0xv8dnHH',#5,'SiteLink','',#40,(#42));
|
||||
#44=IFCBUILDINGSTOREY('1L8$GCIw116uw35vpyjSsO',#5,'Default Storey','',$,$,$,$,.ELEMENT.,$);
|
||||
#45=IFCRELAGGREGATES('1lB$$h00nFaPQb2gvlhRX$',#5,'DefaultStoreyLink','',#42,(#44));
|
||||
#46=IFCRELCONTAINEDINSPATIALSTRUCTURE('3dnpjDLD5DvuyUGcyHqRvU',#5,'UnassignedObjectsLink','',(#39),#44);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -0,0 +1,54 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||
FILE_NAME('col.ifc','2021-01-11T05:12:19',('',''),(''),'IfcOpenShell 0.6.0b0','IfcOpenShell 0.6.0b0','');
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPERSON($,$,'',$,$,$,$,$);
|
||||
#2=IFCORGANIZATION($,'',$,$,$);
|
||||
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||
#4=IFCAPPLICATION(#2,'0.19 build 23652 (Git)','FreeCAD','118df2cf_ed21_438e_a41');
|
||||
#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,1610341939,#3,#4,1610341939);
|
||||
#6=IFCDIRECTION((1.,0.,0.));
|
||||
#7=IFCDIRECTION((0.,0.,1.));
|
||||
#8=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
|
||||
#10=IFCDIRECTION((0.,1.,0.));
|
||||
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
|
||||
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
|
||||
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
|
||||
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
|
||||
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
|
||||
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
|
||||
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
|
||||
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
|
||||
#20=IFCDIRECTION((0.,1.));
|
||||
#21=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#20);
|
||||
#22=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#21,$,.MODEL_VIEW.,$);
|
||||
#23=IFCPROJECT('2iAYrakL9FABNNwZfj$CbO',#5,'BIMTester Example 1 - IFC4',$,$,$,$,(#21),#19);
|
||||
#24=IFCDIRECTION((1.,0.));
|
||||
#25=IFCCARTESIANPOINT((0.,0.));
|
||||
#26=IFCAXIS2PLACEMENT2D(#25,#24);
|
||||
#27=IFCCIRCLEPROFILEDEF(.AREA.,$,#26,0.2);
|
||||
#28=IFCCARTESIANPOINT((0.,0.,0.));
|
||||
#29=IFCAXIS2PLACEMENT3D(#28,#7,#6);
|
||||
#30=IFCEXTRUDEDAREASOLID(#27,#29,#7,5.);
|
||||
#31=IFCCOLOURRGB($,1.,0.5,1.);
|
||||
#32=IFCSURFACESTYLERENDERING(#31,$,$,$,$,$,$,$,.FLAT.);
|
||||
#33=IFCSURFACESTYLE($,.BOTH.,(#32));
|
||||
#34=IFCPRESENTATIONSTYLEASSIGNMENT((#33));
|
||||
#35=IFCSTYLEDITEM(#30,(#34),$);
|
||||
#36=IFCLOCALPLACEMENT($,#9);
|
||||
#37=IFCSHAPEREPRESENTATION(#22,'Body','SweptSolid',(#30));
|
||||
#38=IFCPRODUCTDEFINITIONSHAPE($,$,(#37));
|
||||
#39=IFCBUILDINGELEMENTPROXY('3JNmm1CUH9H9P6lVsx1y3W',#5,'Structure','',$,#36,#38,$,.COMPLEX.);
|
||||
#40=IFCSITE('2PJ1ax1HL4SgHFFReEEwE$',#5,'Default Site','',$,$,$,$,.ELEMENT.,$,$,$,$,$);
|
||||
#41=IFCRELAGGREGATES('1J6GQExT511x6QRu5FmkD2',#5,'ProjectLink','',#23,(#40));
|
||||
#42=IFCBUILDING('1tIoXRzCXF3vuIMrF6RVcd',#5,'Default Building','',$,$,$,$,.ELEMENT.,$,$,$);
|
||||
#43=IFCRELAGGREGATES('2GkPanCgnAzQY_0xv8dnHH',#5,'SiteLink','',#40,(#42));
|
||||
#44=IFCBUILDINGSTOREY('1L8$GCIw116uw35vpyjSsO',#5,'Default Storey','',$,$,$,$,.ELEMENT.,$);
|
||||
#45=IFCRELAGGREGATES('1lB$$h00nFaPQb2gvlhRX$',#5,'DefaultStoreyLink','',#42,(#44));
|
||||
#46=IFCRELCONTAINEDINSPATIALSTRUCTURE('3dnpjDLD5DvuyUGcyHqRvU',#5,'UnassignedObjectsLink','',(#39),#44);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -0,0 +1,18 @@
|
||||
# language: de
|
||||
|
||||
Funktionalität: Basisdaten
|
||||
|
||||
Um BIM-Daten anzusehen
|
||||
Für alle beteiligten Akteure
|
||||
Wir brauchen eine IFC-Datei
|
||||
|
||||
|
||||
Szenario: Bereitstellen von IFC-Daten
|
||||
|
||||
* Die IFC-Datei wurde durch einen Startparameter zur Verfügung gestellt
|
||||
* Die IFC-Daten müssen das IFC2X3 Schema benutzen
|
||||
|
||||
|
||||
Szenario: Projektinformationen
|
||||
|
||||
* Der Name, die Abkürzung oder die Kurzkennung des Projektes ist "BIMTester Example 1 - IFC2X3"
|
||||
@@ -0,0 +1,18 @@
|
||||
# language: en
|
||||
|
||||
Feature: Base setup
|
||||
|
||||
In order to view the BIM data
|
||||
As any interested stakeholder
|
||||
We need an IFC file
|
||||
|
||||
|
||||
Scenario: Receiving a file
|
||||
|
||||
* The IFC file has been provided through an argument
|
||||
* IFC data must use the IFC2X3 schema
|
||||
|
||||
|
||||
Scenario: Project information
|
||||
|
||||
* The project name, code, or short identifier must be "BIMTester Example 1 - IFC2X3"
|
||||
@@ -0,0 +1,18 @@
|
||||
# language: fr
|
||||
|
||||
Fonctionnalité: Base setup
|
||||
|
||||
In order to view the BIM data
|
||||
As any interested stakeholder
|
||||
We need an IFC file
|
||||
|
||||
|
||||
Scénario: Recevoir e fichier
|
||||
|
||||
* The IFC file has been provided through an argument
|
||||
* Les données IFC doivent utiliser le schéma IFC2X3
|
||||
|
||||
|
||||
Scénario: Project information
|
||||
|
||||
* The project name, code, or short identifier must be "BIMTester Example 1 - IFC2X3"
|
||||
@@ -0,0 +1,18 @@
|
||||
# language: it
|
||||
|
||||
Funzionalità: Dati di base
|
||||
|
||||
Per poter consultare dati BIM
|
||||
a tutti gli attori partecipanti
|
||||
serve un file IFC
|
||||
|
||||
|
||||
Scenario: Preparare Dati IFC
|
||||
|
||||
* Il file IFC è stato fornito attraverso un argumento
|
||||
* I dati IFC devono seguire lo schema IFC2X3
|
||||
|
||||
|
||||
Scenario: Project information
|
||||
|
||||
* Il nome del progetto, codice o identificatore breve deve essere "BIMTester Example 1 - IFC2X3"
|
||||
@@ -0,0 +1,18 @@
|
||||
# language: nl
|
||||
|
||||
Functionaliteit: Basisgegevens
|
||||
|
||||
Om BIM-gegevens te bekijken
|
||||
Zoals elke geïnteresseerde stakeholder
|
||||
We hebben een IFC-bestand nodig
|
||||
|
||||
|
||||
Scenario: Bestand ontvangen
|
||||
|
||||
* The IFC file has been provided through an argument
|
||||
* IFC-gegevens moeten het IFC2X3 -schema gebruiken
|
||||
|
||||
|
||||
Scenario: Project informatie
|
||||
|
||||
* De projectnaam, code of korte ID moet "BIMTester Example 1 - IFC2X3"
|
||||
@@ -0,0 +1,144 @@
|
||||
from behave import step, given, when, then, use_step_matcher
|
||||
|
||||
use_step_matcher("parse")
|
||||
|
||||
|
||||
@step("There must be exactly {number} {ifc_class} element")
|
||||
@step("There must be exactly {number} {ifc_class} elements")
|
||||
def step_impl(context, number, ifc_class):
|
||||
num = len(IfcStore.file.by_type(ifc_class))
|
||||
assert num == int(number), "Could not find {} elements of {}. Found {} element(s).".format(number, ifc_class, num)
|
||||
|
||||
|
||||
@given("a set of specific related elements")
|
||||
def step_impl(context):
|
||||
model = getattr(context, "model", None)
|
||||
if not model:
|
||||
context.model = TableModel()
|
||||
for row in context.table:
|
||||
context.model.add_row(row["RelatedObjects"], row["RelatingGroup"])
|
||||
|
||||
|
||||
@given('a set of specific related elements taken from the file "{path_file}"')
|
||||
def step_impl(context, path_file):
|
||||
import csv
|
||||
import os
|
||||
|
||||
model = getattr(context, "model", None)
|
||||
if not model:
|
||||
context.model = TableModel()
|
||||
if context.config.userdata.get("path"):
|
||||
path_file = os.path.join(context.config.userdata.get("path"), path_file)
|
||||
if not os.path.exists(path_file):
|
||||
assert False, "File {} not found".format(path_file)
|
||||
with open(path_file, "r", encoding="utf-8-sig") as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
for row in reader:
|
||||
context.model.add_row(row["RelatedObjects"], row["RelatingGroup"])
|
||||
|
||||
|
||||
@then("there must be exactly a number of {ifc_class} equals to the number of distinct row value")
|
||||
def step_impl(context, ifc_class):
|
||||
try:
|
||||
context.execute_steps(
|
||||
"""
|
||||
then There must be exactly {number} {ifc_class} elements
|
||||
""".format(
|
||||
ifc_class=ifc_class, number=context.model.get_count_distinct_values()
|
||||
)
|
||||
)
|
||||
except AssertionError as error:
|
||||
str_error = str(error)
|
||||
assert False, str_error[: str_error.find("Traceback")]
|
||||
assert True
|
||||
|
||||
|
||||
@then(
|
||||
"there is a relationship {ifc_class} with {left_attribute} and {right_attribute} between the two elements of each row"
|
||||
)
|
||||
def step_impl(context, ifc_class, left_attribute, right_attribute):
|
||||
rows = context.model.rows
|
||||
elements = IfcStore.file.by_type(ifc_class)
|
||||
errors = []
|
||||
for key, value in rows.items():
|
||||
found = False
|
||||
for element in elements:
|
||||
if (
|
||||
any(x.Name == key for x in getattr(element, left_attribute))
|
||||
and getattr(element, right_attribute).Name == value
|
||||
):
|
||||
found = True
|
||||
if not found:
|
||||
errors.append(f"The row ({key}, {value}) does not have the relationship.")
|
||||
assert not errors, "Errors occured:\n{}".format("\n".join(errors))
|
||||
|
||||
|
||||
use_step_matcher("re")
|
||||
|
||||
|
||||
@step("all IfcGroup must be linked to a type in the list (?P<linked_ifc_classes>.*)")
|
||||
def step_impl(context, linked_ifc_classes):
|
||||
groups = IfcStore.file.by_type("IfcGroup")
|
||||
errors = []
|
||||
for group in groups:
|
||||
if not hasattr(group, "IsGroupedBy"):
|
||||
errors.append(f'The element "{group.Name}" has no "IsGroupedBy" attribute.')
|
||||
else:
|
||||
for grouped_by in getattr(group, "IsGroupedBy"):
|
||||
if not hasattr(grouped_by, "RelatedObjects"):
|
||||
errors.append(f'The element "{grouped_by.Name}" has no "RelatedObjects" attribute.')
|
||||
else:
|
||||
for related_object in getattr(grouped_by, "RelatedObjects"):
|
||||
found = False
|
||||
for linked_ifc_class in linked_ifc_classes.split(","):
|
||||
if related_object.is_a(linked_ifc_class):
|
||||
found = True
|
||||
if not found:
|
||||
errors.append(
|
||||
f'The element "{related_object.Name}" does not have the right associated type.'
|
||||
)
|
||||
assert not errors, "Errors occured:\n{}".format("\n".join(errors))
|
||||
|
||||
|
||||
@then("there is an element of type (?P<ifc_types>.*) with a (?P<attribute_name>.*) attribute for each row key")
|
||||
def step_impl(context, ifc_types, attribute_name):
|
||||
check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_name, context.model.rows.keys())
|
||||
|
||||
|
||||
@then("there is an element of type (?P<ifc_types>.*) with a (?P<attribute_name>.*) attribute for each row value")
|
||||
def step_impl(context, ifc_types, attribute_name):
|
||||
values = set(context.model.rows.values())
|
||||
check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_name, values)
|
||||
|
||||
|
||||
def check_if_element_exists_by_types_with_attribute_name(ifc_types, attribute_name, attribute_values):
|
||||
errors = []
|
||||
|
||||
elements = []
|
||||
for ifc_type in ifc_types.split(","):
|
||||
elements += ifc.by_type(ifc_type.strip())
|
||||
|
||||
for attribute_value in attribute_values:
|
||||
found = False
|
||||
for element in elements:
|
||||
if hasattr(element, attribute_name) and getattr(element, attribute_name) == attribute_value:
|
||||
found = True
|
||||
if not found:
|
||||
errors.append(f'An element with {attribute_name} attribute "{attribute_value}" was not found.')
|
||||
assert not errors, "Errors occured:\n{}".format("\n".join(errors))
|
||||
|
||||
|
||||
class TableModel(object):
|
||||
"""This class represents a table of data."""
|
||||
|
||||
def __init__(self):
|
||||
self.rows = dict()
|
||||
|
||||
def add_row(self, related, relating):
|
||||
self.rows[related] = relating
|
||||
|
||||
def get_count(self):
|
||||
return len(self.rows)
|
||||
|
||||
def get_count_distinct_values(self):
|
||||
return len(set(self.rows.values()))
|
||||
@@ -0,0 +1,52 @@
|
||||
import gettext
|
||||
from behave import given
|
||||
from behave import step
|
||||
|
||||
from utils import IfcFile
|
||||
from bimtester.ifc import IfcStore
|
||||
from bimtester.lang import _
|
||||
|
||||
|
||||
@step("The IFC file must be exported by application full name {fullname}")
|
||||
def step_impl(context, fullname):
|
||||
|
||||
real_fullname = IfcStore.file.by_type("IfcApplication")[0].ApplicationFullName
|
||||
assert real_fullname == fullname, (
|
||||
"The IFC file was not exported by application full name {} "
|
||||
"instead it was exported by application full name {}".format(fullname, real_fullname)
|
||||
)
|
||||
|
||||
|
||||
@step("The IFC file must be exported by application identifier {identifier}")
|
||||
def step_impl(context, identifier):
|
||||
|
||||
real_identifier = IfcStore.file.by_type("IfcApplication")[0].ApplicationIdentifier
|
||||
assert (
|
||||
real_identifier == identifier
|
||||
), "The IFC file was not exported by application identifier {} " "instead it was exported by identifier {}".format(
|
||||
identifier, real_identifier
|
||||
)
|
||||
|
||||
|
||||
@step("The IFC file must be exported by the application version {version}")
|
||||
def step_impl(context, version):
|
||||
|
||||
real_version = IfcStore.file.by_type("IfcApplication")[0].Version
|
||||
assert (
|
||||
real_version == version
|
||||
), "The IFC file was not exported by application version {} " "instead it was exported by version {}".format(
|
||||
version, real_version
|
||||
)
|
||||
|
||||
|
||||
@step(
|
||||
"IFC data header must have a file description of {header_file_description} such as the new Allplan IFC exporter creates it"
|
||||
)
|
||||
def step_impl(context, header_file_description):
|
||||
|
||||
is_header_file_description = IfcStore.file.wrapped_data.header.file_description.description
|
||||
assert (
|
||||
str(is_header_file_description) == header_file_description
|
||||
), "The file was not exported by the new ifc exporter in Allplan. File description header: {}".format(
|
||||
is_header_file_description
|
||||
)
|
||||
@@ -0,0 +1,91 @@
|
||||
from behave import step
|
||||
|
||||
import attributes_eleclasses_methods as aem
|
||||
from utils import assert_elements
|
||||
from utils import IfcFile
|
||||
|
||||
|
||||
@step("There are no {ifc_class} elements")
|
||||
def step_impl(context, ifc_class):
|
||||
aem.no_eleclass(context, ifc_class)
|
||||
|
||||
|
||||
@step("There are no {ifc_class} elements because {reason}")
|
||||
def step_impl(context, ifc_class, reason):
|
||||
aem.no_eleclass(context, ifc_class)
|
||||
|
||||
|
||||
@step("All {ifc_class} elements class attributes have a value")
|
||||
def step_impl(context, ifc_class):
|
||||
aem.eleclass_have_class_attributes_with_a_value(context, ifc_class)
|
||||
|
||||
|
||||
@step("All {ifc_class} elements have a name given")
|
||||
def step_impl(context, ifc_class):
|
||||
aem.eleclass_has_name_with_a_value(context, ifc_class)
|
||||
|
||||
|
||||
@step("All {ifc_class} elements have a description given")
|
||||
def step_impl(context, ifc_class):
|
||||
aem.eleclass_has_description_with_a_value(context, ifc_class)
|
||||
|
||||
|
||||
@step('all {ifc_class} elements have a name matching the pattern "{pattern}"')
|
||||
def step_impl(context, ifc_class, pattern):
|
||||
import re
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
if not re.search(pattern, element.Name):
|
||||
assert False
|
||||
|
||||
|
||||
@step('there is an {ifc_class} element with a {attribute_name} attribute with a value of "{attribute_value}"')
|
||||
def step_impl(context, ifc_class, attribute_name, attribute_value):
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
if hasattr(element, attribute_name) and getattr(element, attribute_name) == attribute_value:
|
||||
return
|
||||
assert False
|
||||
|
||||
|
||||
use_step_matcher("re")
|
||||
|
||||
|
||||
@step("all (?P<ifc_class>.*) elements have an? (?P<attribute>.*) attribute")
|
||||
def step_impl(context, ifc_class, attribute):
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
if not getattr(element, attribute):
|
||||
assert False
|
||||
|
||||
|
||||
@step('all (?P<ifc_class>.*) elements have an? (?P<attribute>.*) matching the pattern "(?P<pattern>.*)"')
|
||||
def step_impl(context, ifc_class, attribute, pattern):
|
||||
import re
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
value = getattr(element, attribute)
|
||||
print(f'Checking value "{value}" for {element}')
|
||||
assert re.search(pattern, value)
|
||||
|
||||
|
||||
@step('all (?P<ifc_class>.*) elements have an? (?P<attributes>.*) taken from the list in "(?P<list_file>.*)"')
|
||||
def step_impl(context, ifc_class, attributes, list_file):
|
||||
import csv
|
||||
|
||||
values = []
|
||||
with open(list_file) as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
for row in reader:
|
||||
values.append(row)
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
attribute_values = []
|
||||
for attribute in attributes.split(","):
|
||||
if not hasattr(element, attribute):
|
||||
assert False, f"Failed at element {element.GlobalId}"
|
||||
attribute_values.append(getattr(element, attribute))
|
||||
if attribute_values not in values:
|
||||
assert False, f"Failed at element {element.GlobalId}"
|
||||
@@ -0,0 +1,26 @@
|
||||
from behave import step
|
||||
|
||||
|
||||
@step("Es sind keine {ifc_class} Bauteile vorhanden")
|
||||
def step_impl(context, ifc_class):
|
||||
context.execute_steps(f"* There are no {ifc_class} elements")
|
||||
|
||||
|
||||
@step("Aus folgendem Grund gibt es keine {ifc_class} Bauteile: {reason}")
|
||||
def step_impl(context, ifc_class, reason):
|
||||
context.execute_steps(f"* There are no {ifc_class} elements because {reason}")
|
||||
|
||||
|
||||
@step("Alle {ifc_class} Bauteilklassenattribute haben einen Wert")
|
||||
def step_impl(context, ifc_class):
|
||||
context.execute_steps(f"* All {ifc_class} elements class attributes have a value")
|
||||
|
||||
|
||||
@step("Bei allen {ifc_class} Bauteile ist der Name angegeben")
|
||||
def step_impl(context, ifc_class):
|
||||
context.execute_steps(f"* All {ifc_class} elements have a name given")
|
||||
|
||||
|
||||
@step("Bei allen {ifc_class} Bauteile ist die Beschreibung angegeben")
|
||||
def step_impl(context, ifc_class):
|
||||
context.execute_steps(f"* All {ifc_class} elements have a description given")
|
||||
@@ -0,0 +1,140 @@
|
||||
import gettext # noqa
|
||||
|
||||
from utils import assert_elements
|
||||
from utils import IfcFile
|
||||
|
||||
|
||||
def no_eleclass(
|
||||
context, ifc_class
|
||||
):
|
||||
|
||||
context.falseelems = []
|
||||
context.falseguids = []
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for elem in elements:
|
||||
context.falseelems.append(str(elem))
|
||||
context.falseguids.append(elem.GlobalId)
|
||||
|
||||
context.elemcount = len(elements)
|
||||
context.falsecount = len(context.falseelems)
|
||||
|
||||
if context.elemcount == 0 and context.falsecount == 0:
|
||||
return # Test OK, thus we can not use the assert_elements method
|
||||
elif context.falsecount == context.elemcount:
|
||||
assert False, (
|
||||
_("All {elemcount} elements in the file are {ifc_class}.")
|
||||
.format(
|
||||
elemcount=context.elemcount,
|
||||
ifc_class=ifc_class
|
||||
)
|
||||
)
|
||||
elif context.falsecount > 0 and fcontext.alsecount < context.elemcount:
|
||||
assert False, (
|
||||
_("{falsecount} of {elemcount} element are {ifc_class} elements: {falseelems}")
|
||||
.format(
|
||||
falsecount=context.falsecount,
|
||||
elemcount=context.elemcount,
|
||||
ifc_class=ifc_class,
|
||||
falseelems=context.falseelems,
|
||||
)
|
||||
)
|
||||
else:
|
||||
assert False, _("Error in falsecount, something went wrong.")
|
||||
|
||||
|
||||
def eleclass_have_class_attributes_with_a_value(
|
||||
context, ifc_class
|
||||
):
|
||||
|
||||
from ifcopenshell.ifcopenshell_wrapper import schema_by_name
|
||||
# schema = schema_by_name("IFC2X3")
|
||||
schema = schema_by_name(IfcFile.get().schema)
|
||||
class_attributes = []
|
||||
for cl_attrib in schema.declaration_by_name(ifc_class).all_attributes():
|
||||
class_attributes.append(cl_attrib.name())
|
||||
# print(class_attributes)
|
||||
|
||||
context.falseelems = []
|
||||
context.falseguids = []
|
||||
context.falseprops = {}
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for elem in elements:
|
||||
failed_attribs = []
|
||||
elem_failed = False
|
||||
for cl_attrib in class_attributes:
|
||||
attrib_value = getattr(elem, cl_attrib)
|
||||
if not attrib_value:
|
||||
elem_failed = True
|
||||
failed_attribs.append(cl_attrib)
|
||||
# print(attrib_value)
|
||||
if elem_failed is True:
|
||||
context.falseelems.append(str(elem))
|
||||
context.falseguids.append(elem.GlobalId)
|
||||
context.falseprops[elem.id()] = failed_attribs
|
||||
|
||||
context.elemcount = len(elements)
|
||||
context.falsecount = len(context.falseelems)
|
||||
assert_elements(
|
||||
ifc_class,
|
||||
context.elemcount,
|
||||
context.falsecount,
|
||||
context.falseelems,
|
||||
message_all_falseelems=_("For all {elemcount} {ifc_class} elements at least one of these class attributes {parameter} has no value."),
|
||||
message_some_falseelems=_("For the following {falsecount} out of {elemcount} {ifc_class} elements at least one of these class attributes {parameter} has no value: {falseelems}"),
|
||||
message_no_elems=_("There are no {ifc_class} elements in the IFC file."),
|
||||
parameter=failed_attribs
|
||||
)
|
||||
|
||||
|
||||
def eleclass_has_name_with_a_value(context, ifc_class):
|
||||
|
||||
context.falseelems = []
|
||||
context.falseguids = []
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for elem in elements:
|
||||
# print(elem.Name)
|
||||
if not elem.Name:
|
||||
context.falseelems.append(str(elem))
|
||||
context.falseguids.append(elem.GlobalId)
|
||||
|
||||
context.elemcount = len(elements)
|
||||
context.falsecount = len(context.falseelems)
|
||||
assert_elements(
|
||||
ifc_class,
|
||||
context.elemcount,
|
||||
context.falsecount,
|
||||
context.falseelems,
|
||||
message_all_falseelems=_("The name of all {elemcount} {elemcount} elements is not set."),
|
||||
message_some_falseelems=_("The name of {falsecount} out of {elemcount} {ifc_class} elements is not set: {falseelems}"),
|
||||
message_no_elems=_("There are no {ifc_class} elements in the IFC file."),
|
||||
)
|
||||
|
||||
|
||||
def eleclass_has_description_with_a_value(
|
||||
context, ifc_class
|
||||
):
|
||||
|
||||
context.falseelems = []
|
||||
context.falseguids = []
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for elem in elements:
|
||||
# print(elem.Description)
|
||||
if not elem.Description:
|
||||
context.falseelems.append(str(elem))
|
||||
context.falseguids.append(elem.GlobalId)
|
||||
|
||||
context.elemcount = len(elements)
|
||||
context.falsecount = len(context.falseelems)
|
||||
assert_elements(
|
||||
ifc_class,
|
||||
context.elemcount,
|
||||
context.falsecount,
|
||||
context.falseelems,
|
||||
message_all_falseelems=_("The description of all {elemcount} {elemcount} elements is not set."),
|
||||
message_some_falseelems=_("The description of {falsecount} out of {elemcount} {ifc_class} elements is not set: {falseelems}"),
|
||||
message_no_elems=_("There are no {ifc_class} elements in the IFC file."),
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
from behave import step
|
||||
|
||||
import attributes_psets_methods as apm
|
||||
from utils import assert_elements
|
||||
from utils import IfcFile
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
the_lang = "en"
|
||||
|
||||
|
||||
@step("all {ifc_class} elements have an {aproperty} property in the {pset} pset")
|
||||
def step_impl(context, ifc_class, aproperty, pset):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
apm.eleclass_has_property_in_pset(
|
||||
context,
|
||||
ifc_class,
|
||||
aproperty,
|
||||
pset
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# STEPS with Regular Expression Matcher ("re")
|
||||
# ------------------------------------------------------------------------
|
||||
use_step_matcher("re")
|
||||
|
||||
|
||||
@step("all (?P<ifc_class>.*) elements have an? (?P<property_path>.*\..*) property")
|
||||
def step_impl(context, ifc_class, property_path):
|
||||
pset_name, property_name = property_path.split(".")
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
if not IfcFile.get_property(element, pset_name, property_name):
|
||||
assert False
|
||||
|
||||
|
||||
@step(
|
||||
'all (?P<ifc_class>.*) elements have an? (?P<property_path>.*\..*) property value matching the pattern "(?P<pattern>.*)"'
|
||||
)
|
||||
def step_impl(context, ifc_class, property_path, pattern):
|
||||
import re
|
||||
|
||||
pset_name, property_name = property_path.split(".")
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
prop = IfcFile.get_property(element, pset_name, property_name)
|
||||
if not prop:
|
||||
assert False
|
||||
# For now, we only check single values
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
if not (prop.NominalValue and re.search(pattern, prop.NominalValue.wrappedValue)):
|
||||
assert False
|
||||
@@ -0,0 +1,18 @@
|
||||
from behave import step
|
||||
|
||||
import attributes_psets_methods as apm
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
the_lang = "de"
|
||||
|
||||
|
||||
@step("An alle {ifc_class} Bauteile ist im PSet {pset} das Attribut {aproperty} angehängt")
|
||||
def step_impl(context, ifc_class, aproperty, pset):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
apm.eleclass_has_property_in_pset(
|
||||
context,
|
||||
ifc_class,
|
||||
aproperty,
|
||||
pset
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
from behave import step
|
||||
|
||||
import attributes_psets_methods as apm
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
the_lang = "fr"
|
||||
|
||||
|
||||
"""
|
||||
# TODO the next line needs translation
|
||||
@step("All {ifc_class} elements have an {aproperty} property in the {pset} pset")
|
||||
def step_impl(context, ifc_class, aproperty, pset):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
apm.eleclass_has_property_in_pset(
|
||||
context,
|
||||
ifc_class,
|
||||
aproperty,
|
||||
pset
|
||||
)
|
||||
"""
|
||||
@@ -0,0 +1,36 @@
|
||||
import gettext # noqa
|
||||
|
||||
from utils import assert_elements
|
||||
from utils import IfcFile
|
||||
|
||||
|
||||
def eleclass_has_property_in_pset(
|
||||
context, ifc_class, aproperty, pset
|
||||
):
|
||||
|
||||
context.falseelems = []
|
||||
context.falseguids = []
|
||||
context.falseprops = {}
|
||||
from ifcopenshell.util.element import get_psets
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for elem in elements:
|
||||
psets = get_psets(elem)
|
||||
if not (pset in psets and aproperty in psets[pset]):
|
||||
context.falseelems.append(str(elem))
|
||||
context.falseguids.append(elem.GlobalId)
|
||||
context.falseprops[elem.id()] = str(psets)
|
||||
|
||||
context.elemcount = len(elements)
|
||||
context.falsecount = len(context.falseelems)
|
||||
assert_elements(
|
||||
ifc_class,
|
||||
context.elemcount,
|
||||
context.falsecount,
|
||||
context.falseelems,
|
||||
message_all_falseelems=_("All {elemcount} {ifc_class} elements are missing the property {parameter} in the pset."),
|
||||
message_some_falseelems=_("The following {falsecount} of {elemcount} {ifc_class} elements are missing the property {parameter} in the pset: {falseelems}"),
|
||||
message_no_elems=_("There are no {ifc_class} elements in the IFC file."),
|
||||
parameter=aproperty
|
||||
)
|
||||
# the pset name is missing in the failing message, but it is in the step test name
|
||||
@@ -0,0 +1,19 @@
|
||||
from behave import step
|
||||
|
||||
from utils import IfcFile
|
||||
|
||||
|
||||
@step("all {ifc_class} elements have a {qto_name}.{quantity_name} quantity")
|
||||
def step_impl(context, ifc_class, qto_name, quantity_name):
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for element in elements:
|
||||
is_successful = False
|
||||
if not element.IsDefinedBy:
|
||||
assert False
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.RelatingPropertyDefinition.Name == qto_name:
|
||||
for quantity in relationship.RelatingPropertyDefinition.Quantities:
|
||||
if quantity.Name == quantity_name:
|
||||
is_successful = True
|
||||
if not is_successful:
|
||||
assert False
|
||||
@@ -0,0 +1,47 @@
|
||||
from behave import step
|
||||
|
||||
import geometric_detail_methods as gdm
|
||||
from utils import assert_elements
|
||||
from utils import IfcFile
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
the_lang = "en"
|
||||
|
||||
|
||||
@step("All elements must be under {number} polygons")
|
||||
def step_impl(context, number):
|
||||
number = int(number)
|
||||
errors = []
|
||||
for element in IfcFile.get().by_type("IfcElement"):
|
||||
if not element.Representation:
|
||||
continue
|
||||
total_polygons = 0
|
||||
tree = IfcFile.get().traverse(element.Representation)
|
||||
for e in tree:
|
||||
if e.is_a("IfcFace"):
|
||||
total_polygons += 1
|
||||
elif e.is_a("IfcPolygonalFaceSet"):
|
||||
total_polygons += len(e.Faces)
|
||||
elif e.is_a("IfcTriangulatedFaceSet"):
|
||||
total_polygons += len(e.CoordIndex)
|
||||
if total_polygons > number:
|
||||
errors.append((total_polygons, element))
|
||||
if errors:
|
||||
message = (
|
||||
"The following {} elements are over 500 polygons:\n"
|
||||
.format(len(errors))
|
||||
)
|
||||
for error in errors:
|
||||
message += "Polygons: {} - {}\n".format(error[0], error[1])
|
||||
assert False, message
|
||||
|
||||
|
||||
@step("all {ifc_class} elements have an {representation_class} representation")
|
||||
def step_impl(context, ifc_class, representation_class):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
gdm.eleclass_has_geometric_representation_of_specific_class(
|
||||
context,
|
||||
ifc_class,
|
||||
representation_class
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
from behave import step
|
||||
|
||||
import geometric_detail_methods as gdm
|
||||
from utils import switch_locale
|
||||
|
||||
|
||||
the_lang = "de"
|
||||
|
||||
|
||||
@step("Alle {ifc_class} Bauteile müssen eine geometrische Repräsentation der Klasse {representation_class} verwenden")
|
||||
def step_impl(context, ifc_class, representation_class):
|
||||
switch_locale(context.localedir, the_lang)
|
||||
gdm.eleclass_has_geometric_representation_of_specific_class(
|
||||
context,
|
||||
ifc_class,
|
||||
representation_class
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
import gettext # noqa
|
||||
from utils import assert_elements
|
||||
from utils import IfcFile
|
||||
|
||||
|
||||
def eleclass_has_geometric_representation_of_specific_class(
|
||||
context,
|
||||
ifc_class,
|
||||
representation_class
|
||||
):
|
||||
|
||||
def is_item_a_representation(item, representation):
|
||||
if "/" in representation:
|
||||
for cls in representation.split("/"):
|
||||
if item.is_a(cls):
|
||||
return True
|
||||
elif item.is_a(representation):
|
||||
return True
|
||||
|
||||
context.falseelems = []
|
||||
context.falseguids = []
|
||||
context.falseprops = {}
|
||||
rep = None
|
||||
|
||||
elements = IfcFile.get().by_type(ifc_class)
|
||||
for elem in elements:
|
||||
if not elem.Representation:
|
||||
continue
|
||||
has_representation = False
|
||||
for representation in elem.Representation.Representations:
|
||||
for item in representation.Items:
|
||||
if item.is_a("IfcMappedItem"):
|
||||
# We only check one more level deep.
|
||||
for item2 in item.MappingSource.MappedRepresentation.Items:
|
||||
if is_item_a_representation(item2, representation_class):
|
||||
has_representation = True
|
||||
rep = item2
|
||||
else:
|
||||
if is_item_a_representation(item, representation_class):
|
||||
has_representation = True
|
||||
rep = item
|
||||
if not has_representation:
|
||||
context.falseelems.append(str(elem))
|
||||
context.falseguids.append(elem.GlobalId)
|
||||
context.falseprops[elem.id()] = str(rep)
|
||||
|
||||
context.elemcount = len(elements)
|
||||
context.falsecount = len(context.falseelems)
|
||||
assert_elements(
|
||||
ifc_class,
|
||||
context.elemcount,
|
||||
context.falsecount,
|
||||
context.falseelems,
|
||||
message_all_falseelems=_("All {elemcount} {ifc_class} elements are not a {parameter} representation."),
|
||||
message_some_falseelems=_("The following {falsecount} of {elemcount} {ifc_class} elements are not a {parameter} representation: {falseelems}"),
|
||||
message_no_elems=_("There are no {ifc_class} elements in the IFC file."),
|
||||
parameter=representation_class
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
from behave import step
|
||||
|
||||
from utils import IfcFile
|
||||
|
||||
|
||||
@step("all buildings have an address")
|
||||
def step_impl(context):
|
||||
for building in IfcFile.get().by_type("IfcBuilding"):
|
||||
if not building.BuildingAddress:
|
||||
assert False, f'The building "{building.Name}" has no address.'
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from bimtester.guiwidget import run
|
||||
|
||||
|
||||
run()
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icon-css-de" viewBox="0 0 640 480">
|
||||
<path fill="#ffce00" d="M0 320h640v160H0z"/>
|
||||
<path d="M0 0h640v160H0z"/>
|
||||
<path fill="#d00" d="M0 160h640v160H0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icon-css-fr" viewBox="0 0 640 480">
|
||||
<g fill-rule="evenodd" stroke-width="1pt">
|
||||
<path fill="#fff" d="M0 0h640v480H0z"/>
|
||||
<path fill="#00267f" d="M0 0h213.3v480H0z"/>
|
||||
<path fill="#f31830" d="M426.7 0H640v480H426.7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 291 B |
|
After Width: | Height: | Size: 1.3 KiB |