mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-26 06:46:47 +00:00
Compare commits
839 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 491dc57098 | |||
| 19ed074b4a | |||
| 2cfe74ce09 | |||
| 7e089ab8b5 | |||
| 77e02ddae4 | |||
| b27482db46 | |||
| 3d2e99b5fa | |||
| 9a85fbc225 | |||
| 784c1724d5 | |||
| 17077c74a9 | |||
| 825a493d7a | |||
| 241c95e6df | |||
| c61bc29f06 | |||
| 90ac50f210 | |||
| a5cc0f1cef | |||
| a98ab72f67 | |||
| 62e4bf8fb3 | |||
| fd9ba0d243 | |||
| 9a9422bea6 | |||
| 0ae1c90797 | |||
| 8c1924084a | |||
| dafa304936 | |||
| e4fdfd2eb3 | |||
| bf1623975c | |||
| df49f1bc1b | |||
| acde878946 | |||
| d12cacbd2e | |||
| 5db91e52da | |||
| a4c2aa8f42 | |||
| 9b0be0fb4e | |||
| 3fdd92dc84 | |||
| 9f7a7a2c5b | |||
| 6bac067488 | |||
| 000d11daab | |||
| 3bef32bb87 | |||
| 9211f80625 | |||
| 910cdb3754 | |||
| 38f7ccdc20 | |||
| 40c2602b0e | |||
| 81f978f101 | |||
| 3e041a1fb2 | |||
| bd3118b4a3 | |||
| ef7eee411b | |||
| 944813146e | |||
| b1754f7b26 | |||
| 9d6eec0f5a | |||
| 9048ce4857 | |||
| dec0c646cc | |||
| 382efc0255 | |||
| bf50d0534b | |||
| 7022e842bf | |||
| 4936ea3ac3 | |||
| a2231605a5 | |||
| 161deeb4e0 | |||
| 99b865bbd9 | |||
| 1a1b7e93ab | |||
| f5af6ee6c0 | |||
| 94f49af00b | |||
| 39b8680f64 | |||
| 26282437cc | |||
| 6911b48a46 | |||
| 20471bd33e | |||
| bb15446822 | |||
| a0f37cf9ea | |||
| a8f4f0270a | |||
| 336601a7ae | |||
| d39c7bd641 | |||
| d3092090df | |||
| 327be7030f | |||
| 06589ca1c6 | |||
| 1c83b2db53 | |||
| 6f3eb84f66 | |||
| e7a8a97639 | |||
| fe1cb9e92b | |||
| f849434f24 | |||
| 8c0c216a1d | |||
| 747ddeb20c | |||
| 4afd104f3d | |||
| 4ed9166e8a | |||
| 64eb5b4d1d | |||
| 116de5daec | |||
| 0018224da7 | |||
| 334d867873 | |||
| 50e5d5f4d5 | |||
| e64d8750e1 | |||
| 342cce89d7 | |||
| ffa282af9d | |||
| fbc0215ef1 | |||
| 58759cf365 | |||
| 2024a24e31 | |||
| c420cc1abb | |||
| e39835d25e | |||
| 9c1660a84b | |||
| 7e9e56cd2c | |||
| a6d3619449 | |||
| dab3382b29 | |||
| 882d27fe25 | |||
| 81a9d0e747 | |||
| 3102a1c179 | |||
| a8effa8289 | |||
| 92f4d76c81 | |||
| d2337dc692 | |||
| 6b8681b74a | |||
| 297812d4e2 | |||
| 7205f0165e | |||
| bf4b5928dd | |||
| 3840bd1154 | |||
| 622853d73d | |||
| bef9bd25f7 | |||
| 34b3ca307f | |||
| 08557c2664 | |||
| a54b7863cd | |||
| 3101ccb83a | |||
| 394a7f14ad | |||
| 983745b508 | |||
| b05b62f440 | |||
| 51a9bb0ac1 | |||
| 452ab6e5bc | |||
| 0df2bfadef | |||
| 12623dd1e5 | |||
| 2eaae2bda8 | |||
| 229a708fc3 | |||
| d78c9171db | |||
| c2f4c2cf74 | |||
| dbc3577497 | |||
| 0a45a82109 | |||
| 07f5ac6994 | |||
| 0eb6c597f0 | |||
| b13c912c47 | |||
| 683cadeb76 | |||
| 891448e902 | |||
| 82718fad30 | |||
| 9f03a78652 | |||
| 505d234ea2 | |||
| bd5e25889a | |||
| ba5049a7eb | |||
| 40b70b1815 | |||
| 54d9edfee5 | |||
| 1c2587ba19 | |||
| 5359e88f0e | |||
| 6fdd2049a2 | |||
| 8428b93451 | |||
| 8e2a074d70 | |||
| 1b809ed849 | |||
| 8c0bcc4ce7 | |||
| 11d0c6ef56 | |||
| 4cfb2f1027 | |||
| 6d25047c6b | |||
| 136ec5b224 | |||
| 3ac8379187 | |||
| 6c1ad47481 | |||
| 20cd383504 | |||
| fd821d08cd | |||
| 6b772d18b8 | |||
| 33635c2c40 | |||
| 7e6b758450 | |||
| 127bcaf96e | |||
| 2d59f9d53a | |||
| 1014049b05 | |||
| 37460d3df9 | |||
| 637d3e2eb6 | |||
| 2ed2e9cf86 | |||
| c1635d65eb | |||
| 495ad75b14 | |||
| 966e57e94a | |||
| ef7a1ab421 | |||
| 089bb95c9f | |||
| 7786b89269 | |||
| 589f4bda2e | |||
| 8ffc12e6cd | |||
| bd9875fae5 | |||
| 7994bcbb80 | |||
| baa0211562 | |||
| a24333486a | |||
| 452ba651d0 | |||
| 161f24d804 | |||
| 6ee283dbae | |||
| c748c8dcea | |||
| 1f68d9d5bc | |||
| 120ff2f629 | |||
| 656346741e | |||
| e21277e80a | |||
| 9f55444d2e | |||
| 42c09deb8c | |||
| 93cca8f7b7 | |||
| 14be6b49fe | |||
| e87a1a0b32 | |||
| 27e335343e | |||
| 0920116178 | |||
| 1a7a4bdc3e | |||
| 5d0c04a077 | |||
| 8a316e45ed | |||
| 162b665d07 | |||
| 5bf7f53fd8 | |||
| e3a7e6137d | |||
| 01d261e168 | |||
| c4e56f670d | |||
| 4e28caaf8d | |||
| b7992d6a12 | |||
| efe765f71f | |||
| be5bd2e735 | |||
| ce782cc784 | |||
| 39b0d11746 | |||
| e06105e878 | |||
| 34e2ff31bb | |||
| c0a2e3395f | |||
| 7cd354ee7d | |||
| 95be422d4f | |||
| 111569ec1b | |||
| 9c88a35173 | |||
| 2647eacb74 | |||
| df942f6460 | |||
| 7de877823b | |||
| ea3318ae2e | |||
| fb37899b26 | |||
| 6463690a8b | |||
| 148432c011 | |||
| 478da8f658 | |||
| 9be413a308 | |||
| a9606b3044 | |||
| c419885329 | |||
| b6b41e920f | |||
| b7ce547a0c | |||
| 6cd59aa121 | |||
| 8439e5b46d | |||
| 403f64a0d0 | |||
| 6bffee83fe | |||
| f3e20ae450 | |||
| 352b14f07d | |||
| d7bcd00904 | |||
| afce8b37c4 | |||
| a2b575eec8 | |||
| eb87d50548 | |||
| f64246f7c5 | |||
| c5495d83e4 | |||
| 095801faa1 | |||
| 07954122fb | |||
| b347a488af | |||
| 0429e765b5 | |||
| 33220fa78c | |||
| effcecc75f | |||
| 85f872d3b8 | |||
| f3b3e2a441 | |||
| 25d68d320d | |||
| c38ac108cc | |||
| d14d63dd5c | |||
| 34d04c0da7 | |||
| 6c4fee1028 | |||
| ada503dbf1 | |||
| 8d0c95a939 | |||
| 7ba0b04031 | |||
| 688a652bc1 | |||
| d839238949 | |||
| f91ac2b375 | |||
| 8792a8d750 | |||
| f981c43042 | |||
| 8c26037f84 | |||
| 96ec925f31 | |||
| ee16deabe5 | |||
| 4f31b9467a | |||
| f6645b19ef | |||
| f2dbf7d063 | |||
| 49e5f2944c | |||
| fae9661efd | |||
| b90b39a894 | |||
| c7c5e692d3 | |||
| 93c2ca6267 | |||
| 430ab54602 | |||
| 08522c9fd7 | |||
| 475b8554a8 | |||
| 7bea054c1e | |||
| 490c35bf60 | |||
| b019ff6aed | |||
| 4c6ca83528 | |||
| 9ba5e8fd88 | |||
| 2e36793af2 | |||
| e3d2621ea9 | |||
| ebfe8f9c54 | |||
| 132e5d5f6e | |||
| 62794d4b07 | |||
| fd7f6e1f26 | |||
| b00acd3f67 | |||
| f12d8eeaa6 | |||
| 85524170dd | |||
| c67de4bbb3 | |||
| 36c52b3bea | |||
| ceff8a8822 | |||
| a58f23fae2 | |||
| b696df0da1 | |||
| 94224411a9 | |||
| 5c7e7cab1a | |||
| 29323e4f74 | |||
| aa0a6aa5d1 | |||
| d10efe69cd | |||
| fb1fed247e | |||
| 59d7e0cad3 | |||
| ae0afe2c0a | |||
| 3edb057178 | |||
| 04e3018f3c | |||
| f81616a6fb | |||
| f58539d6d8 | |||
| 90bdf03f54 | |||
| 6e87a21806 | |||
| d94eb9a96e | |||
| ac5800326b | |||
| 901ef57d60 | |||
| b604e5cf99 | |||
| 1e79eb535a | |||
| 6f7afd4b2a | |||
| a90936a7de | |||
| 7c1cf6ebd0 | |||
| 7eb1346407 | |||
| 771b1f3d3c | |||
| 3fb96cad0b | |||
| 23beb096a0 | |||
| 101ef69ae9 | |||
| e60202b738 | |||
| 1e8b1d9c9c | |||
| a27e516011 | |||
| 69c8b7c1c8 | |||
| c25cff6bad | |||
| 48a61219f7 | |||
| 84813084d0 | |||
| 5e0da9725a | |||
| 9a43658716 | |||
| f3f4c41f8d | |||
| 273c977f66 | |||
| 0adcb9ae20 | |||
| 1751cf1c95 | |||
| 1704862d0d | |||
| d7c275e941 | |||
| 1b9a28d799 | |||
| 68b432f6c0 | |||
| af2799acd4 | |||
| c093dc0335 | |||
| 834e05a01c | |||
| 4680298bee | |||
| be39f30029 | |||
| 3919c1f32e | |||
| e02bbefa68 | |||
| 5c497c8f6c | |||
| 942d34ee39 | |||
| 45ec08f840 | |||
| be3dbce9f4 | |||
| 20c189c321 | |||
| 97c5126a77 | |||
| ff64ce1e0e | |||
| 4f6e8a7d56 | |||
| 93ece6b3ea | |||
| ced9d8c917 | |||
| ef0e9d9eb4 | |||
| 3c2fbcc4e1 | |||
| a6ffa7fc42 | |||
| 0f825b7050 | |||
| 845dd13870 | |||
| d713b7e06a | |||
| e3e4fba63a | |||
| 78b904dfeb | |||
| 00c70fa8fb | |||
| d34c3b4b20 | |||
| 033075f809 | |||
| 1d8f6addf8 | |||
| 26a9167eea | |||
| 5fae7a5992 | |||
| 7edca081bf | |||
| 08e4d16f28 | |||
| 470e8e772e | |||
| ea609af394 | |||
| 8ac18752b2 | |||
| 95acd4c517 | |||
| 7ce795def3 | |||
| 8e4febecc0 | |||
| 195979d4f0 | |||
| ab0e47c55f | |||
| 9f362853d6 | |||
| 79e1e0ec04 | |||
| f446f25cee | |||
| a02b189f69 | |||
| 8312853b99 | |||
| be326e13ed | |||
| a2aa2bf9eb | |||
| b9e9bfeceb | |||
| dd8ee915c3 | |||
| 7fdbc9811d | |||
| 2edf867818 | |||
| e4a7d2a149 | |||
| 37f7fbecca | |||
| 0f993b32f4 | |||
| f71ee35f57 | |||
| 1856c499b7 | |||
| 06cad59fa2 | |||
| 8e69f84430 | |||
| 9e3fe84c66 | |||
| 9ad68db0dc | |||
| 40f3447ad1 | |||
| c8cc8ab1c8 | |||
| 4faa66f9b8 | |||
| 5705f24f3e | |||
| 52d035d09a | |||
| 921e343187 | |||
| 50ea73a28f | |||
| 3275119d1d | |||
| 026b4853a2 | |||
| cd132473f5 | |||
| 06c1ea9d2c | |||
| b058647d46 | |||
| 8e44290341 | |||
| 5d7b392415 | |||
| 0383221d00 | |||
| 678d422ec8 | |||
| eade74c919 | |||
| 9074f25e93 | |||
| 4b5790e83a | |||
| 470b08cd04 | |||
| d88c93a150 | |||
| 7101ac22c8 | |||
| 1b7d38a56d | |||
| de5864fc72 | |||
| 195d68cd0a | |||
| d8a11dd096 | |||
| 63b0488909 | |||
| 3abee03b9b | |||
| f1709dc71a | |||
| 6b2c8ad23e | |||
| 8ce4bbdf61 | |||
| e2cf5f4826 | |||
| 332a9a571f | |||
| ac9505506c | |||
| 257c28d450 | |||
| f95ad29c7a | |||
| 93c2f93c44 | |||
| 77d53429e3 | |||
| fa76aa9bb0 | |||
| f0e07f3447 | |||
| 80f0ff688d | |||
| 222cbe73d3 | |||
| ecff54c480 | |||
| 2d58a1bc7c | |||
| 5667f3f94a | |||
| 5d6160b47a | |||
| 37905c6725 | |||
| 99bde93af5 | |||
| e6ff78fa29 | |||
| c08aa3216d | |||
| da0b350378 | |||
| 4354361a13 | |||
| 51409aff8f | |||
| c94ed73b8d | |||
| 90e4f21f29 | |||
| 3ba2f781d8 | |||
| aec20057c7 | |||
| 5c107eb165 | |||
| 3d1ea31098 | |||
| 3d24b67146 | |||
| 18c87b515b | |||
| 11a95e7045 | |||
| 714a365942 | |||
| 739bbcc744 | |||
| 6a275d04b9 | |||
| e8ae491dc3 | |||
| 8e2f528a99 | |||
| 00215867c0 | |||
| 1e86a31b81 | |||
| f0c4ce5753 | |||
| ea514e9528 | |||
| 229b123798 | |||
| 86c9d7074b | |||
| bc000fcbab | |||
| a171b2b9b0 | |||
| 8a92e1a766 | |||
| efd6eb7f97 | |||
| 7ed98869d2 | |||
| 72d8b5377c | |||
| 8efd9757b7 | |||
| a0f5a422e2 | |||
| 8a950b1475 | |||
| d2ce8d70b1 | |||
| ac1a8f6d16 | |||
| 7883953290 | |||
| 64dc2ea95c | |||
| e064098f84 | |||
| 8dadb71648 | |||
| 9d757e1b3c | |||
| 92032b6e3b | |||
| 202b226ea0 | |||
| 1e8c7ef7f0 | |||
| 59035e7ce2 | |||
| 2c332199b1 | |||
| ecf67fe939 | |||
| f1099fc3b1 | |||
| 16e8fff9ca | |||
| 0f857283e8 | |||
| 1def5db7e4 | |||
| e9bd18fd4b | |||
| fb98275f50 | |||
| 97a62f45d0 | |||
| 4ebee68736 | |||
| a26253321f | |||
| 4b90e618b0 | |||
| 21b35767ba | |||
| e6fbfee766 | |||
| 3cba5e6e94 | |||
| 6d6e7c2982 | |||
| fd4086aa8d | |||
| cd2b0c3a05 | |||
| 42067de44c | |||
| 240133515a | |||
| b7f23ee701 | |||
| 4a85e5e7a5 | |||
| 3a507ca061 | |||
| 6bcabfd567 | |||
| 435eb7ae10 | |||
| 17e153d0c5 | |||
| aebb9e324b | |||
| 90ad3a4a93 | |||
| 478bda30bc | |||
| 69535800b5 | |||
| ee2303d965 | |||
| b792aba95f | |||
| 8f55aa732d | |||
| 2c2be389fb | |||
| 62a0f21a57 | |||
| 3c418e8e0a | |||
| ea6fbc77dd | |||
| be10bc46ca | |||
| f4fd3a5ca1 | |||
| 685717b8b8 | |||
| 8725c66306 | |||
| cbbdb61a0c | |||
| b3167cf1e6 | |||
| 44f785cce0 | |||
| 8e998ec7db | |||
| fc50e6ac23 | |||
| 815ed07192 | |||
| 009870135f | |||
| dbc59fe346 | |||
| 08aeb80f58 | |||
| 7f2729c956 | |||
| 4d6e25fb69 | |||
| 37aadcbfbf | |||
| 74a3f131be | |||
| 77b72a9dde | |||
| c644ffbe5e | |||
| aa03da20a8 | |||
| 42acc7224c | |||
| 53fadd9808 | |||
| 4afc568a41 | |||
| 906fefaf35 | |||
| 90a2c445ea | |||
| cae24a9492 | |||
| 8b3d581131 | |||
| c13eb9e131 | |||
| 3b169219af | |||
| 83243ec514 | |||
| b5c4eb3b6f | |||
| a298903fdd | |||
| ccb24948df | |||
| 5bdb4ce668 | |||
| 9909e2f7e2 | |||
| 38d1ec7a78 | |||
| 85ddae1228 | |||
| 06c3f8d6a9 | |||
| b04ca74308 | |||
| 7c0944e43b | |||
| a1c660f4ab | |||
| 2a5974fde1 | |||
| e2ffd0b6d6 | |||
| 9bff4876c5 | |||
| 42188904af | |||
| 414d7bb810 | |||
| 01269e5dd5 | |||
| 0253c1ddbe | |||
| 3f5d30f497 | |||
| 3d2ab25175 | |||
| d4dc933b99 | |||
| d54c1cdc9c | |||
| c848a7dbe0 | |||
| 60cf4ab757 | |||
| 2c3fa89dd0 | |||
| e9c7cf235b | |||
| fefa8a0563 | |||
| 04acfe1169 | |||
| 0e25787df3 | |||
| 3c3bee71d1 | |||
| 37f0e09947 | |||
| 39e26dff46 | |||
| 2f129556fd | |||
| a1d584796a | |||
| c6d5adfa42 | |||
| 1e262c8768 | |||
| d8919c50ab | |||
| 416b163050 | |||
| 1d1fb6d27a | |||
| 9fedbbff6d | |||
| 1c1097c8a4 | |||
| 884b3fe850 | |||
| 81ee60079e | |||
| 426b1279e3 | |||
| 1142db3265 | |||
| 191c029546 | |||
| 9bcfe2f50e | |||
| fa585e60ee | |||
| 0ff9de3cd9 | |||
| 63c3b54762 | |||
| d7f990d8bd | |||
| b59e8e22a7 | |||
| 25a5135e8d | |||
| 4c574df1a4 | |||
| f3d5954aee | |||
| 7d145a5f69 | |||
| 5b30dfc580 | |||
| e1bb6c95c3 | |||
| edc6f8ebd4 | |||
| ee56aad90d | |||
| 5c1ac39f19 | |||
| 0fd2f04f08 | |||
| b171bd4a14 | |||
| 54a9d72e25 | |||
| 798597d5d9 | |||
| f7453c033a | |||
| bf76bd17b2 | |||
| 59d2d227d9 | |||
| 4a3a0bff71 | |||
| 8ecc1269d7 | |||
| 614148e5e4 | |||
| 74e495cec0 | |||
| 0ceef8fe67 | |||
| 562f38756f | |||
| 76d3823bd4 | |||
| 28560d164c | |||
| 923f1212f5 | |||
| e9f551ea71 | |||
| b9c5b0e6c8 | |||
| 4953b35849 | |||
| 83e5096570 | |||
| 5008c3503d | |||
| ce38854cff | |||
| 24e2aece0b | |||
| 43f5d446ec | |||
| 50a467fda5 | |||
| 0da0e63687 | |||
| e35cefac52 | |||
| 84e7a86e77 | |||
| 82a1e33386 | |||
| 827c6fd407 | |||
| 851fadb4ec | |||
| 90f72c4b2c | |||
| 7ed219af70 | |||
| d1a1759016 | |||
| acc4af87d6 | |||
| ee75545ecd | |||
| 58b5838da9 | |||
| e171e308d0 | |||
| 645315a8f4 | |||
| 5ee9bed590 | |||
| 0f88520e75 | |||
| 481515f5ae | |||
| a33f5b6e17 | |||
| 10bec3eee0 | |||
| da0af77570 | |||
| a6094f50b9 | |||
| c3f1b41860 | |||
| 4122759f1a | |||
| a583339492 | |||
| f12e980bfe | |||
| 4979deb111 | |||
| c575352d20 | |||
| 2f8fa3282b | |||
| 993e322a8e | |||
| 61ccd8a7cf | |||
| 450baf224f | |||
| 7b2197fa37 | |||
| cde1c5f2ee | |||
| 33772a4710 | |||
| 178c34fb22 | |||
| 17ecc43f78 | |||
| 72999e04d9 | |||
| f191b1fdd2 | |||
| d095bfecde | |||
| 2e1699beea | |||
| 5c07c8dc95 | |||
| 0fa05faee8 | |||
| 21e8a1e8e6 | |||
| 2e6601b3e7 | |||
| 634932e6e7 | |||
| a041d289c6 | |||
| 01f8882311 | |||
| f2bdfbd049 | |||
| c7bf03e8f8 | |||
| 18e451b874 | |||
| f4d58b3969 | |||
| 76a3146b70 | |||
| fe70acb541 | |||
| a52901344c | |||
| d0e26f07fc | |||
| 5270710fc2 | |||
| ed8cc620ea | |||
| 9b13ed3f38 | |||
| 801c7da40f | |||
| 4be7da1a43 | |||
| a0db3a3406 | |||
| 18f8d7b3a3 | |||
| 010e05308d | |||
| 973fa9ccb5 | |||
| 0d42a293ad | |||
| a02a06a0e3 | |||
| d3438335a2 | |||
| a0bb589ed3 | |||
| 056bcd12d0 | |||
| fe699e0a81 | |||
| 4eee9a1936 | |||
| 8c1710cae1 | |||
| e2d3b3734f | |||
| b243572d23 | |||
| 3224a2cf83 | |||
| a2d91f9331 | |||
| 0301e2fb1b | |||
| 0036bd2dc2 | |||
| 0877bb0ac3 | |||
| e62ce005dc | |||
| b6e2d78bfa | |||
| bf292cccd0 | |||
| 9d730054dd | |||
| 2dc5cd44b7 | |||
| fcbe93c4c2 | |||
| 89b3b96f1e | |||
| 16a82f035f | |||
| b562508f5b | |||
| a13042f227 | |||
| 3cbb47a831 | |||
| 7d806296c1 | |||
| d6bd0c861b | |||
| 8164c90ba0 | |||
| fff6f97651 | |||
| 6a5d5dcb12 | |||
| bb3dc351b8 | |||
| 90946e0877 | |||
| 7895835ca3 | |||
| 7caa4e3e1d | |||
| d721dabf3b | |||
| 9087bc65a7 | |||
| ccf4e84e18 | |||
| 0f1db1267b | |||
| 431e993ba3 | |||
| d912e73282 | |||
| 106da12444 | |||
| 89a015724b | |||
| 8b63959509 | |||
| 0a8791e04e | |||
| 318779196b | |||
| 6190771817 | |||
| 206a9cf5d0 | |||
| d4b51a82e7 | |||
| a749775045 | |||
| 26ed16ba52 | |||
| 7b4d8734ed | |||
| 455d382a9e | |||
| c9313c8e02 | |||
| b44a88d203 | |||
| 921c3eb2d3 | |||
| d5efe45b0b | |||
| b8a7b5426d | |||
| 89bea19b75 | |||
| 5c0eb05482 | |||
| c9ff455812 | |||
| fdf1b29682 | |||
| f3af39546b | |||
| 44ddc5e6dc | |||
| c9db70c7c3 | |||
| c7ddc0643e | |||
| 3a9e90f94f | |||
| db6630c1f8 | |||
| c20afd24a5 | |||
| 7b82bfb477 | |||
| 3e00139a87 | |||
| c6a265adf8 | |||
| 94d214808e | |||
| a58b2ede63 | |||
| e5c20a009d | |||
| b590747db9 | |||
| 2532ba30e4 | |||
| 9096895bdb | |||
| 82143f226e | |||
| 22a8d9a17b | |||
| 4385f3b14d | |||
| 3cba242295 | |||
| 8406c5243c | |||
| 810d3eab1e | |||
| 17f6e67706 | |||
| f5c32adff1 | |||
| bb43dec90f | |||
| 0e5edbcd84 | |||
| 68fc361d96 | |||
| 06b4fcb066 | |||
| 85d5d86c84 | |||
| 78f4d7d592 | |||
| 92bc7b13cb | |||
| e518146bd6 | |||
| e9eb7db620 | |||
| 1068070037 | |||
| 3d302619c5 | |||
| 48febb7eff | |||
| feaa13a809 | |||
| 76cbb79c75 | |||
| 6aabbd4a55 | |||
| 48676a733f | |||
| f8c45b2a99 | |||
| 1ec14118fe | |||
| 381f711103 | |||
| b748c0a573 | |||
| c4e8a409de | |||
| 28863f90b9 | |||
| 4f60c7d48c | |||
| f70b37137d | |||
| 49d491704e | |||
| a3f17cd4e7 | |||
| b96cf38e17 | |||
| 5a994ebe28 | |||
| ffc008228d | |||
| 77ba3f5a80 | |||
| 04d4095ffe | |||
| 569fa49796 | |||
| 7abe2df066 | |||
| 9b64c6f4b9 | |||
| 771f78a671 | |||
| 3d6b330366 | |||
| 37d4e9bcbd | |||
| 7390faa654 | |||
| b7d4eb62d5 | |||
| 40a60386c8 | |||
| 9bf9f82b3b | |||
| b37a684eb9 | |||
| c97cf661a2 | |||
| 53f625346f | |||
| 608c7d9841 | |||
| 955190b517 | |||
| 462425f61d | |||
| 0a0f44b674 | |||
| 9f9a0ea3dc |
+14
@@ -0,0 +1,14 @@
|
||||
# Dependency and build folders created by the build scripts
|
||||
/deps*/
|
||||
/build*/
|
||||
/install*/
|
||||
/win/BuildDepsCache*.txt
|
||||
# IfcExpressParser residue
|
||||
/src/ifcexpressparser/express_parser.py
|
||||
# General Python residue
|
||||
__pycache__
|
||||
*.py.bak
|
||||
# Visual Studio Code files
|
||||
.vscode
|
||||
# PyCharm files
|
||||
.idea
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "test/input"]
|
||||
path = test/input
|
||||
url = https://github.com/IfcOpenShell/files
|
||||
ignore = dirty
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
|
||||
|
||||
install:
|
||||
- sudo apt-get install -qq gcc-4.8 g++-4.8
|
||||
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90
|
||||
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
|
||||
|
||||
- sudo apt-get install -y libboost1.55-dev
|
||||
- sudo apt-get install -y libboost-regex1.55-dev
|
||||
- sudo apt-get install -y libboost-system1.55-dev
|
||||
- sudo apt-get install -y libboost-thread1.55-dev
|
||||
- sudo apt-get install -y libboost-program-options1.55-dev
|
||||
- sudo apt-get install -y cmake
|
||||
- sudo apt-get install -y libicu-dev
|
||||
- sudo apt-get install -y python-all-dev
|
||||
- sudo apt-get install -y swig
|
||||
- sudo apt-get install -y liboce-foundation-dev
|
||||
- sudo apt-get install -y liboce-modeling-dev
|
||||
- sudo apt-get install -y liboce-ocaf-dev
|
||||
- sudo apt-get install -y liboce-visualization-dev
|
||||
- sudo apt-get install -y liboce-ocaf-lite-dev
|
||||
|
||||
- sudo apt-get install -y libpcre3-dev
|
||||
|
||||
script:
|
||||
- pwd
|
||||
- cd ..
|
||||
- git clone https://github.com/KhronosGroup/OpenCOLLADA.git
|
||||
- cd OpenCOLLADA
|
||||
- git checkout 064a60b65c2c31b94f013820856bc84fb1937cc6
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- make
|
||||
- sudo make install
|
||||
- cd ..
|
||||
- cd ..
|
||||
- pwd
|
||||
- cd IfcOpenShell
|
||||
- pwd
|
||||
- cd cmake
|
||||
- mkdir build-ifc2x3 build-ifc4
|
||||
- cd build-ifc2x3
|
||||
- cmake -DCOLLADA_SUPPORT=True -DOPENCOLLADA_INCLUDE_DIR=/usr/local/include/opencollada -DOPENCOLLADA_LIBRARY_DIR=/usr/local/lib/opencollada -DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DUSE_IFC4=False -DBUILD_IFCPYTHON=True -DUNICODE_SUPPORT=True -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so -DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_EXECUTABLE=/usr/bin/python2.7 ..
|
||||
- make -j
|
||||
- sudo make install
|
||||
- ./examples/IfcOpenHouse
|
||||
- ./examples/IfcAdvancedHouse
|
||||
- test -f IfcOpenHouse.ifc
|
||||
- test -f IfcAdvancedHouse.ifc
|
||||
- cd ../../test
|
||||
- /usr/bin/python2.7 tests.py
|
||||
- cd ../cmake/build-ifc4
|
||||
- cmake -DCOLLADA_SUPPORT=True -DOPENCOLLADA_INCLUDE_DIR=/usr/local/include/opencollada -DOPENCOLLADA_LIBRARY_DIR=/usr/local/lib/opencollada -DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DUSE_IFC4=True -DBUILD_IFCPYTHON=True -DUNICODE_SUPPORT=True -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu -DPYTHON_LIBRARY=/usr/lib/python2.7/config-x86_64-linux-gnu/libpython2.7.so -DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_EXECUTABLE=/usr/bin/python2.7 ..
|
||||
- make -j
|
||||
- ./examples/IfcOpenHouse
|
||||
- ./examples/IfcAdvancedHouse
|
||||
- test -f IfcOpenHouse.ifc
|
||||
- test -f IfcAdvancedHouse.ifc
|
||||
@@ -1,65 +0,0 @@
|
||||
IfcOpenShell
|
||||
============
|
||||
open source (LGPL) software library for working with the IFC file format
|
||||
|
||||
|
||||
http://IfcOpenShell.org
|
||||
|
||||
|
||||
Compiling on Windows
|
||||
====================
|
||||
Users are advised to use the Visual Studio .sln file in the win/ folder.
|
||||
For Windows users a prebuilt Open CASCADE version is available from the
|
||||
http://opencascade.org website. Download and install this version and
|
||||
provide the paths to the Open CASCADE header and library files to MS
|
||||
Visual Studio C++.
|
||||
|
||||
For building the Autodesk 3ds Max plugin, the 3ds Max SDK needs to be
|
||||
installed as well as 3ds Max itself. Please provide the include and
|
||||
library paths to Visual Studio.
|
||||
|
||||
For building the IfcPython wrapper, SWIG needs to be installed. Please
|
||||
download the latest swigwin version from http://www.swig.org/download.html.
|
||||
After extracting the .zip file, please add the extracted folder to the PATH
|
||||
environment variable. Python needs to be installed, please provide the
|
||||
include and library paths to Visual Studio.
|
||||
|
||||
|
||||
|
||||
Compiling on *nix
|
||||
====================
|
||||
Users are advised to build IfcOpenShell using the cmake file provided in
|
||||
the cmake/ folder. There might be an Open CASCADE package in your operating
|
||||
system's software repository. If not, you will need to compile Open
|
||||
CASCADE yourself. See http://opencascade.org.
|
||||
|
||||
For building the IfcPython wrapper, SWIG and Python development are
|
||||
required.
|
||||
|
||||
To build IfcOpenShell please take the following steps:
|
||||
$ cd /path/to/IfcOpenShell/cmake
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
Optionally:
|
||||
$ OCC_INCLUDE_PATH="/path/to/OpenCASCADE/include"
|
||||
$ OCC_LIBRARY_PATH="/path/to/OpenCASCADE/lib"
|
||||
$ export OCC_INCLUDE_PATH
|
||||
$ export OCC_LIBRARY_PATH
|
||||
$ cmake ../
|
||||
$ make
|
||||
|
||||
If all worked out correctly you can now use IfcOpenShell. For example:
|
||||
$ wget ftp://ftp.dds.no/pub/ifc/Munkerud/Munkerud_hus6_BE.zip
|
||||
$ unzip Munkerud_hus6_BE.zip
|
||||
$ ./IfcObj Munkerud_hus6_BE.ifc
|
||||
$ less Munkerud_hus6_BE.obj
|
||||
Or:
|
||||
$ wget ftp://ftp.dds.no/pub/ifc/Munkerud/Munkerud_hus6_BE.zip
|
||||
$ unzip Munkerud_hus6_BE.zip
|
||||
$ python
|
||||
>>> import IfcImport
|
||||
>>> IfcImport.Init('Munkerud_hus6_BE.ifc')
|
||||
>>> geom = IfcImport.Get()
|
||||
>>> geom.name
|
||||
>>> for v in geom.mesh.verts: v
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
IfcOpenShell
|
||||
============
|
||||
IfcOpenShell is an open source ([LGPL]) software library for working with the Industry Foundation Classes ([IFC])
|
||||
file format. Currently supported IFC releases are [IFC2x3 TC1] and [IFC4 Add2 TC1].
|
||||
|
||||
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)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
* Git
|
||||
* CMake (2.6 or newer)
|
||||
* Windows: [Visual Studio] 2008 or newer with C++ toolset (or [Visual C++ Build Tools]) or [MSYS2] + MinGW
|
||||
* *nix: GCC 4.7 or newer, or Clang (any version)
|
||||
|
||||
Dependencies
|
||||
-------------
|
||||
* [Boost](http://www.boost.org/)
|
||||
* [Open Cascade](http://opencascade.org) - *optional*, but required for building IfcGeom
|
||||
([official](http://www.opencascade.org/getocc/download/loadocc/), "OCCT", or [community edition](https://github.com/tpaviot/oce), "OCE")
|
||||
For converting IFC representation items into BRep solids and tesselated meshes
|
||||
* [ICU](http://site.icu-project.org/) - *optional*
|
||||
For handling code pages and Unicode in the parser
|
||||
* [OpenCOLLADA](https://github.com/khronosGroup/OpenCOLLADA/) - *optional*
|
||||
For IfcConvert to be able to write tessellated Collada (.dae) files
|
||||
* [SWIG](http://www.swig.org/) and [Python](https://www.python.org/) - *optional*
|
||||
For building the IfcOpenShell Python interface and the Blender add-on
|
||||
* [3ds Max SDK](http://www.autodesk.com/products/3ds-max/free-trial) - *optional*
|
||||
For building the 3ds Max plug-in.
|
||||
All recent versions of 3ds Max (2014 and newer) are 64-bit only, so a 64-bit installation is assumed.
|
||||
|
||||
Building IfcOpenShell
|
||||
---------------------
|
||||
|
||||
**Note:** The path where the source code is cloned to can contain spaces but non-ASCII characters are very likely to cause problems with the build.
|
||||
|
||||
### Compiling on Windows
|
||||
The preferred way to fetch and build this project's dependencies is to use the build scripts
|
||||
in win/ folder. **See [win/readme.md] for more information**.
|
||||
|
||||
#### Using Visual Studio
|
||||
Instructions in a nutshell (**assuming Visual Studio 2015 x64 environment variables set**):
|
||||
|
||||
> cd IfcOpenShell\win
|
||||
> build-deps.cmd
|
||||
> run-cmake.bat
|
||||
|
||||
You can now open and build the solution file in Visual Studio:
|
||||
|
||||
> ..\build-vs2015-x64\IfcOpenShell.sln
|
||||
|
||||
As the scripts default to using the `RelWithDebInfo` configuration, and a freshly created solution by CMake defaults
|
||||
to `Debug`, make sure to switch the used build configuration. Build the `INSTALL` project (right-click -> Project
|
||||
Only) to deploy the headers and binaries into a single location if wanted/needed.
|
||||
|
||||
Alternatively, one can use the utility batch file(s) to build and install the project easily from the command-line
|
||||
(installing a project will build it also, if required):
|
||||
|
||||
> install-ifcopenshell.bat
|
||||
|
||||
#### Using MSYS2 + MinGW
|
||||
|
||||
Start the MSYS2 Shell and then:
|
||||
|
||||
$ cd IfcOpenShell/win
|
||||
$ ./build-deps.sh
|
||||
$ ./run-cmake.sh
|
||||
$ ./install-ifcopenshell.sh
|
||||
|
||||
#### Using Bash on Ubuntu on Windows
|
||||
|
||||
Start Bash on Ubuntu on Windows and follow the instructions below. Compiling on Ubuntu 14.04.4 LTS using GCC 4.8.4
|
||||
or Clang 3.5 has been confirmed to work.
|
||||
|
||||
### Compiling on *nix
|
||||
|
||||
The following instructions are for Ubuntu, modify as required for other operating systems. [nix/build-all.py] script
|
||||
can be experimented with and studied for pointers for other operating systems, but note that this script is not currently
|
||||
meant to be used for a typical IfcOpenShell workspace setup.
|
||||
|
||||
Note: where `make -j` is written, add a number roughly equal to the amount of CPU cores + 1.
|
||||
|
||||
**1)** Install most of the prerequisites and dependencies:
|
||||
|
||||
$ sudo apt-get install git cmake gcc g++ libboost-all-dev libicu-dev
|
||||
|
||||
**2a)** Either use an OCE package from your operating system's software repository
|
||||
|
||||
$ sudo apt-get install liboce-foundation-dev liboce-modeling-dev liboce-ocaf-dev liboce-visualization-dev liboce-ocaf-lite-dev
|
||||
|
||||
**2b)** or (if not available, or the latest code is wanted) compile OCE yourself (note that the build takes a long time):
|
||||
|
||||
$ sudo apt-get install libftgl-dev libtbb2 libtbb-dev libgl1-mesa-dev libfreetype6-dev
|
||||
$ git clone https://github.com/tpaviot/oce.git
|
||||
$ cd oce
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make -j
|
||||
$ sudo make install
|
||||
|
||||
**2c)** or obtain and compile OCCT from http://www.opencascade.org/getocc/download/loadocc/
|
||||
|
||||
**3)** For building IfcConvert with COLLADA (.dae) support (on by default), OpenCOLLADA is needed:
|
||||
|
||||
$ sudo apt-get install libpcre3-dev libxml2-dev
|
||||
$ git clone https://github.com/KhronosGroup/OpenCOLLADA.git
|
||||
$ cd OpenCOLLADA
|
||||
Using a known good revision, but HEAD should work too:
|
||||
$ git checkout 064a60b65c2c31b94f013820856bc84fb1937cc6
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make -j
|
||||
$ sudo make install
|
||||
|
||||
**4)** For building the IfcPython wrapper (on by default), SWIG and Python development are needed, if not already available:
|
||||
|
||||
$ sudo apt-get install python-all-dev swig
|
||||
|
||||
**5)** To build IfcOpenShell please take the following steps. Alternatively use environment variables for setting the
|
||||
dependencies' paths. `OCC_INCLUDE_DIR` might be needed to set also. `OPENCOLLADA_INCLUDE_DIR` and `OPENCOLLADA_LIBRARY_DIR`
|
||||
(and potentially `PCRE_LIBRARY_DIR`) are needed if building with COLLADA support. (`-DCOLLADA_SUPPORT=0` disables it).
|
||||
|
||||
$ cd /path/to/IfcOpenShell
|
||||
$ mkdir build && cd build
|
||||
$ cmake ../cmake -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
|
||||
-DOPENCOLLADA_INCLUDE_DIR="/usr/local/include/opencollada" \
|
||||
-DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \
|
||||
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/
|
||||
$ make -j
|
||||
|
||||
If all worked out correctly you can now use IfcOpenShell. See the examples below.
|
||||
|
||||
**6)** Install the project if wanted:
|
||||
|
||||
$ sudo make install
|
||||
|
||||
Installing IfcOpenShell with Conda
|
||||
----------------------------------
|
||||
Another option for building and installing IfcOpenShell is to use the popular
|
||||
[Anaconda Python Distribution](https://www.anaconda.com/download).
|
||||
The requirements are spread across a number of channels.
|
||||
You can add these channels to your configuration, or specify them all on the command line:
|
||||
|
||||
$ conda install -c conda-forge -c oce -c dlr-sc -c ifcopenshell ifcopenshell
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
|
||||
**Invoking IfcConvert from the command line**
|
||||
|
||||
$ wget ftp://ftp.dds.no/pub/ifc/Munkerud/Munkerud_hus6_BE.zip
|
||||
$ unzip Munkerud_hus6_BE.zip
|
||||
$ ./IfcConvert Munkerud_hus6_BE.ifc
|
||||
$ less Munkerud_hus6_BE.obj
|
||||
|
||||
**Using the IfcOpenShell Python interface**
|
||||
|
||||
$ wget -O duplex.zip http://projects.buildingsmartalliance.org/files/?artifact_id=4278
|
||||
$ unzip duplex.zip
|
||||
$ python
|
||||
>>> import ifcopenshell
|
||||
>>> f = ifcopenshell.open("Duplex_A_20110907_optimized.ifc")
|
||||
>>>
|
||||
>>> # Accessing entity instances by type:
|
||||
>>> f.by_type("ifcwall")[:2]
|
||||
[#91=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FL9r',#1,'Basic Wall:Interior - Partition (92mm Stud):144586',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5198,#18806,'144586'), #92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')]
|
||||
>>> wall = _[0]
|
||||
>>> len(wall) # number of EXPRESS attributes
|
||||
8
|
||||
>>>
|
||||
>>> # Accessing EXPRESS attributes by name:
|
||||
>>> wall.GlobalId
|
||||
'2O2Fr$t4X7Zf8NOew3FL9r'
|
||||
>>> wall.Name = "My wall"
|
||||
>>> wall.NonExistingAttr
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File ".\ifcopenshell.py", line 14, in __getattr__
|
||||
except: raise AttributeError("entity instance of type '%s' has no attribute'%s'"%(self.wrapped_data.is_a(), name)) from None
|
||||
AttributeError: entity instance of type 'IfcWallStandardCase' has no attribute 'NonExistingAttr'
|
||||
>>> wall.GlobalId = 3
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File ".\ifcopenshell.py", line 26, in __setattr__
|
||||
self[self.wrapped_data.get_argument_index(key)] = value
|
||||
File ".\ifcopenshell.py", line 30, in __setitem__
|
||||
self.wrapped_data.set_argument(idx, entity_instance.map_value(value))
|
||||
File ".\ifc_wrapper.py", line 118, in <lambda>
|
||||
set_argument = lambda self,x,y: self._set_argument(x) if y is None else self
|
||||
._set_argument(x,y)
|
||||
File ".\ifc_wrapper.py", line 114, in _set_argument
|
||||
def _set_argument(self, *args): return _ifc_wrapper.entity_instance__set_argument(self, *args)
|
||||
RuntimeError: INT is not a valid type for 'GlobalId'
|
||||
>>> # Creating new entity instances
|
||||
>>> f.createIfcCartesianPoint(Coordinates=(1.0,1.5,2.0))
|
||||
#27530=IfcCartesianPoint((1.,1.5,2.))
|
||||
>>>
|
||||
>>> # Working with GlobalId attributes:
|
||||
>>> import uuid
|
||||
>>> ifcopenshell.guid.compress(uuid.uuid1().hex)
|
||||
'3x4C8Q_6qHuv$P$FYkANRX'
|
||||
>>> new_guid = _
|
||||
>>> owner_hist = f.by_type("IfcOwnerHistory")[0]
|
||||
>>> new_wall = f.createIfcWallStandardCase(new_guid, owner_hist, None, None, Tag='my_tag')
|
||||
>>> new_wall.ObjectType = ''
|
||||
>>> new_wall.ObjectPlacement = new_wall.Representation = None
|
||||
>>>
|
||||
>>> # Accessing entity instances by instance id or GlobalId:
|
||||
>>> f[92]
|
||||
#92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')
|
||||
>>> f['2O2Fr$t4X7Zf8NOew3FLIE']
|
||||
#92=IfcWallStandardCase('2O2Fr$t4X7Zf8NOew3FLIE',#1,'Basic Wall:Interior - Partition (92mm Stud):143921',$,'Basic Wall:Interior - Partition (92mm Stud):128360',#5206,#18805,'143921')
|
||||
>>>
|
||||
>>> # Writing IFC-SPF files to disk:
|
||||
>>> 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"
|
||||
[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"
|
||||
[win/readme.md]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/win/readme.md "win/readme.md"
|
||||
[nix/build-all.py]: https://github.com/IfcOpenShell/IfcOpenShell/tree/master/nix/build-all.py "nix/build-all.py"
|
||||
+601
-188
@@ -1,235 +1,648 @@
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
################################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required (VERSION 2.8.5)
|
||||
|
||||
project (IfcOpenShell)
|
||||
|
||||
FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options)
|
||||
OPTION(UNICODE_SUPPORT "Build IfcOpenShell with Unicode support (requires ICU)." ON)
|
||||
OPTION(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON)
|
||||
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(USE_IFC4 "Use IFC 4 instead of IFC 2x3 (full rebuild recommended when switching this)" OFF)
|
||||
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
|
||||
OPTION(BUILD_EXAMPLES "Build example applications." ON)
|
||||
OPTION(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OFF)
|
||||
OPTION(USE_MMAP "Adds a command line options to parse IFC files from memory mapped files using Boost.Iostreams" OFF)
|
||||
OPTION(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." OFF)
|
||||
OPTION(BUILD_SHARED_LIBS "Build IfcParse and IfcGeom as shared libs (SO/DLL)." 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)
|
||||
|
||||
|
||||
# Specify where to install files
|
||||
IF(NOT BINDIR)
|
||||
set(BINDIR bin)
|
||||
ENDIF()
|
||||
IF(NOT IS_ABSOLUTE ${BINDIR})
|
||||
set(BINDIR ${CMAKE_INSTALL_PREFIX}/${BINDIR})
|
||||
ENDIF()
|
||||
MESSAGE(STATUS "BINDIR: ${BINDIR}")
|
||||
|
||||
IF(NOT INCLUDEDIR)
|
||||
set(INCLUDEDIR include)
|
||||
ENDIF()
|
||||
IF(NOT IS_ABSOLUTE ${INCLUDEDIR})
|
||||
set(INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${INCLUDEDIR})
|
||||
ENDIF()
|
||||
MESSAGE(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
|
||||
|
||||
IF(NOT LIBDIR)
|
||||
set(LIBDIR lib)
|
||||
ENDIF()
|
||||
IF(NOT IS_ABSOLUTE ${LIBDIR})
|
||||
set(LIBDIR ${CMAKE_INSTALL_PREFIX}/${LIBDIR})
|
||||
ENDIF()
|
||||
MESSAGE(STATUS "LIBDIR: ${LIBDIR}")
|
||||
|
||||
set(IFCOPENSHELL_LIBARY_DIR "") # for *nix rpaths
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
add_definitions(-DIFC_SHARED_BUILD)
|
||||
if (MSVC)
|
||||
message(WARNING "Building DLLs against the static VC run-time. This is not recommended if the DLLs are to be redistributed.")
|
||||
# C4521: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
|
||||
add_definitions(-wd4251)
|
||||
endif()
|
||||
set(IFCOPENSHELL_LIBARY_DIR "${LIBDIR}")
|
||||
endif()
|
||||
|
||||
# Create cache entries if absent for environment variables
|
||||
MACRO(UNIFY_ENVVARS_AND_CACHE VAR)
|
||||
IF ((NOT DEFINED ${VAR}) AND (NOT "$ENV{${VAR}}" STREQUAL ""))
|
||||
SET(${VAR} "$ENV{${VAR}}" CACHE STRING "${VAR}" FORCE)
|
||||
ENDIF()
|
||||
ENDMACRO()
|
||||
|
||||
UNIFY_ENVVARS_AND_CACHE(OCC_INCLUDE_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(OCC_LIBRARY_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(ICU_INCLUDE_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(ICU_LIBRARY_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(OPENCOLLADA_INCLUDE_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(OPENCOLLADA_LIBRARY_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(PCRE_LIBRARY_DIR)
|
||||
UNIFY_ENVVARS_AND_CACHE(PYTHON_EXECUTABLE)
|
||||
IF(WIN32)
|
||||
UNIFY_ENVVARS_AND_CACHE(THREEDS_MAX_SDK_HOME)
|
||||
ENDIF()
|
||||
|
||||
|
||||
# Set INSTALL_RPATH for target
|
||||
MACRO(SET_INSTALL_RPATHS _target _paths)
|
||||
SET(${_target}_rpaths "")
|
||||
FOREACH(_path ${_paths})
|
||||
LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${_path}" isSystemDir)
|
||||
IF("${isSystemDir}" STREQUAL "-1")
|
||||
LIST(APPEND ${_target}_rpaths ${_path})
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
MESSAGE(STATUS "Set INSTALL_RPATH for ${_target}: ${${_target}_rpaths}")
|
||||
SET_TARGET_PROPERTIES(${_target} PROPERTIES INSTALL_RPATH "${${_target}_rpaths}")
|
||||
ENDMACRO()
|
||||
|
||||
# Find Boost
|
||||
IF(WIN32)
|
||||
SET(Boost_USE_STATIC_LIBS ON)
|
||||
SET(Boost_USE_STATIC_RUNTIME ON)
|
||||
SET(Boost_USE_MULTITHREADED ON)
|
||||
ENDIF()
|
||||
|
||||
set(BOOST_COMPONENTS system program_options regex thread date_time)
|
||||
if(USE_MMAP)
|
||||
if(MSVC)
|
||||
# filesystem is necessary for the utf-16 wpath
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams filesystem)
|
||||
else()
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams)
|
||||
endif()
|
||||
add_definitions(-DUSE_MMAP)
|
||||
endif()
|
||||
|
||||
FIND_PACKAGE(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}")
|
||||
MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
|
||||
|
||||
# Find Open CASCADE header files
|
||||
IF("$ENV{OCC_INCLUDE_DIR}" STREQUAL "")
|
||||
SET(OCC_INCLUDE_DIR "/usr/include/opencascade/" CACHE FILEPATH "Open CASCADE header files")
|
||||
MESSAGE(STATUS "Looking for opencascade include files in: ${OCC_INCLUDE_DIR}")
|
||||
# Usage:
|
||||
# set(SOME_LIRARIES foo bar)
|
||||
# add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" d)
|
||||
# "foo bar" -> "optimized foo debug food optimized bar debug bard"
|
||||
# or
|
||||
# set(SOME_LIRARIES path/foo.lib)
|
||||
# add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" "d")
|
||||
# "path/foo.lib" -> "optimized path/foo.lib debug path/food.lib"
|
||||
# TODO Could be refined: take the library file extension as a parameter and
|
||||
# make sure the lib variable ends with not just contains it.
|
||||
function(add_debug_variants NAME LIBRARIES POSTFIX)
|
||||
set(LIBRARIES_STR "${LIBRARIES}")
|
||||
set(LIBRARIES "")
|
||||
# the result, "optimized <lib> debug <lib>", needs to be a list instead of a string
|
||||
foreach(lib ${LIBRARIES_STR})
|
||||
list(APPEND LIBRARIES optimized)
|
||||
if ("${lib}" MATCHES ".lib")
|
||||
string(REPLACE ".lib" "" lib ${lib})
|
||||
list(APPEND LIBRARIES ${lib}.lib)
|
||||
else()
|
||||
list(APPEND LIBRARIES ${lib})
|
||||
endif()
|
||||
|
||||
list(APPEND LIBRARIES debug)
|
||||
if ("${lib}" MATCHES ".lib")
|
||||
string(REPLACE ".lib" "" lib ${lib})
|
||||
list(APPEND LIBRARIES ${lib}${POSTFIX}.lib)
|
||||
else()
|
||||
list(APPEND LIBRARIES ${lib}${POSTFIX})
|
||||
endif()
|
||||
endforeach()
|
||||
set(${NAME} ${LIBRARIES} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Find Open CASCADE
|
||||
IF("${OCC_INCLUDE_DIR}" STREQUAL "")
|
||||
SET(OCC_INCLUDE_DIR "/usr/include/oce/" CACHE FILEPATH "Open CASCADE header files")
|
||||
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
|
||||
MESSAGE(STATUS "Use OCC_INCLUDE_DIR to specify another directory")
|
||||
ELSE()
|
||||
SET(OCC_INCLUDE_DIR $ENV{OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
|
||||
MESSAGE(STATUS "Looking for opencascade include files in: ${OCC_INCLUDE_DIR}")
|
||||
SET(OCC_INCLUDE_DIR ${OCC_INCLUDE_DIR} CACHE FILEPATH "Open CASCADE header files")
|
||||
MESSAGE(STATUS "Looking for Open CASCADE include files in: ${OCC_INCLUDE_DIR}")
|
||||
ENDIF()
|
||||
|
||||
FIND_FILE(gp_Pnt_hxx "gp_Pnt.hxx" ${OCC_INCLUDE_DIR} /usr/inc /usr/local/inc /usr/local/include/oce)
|
||||
FIND_FILE(gp_Pnt_hxx "gp_Pnt.hxx" ${OCC_INCLUDE_DIR})
|
||||
IF(gp_Pnt_hxx)
|
||||
MESSAGE(STATUS "Header files found")
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "Unable to find header files, aborting")
|
||||
ENDIF()
|
||||
|
||||
# Find Open CASCADE library files
|
||||
IF("$ENV{OCC_LIBRARY_DIR}" STREQUAL "")
|
||||
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
|
||||
)
|
||||
|
||||
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
|
||||
SET(OCC_LIBRARY_DIR "/usr/lib/" CACHE FILEPATH "Open CASCADE library files")
|
||||
MESSAGE(STATUS "Looking for opencascade library files in: ${OCC_LIBRARY_DIR}")
|
||||
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
|
||||
MESSAGE(STATUS "Use OCC_LIBRARY_DIR to specify another directory")
|
||||
ELSE()
|
||||
SET(OCC_LIBRARY_DIR $ENV{OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
|
||||
MESSAGE(STATUS "Looking for opencascade library files in: ${OCC_LIBRARY_DIR}")
|
||||
SET(OCC_LIBRARY_DIR ${OCC_LIBRARY_DIR} CACHE FILEPATH "Open CASCADE library files")
|
||||
MESSAGE(STATUS "Looking for Open CASCADE library files in: ${OCC_LIBRARY_DIR}")
|
||||
ENDIF()
|
||||
|
||||
FIND_LIBRARY(libTKernel "TKernel" ${OCC_LIBRARY_DIR} /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64)
|
||||
FIND_LIBRARY(libTKernel NAMES TKernel TKerneld PATHS ${OCC_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
IF(libTKernel)
|
||||
MESSAGE(STATUS "Library files found")
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "Unable to find library files, aborting")
|
||||
ENDIF()
|
||||
|
||||
IF("$ENV{ICU_INCLUDE_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No ICU include directory specified")
|
||||
ElSE()
|
||||
SET(ICU_INCLUDE_DIR CACHE FILEPATH "ICU header files")
|
||||
# Use the found libTKernel as a template for all other OCC libraries
|
||||
# TODO Extract this into macro/function
|
||||
foreach(lib ${OPENCASCADE_LIBRARY_NAMES})
|
||||
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
|
||||
string(REPLACE TKerneld "${lib}" lib_path "${libTKernel}")
|
||||
string(REPLACE TKernel "${lib}" lib_path "${lib_path}")
|
||||
list(APPEND OPENCASCADE_LIBRARIES "${lib_path}")
|
||||
endforeach()
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-DHAVE_NO_DLL)
|
||||
add_debug_variants(OPENCASCADE_LIBRARIES "${OPENCASCADE_LIBRARIES}" d)
|
||||
endif()
|
||||
if (WIN32)
|
||||
# OCC might require linking to Winsock depending on the version and build configuration
|
||||
list(APPEND OPENCASCADE_LIBRARIES ws2_32.lib)
|
||||
endif()
|
||||
|
||||
IF(UNICODE_SUPPORT)
|
||||
# Find ICU
|
||||
IF("${ICU_INCLUDE_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No ICU include directory specified")
|
||||
ENDIF()
|
||||
|
||||
IF("${ICU_LIBRARY_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No ICU library directory specified")
|
||||
FIND_LIBRARY(icu NAMES icuuc icuucd PATHS ${ICU_LIBRARY_DIR})
|
||||
ELSE()
|
||||
FIND_LIBRARY(icu NAMES icuuc icuucd PATHS ${ICU_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
ENDIF()
|
||||
|
||||
IF(icu)
|
||||
GET_FILENAME_COMPONENT(ICU_LIBRARY_DIR ${icu} PATH)
|
||||
|
||||
ADD_DEFINITIONS(-DHAVE_ICU)
|
||||
MESSAGE(STATUS "ICU libraries found")
|
||||
# NOTE icudata appears to be icudt on Windows/MSVC and icudata on others
|
||||
# dl is included to resolve dlopen and friends symbols
|
||||
IF(WIN32)
|
||||
FIND_LIBRARY(icudt NAMES icudt PATHS ${ICU_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
SET(ICU_LIBRARIES ${icu} ${icudt})
|
||||
add_debug_variants(ICU_LIBRARIES "${ICU_LIBRARIES}" d)
|
||||
# TODO MinGW build would appear to be using dynamic ICU regardless of this definition.
|
||||
ADD_DEFINITIONS(-DU_STATIC_IMPLEMENTATION) # required for static ICU
|
||||
ELSE()
|
||||
FIND_LIBRARY(icudt NAMES icudata PATHS ${ICU_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
FIND_LIBRARY(icui18n NAMES icui18n PATHS ${ICU_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
FIND_LIBRARY(dl NAMES dl)
|
||||
SET(ICU_LIBRARIES ${icu} ${icudt} ${dl} ${icui18n})
|
||||
ENDIF()
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "UNICODE_SUPPORT enabled, but unable to find ICU. Disable UNICODE_SUPPORT or fix ICU paths to proceed.")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF("$ENV{ICU_LIBRARY_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No ICU library directory specified")
|
||||
ElSE()
|
||||
SET(ICU_LIBRARY_DIR CACHE FILEPATH "ICU library files")
|
||||
IF(COLLADA_SUPPORT)
|
||||
# Find OpenCOLLADA
|
||||
IF("${OPENCOLLADA_INCLUDE_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No OpenCOLLADA include directory specified")
|
||||
SET(OPENCOLLADA_INCLUDE_DIR "/usr/include/opencollada" CACHE FILEPATH "OpenCOLLADA header files")
|
||||
ELSE()
|
||||
SET(OPENCOLLADA_INCLUDE_DIR "${OPENCOLLADA_INCLUDE_DIR}" CACHE FILEPATH "OpenCOLLADA header files")
|
||||
ENDIF()
|
||||
|
||||
IF("${OPENCOLLADA_LIBRARY_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No OpenCOLLADA library directory specified")
|
||||
FIND_LIBRARY(OPENCOLLADA_FRAMEWORK_LIB NAMES OpenCOLLADAFramework
|
||||
PATHS /usr/lib64/opencollada /usr/lib/opencollada /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib)
|
||||
GET_FILENAME_COMPONENT(OPENCOLLADA_LIBRARY_DIR ${OPENCOLLADA_FRAMEWORK_LIB} PATH)
|
||||
ENDIF()
|
||||
|
||||
FIND_LIBRARY(OpenCOLLADAFramework NAMES OpenCOLLADAFramework OpenCOLLADAFrameworkd PATHS ${OPENCOLLADA_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
if (OpenCOLLADAFramework)
|
||||
message(STATUS "OpenCOLLADA library files found")
|
||||
else()
|
||||
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find OpenCOLLADA libraries. "
|
||||
"Disable COLLADA_SUPPORT or fix OpenCOLLADA paths to proceed.")
|
||||
endif()
|
||||
|
||||
SET(OPENCOLLADA_LIBRARY_DIR "${OPENCOLLADA_LIBRARY_DIR}" CACHE FILEPATH "OpenCOLLADA library files")
|
||||
|
||||
SET(OPENCOLLADA_INCLUDE_DIRS "${OPENCOLLADA_INCLUDE_DIR}/COLLADABaseUtils" "${OPENCOLLADA_INCLUDE_DIR}/COLLADAStreamWriter")
|
||||
|
||||
FIND_FILE(COLLADASWStreamWriter_h "COLLADASWStreamWriter.h" ${OPENCOLLADA_INCLUDE_DIRS})
|
||||
IF(COLLADASWStreamWriter_h)
|
||||
MESSAGE(STATUS "OpenCOLLADA header files found")
|
||||
ADD_DEFINITIONS(-DWITH_OPENCOLLADA)
|
||||
SET(OPENCOLLADA_LIBRARY_NAMES
|
||||
GeneratedSaxParser MathMLSolver OpenCOLLADABaseUtils OpenCOLLADAFramework OpenCOLLADASaxFrameworkLoader
|
||||
OpenCOLLADAStreamWriter UTF buffer ftoa
|
||||
)
|
||||
|
||||
# Use the found OpenCOLLADAFramework as a template for all other OpenCOLLADA libraries
|
||||
foreach(lib ${OPENCOLLADA_LIBRARY_NAMES})
|
||||
# Make sure we'll handle the Windows/MSVC debug postfix convetion too.
|
||||
string(REPLACE OpenCOLLADAFrameworkd "${lib}" lib_path "${OpenCOLLADAFramework}")
|
||||
string(REPLACE OpenCOLLADAFramework "${lib}" lib_path "${lib_path}")
|
||||
list(APPEND OPENCOLLADA_LIBRARIES "${lib_path}")
|
||||
endforeach()
|
||||
|
||||
if("${PCRE_LIBRARY_DIR}" STREQUAL "")
|
||||
if(WIN32)
|
||||
find_library(pcre_library NAMES pcre pcred PATHS ${OPENCOLLADA_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
else()
|
||||
find_library(pcre_library NAMES pcre PATHS ${OPENCOLLADA_LIBRARY_DIR})
|
||||
endif()
|
||||
GET_FILENAME_COMPONENT(PCRE_LIBRARY_DIR ${pcre_library} PATH)
|
||||
else()
|
||||
find_library(pcre_library NAMES pcre pcred PATHS ${PCRE_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
if (pcre_library)
|
||||
SET(OPENCOLLADA_LIBRARY_DIR ${OPENCOLLADA_LIBRARY_DIR} ${PCRE_LIBRARY_DIR})
|
||||
if (MSVC)
|
||||
# Add release lib regardless whether release or debug found. Debug version will be appended below.
|
||||
list(APPEND OPENCOLLADA_LIBRARIES "${PCRE_LIBRARY_DIR}/pcre.lib")
|
||||
else()
|
||||
list(APPEND OPENCOLLADA_LIBRARIES "${pcre_library}")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find PCRE. "
|
||||
"Disable COLLADA_SUPPORT or fix PCRE_LIBRARY_DIR path to proceed.")
|
||||
endif()
|
||||
|
||||
IF(MSVC)
|
||||
add_debug_variants(OPENCOLLADA_LIBRARIES "${OPENCOLLADA_LIBRARIES}" d)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
message(FATAL_ERROR "COLLADA_SUPPORT enabled, but unable to find OpenCOLLADA headers. "
|
||||
"Disable COLLADA_SUPPORT or fix OpenCOLLADA paths to proceed.")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
FIND_LIBRARY(icu "icuuc" /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${ICU_LIBRARY_DIR})
|
||||
|
||||
IF(icu)
|
||||
MESSAGE(STATUS "ICU libraries found")
|
||||
ADD_DEFINITIONS(-DHAVE_ICU)
|
||||
ELSE()
|
||||
MESSAGE(STATUS "Unable to find ICU library files, continuing")
|
||||
ENDIF()
|
||||
|
||||
IF("$ENV{OPENCOLLADA_INCLUDE_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No OpenCOLLADA include directory specified")
|
||||
SET(OPENCOLLADA_INCLUDE_DIR "/usr/local/include/opencollada" CACHE FILEPATH "OpenCOLLADA header files")
|
||||
ElSE()
|
||||
SET(OPENCOLLADA_INCLUDE_DIR "$ENV{OPENCOLLADA_INCLUDE_DIR}" CACHE FILEPATH "OpenCOLLADA header files")
|
||||
ENDIF()
|
||||
|
||||
IF("$ENV{OPENCOLLADA_LIBRARY_DIR}" STREQUAL "")
|
||||
MESSAGE(STATUS "No OpenCOLLADA library directory specified")
|
||||
SET(OPENCOLLADA_LIBRARY_DIR "/usr/local/lib/opencollada" CACHE FILEPATH "OpenCOLLADA library files")
|
||||
ElSE()
|
||||
SET(OPENCOLLADA_LIBRARY_DIR "$ENV{OPENCOLLADA_LIBRARY_DIR}" CACHE FILEPATH "OpenCOLLADA library files")
|
||||
ENDIF()
|
||||
|
||||
SET(OPENCOLLADA_INCLUDE_DIRS "${OPENCOLLADA_INCLUDE_DIR}/COLLADABaseUtils" "${OPENCOLLADA_INCLUDE_DIR}/COLLADAStreamWriter")
|
||||
|
||||
FIND_FILE(COLLADASWStreamWriter_h "COLLADASWStreamWriter.h" ${OPENCOLLADA_INCLUDE_DIRS})
|
||||
IF(COLLADASWStreamWriter_h)
|
||||
MESSAGE(STATUS "OpenCOLLADA header files found")
|
||||
ADD_DEFINITIONS(-DWITH_OPENCOLLADA)
|
||||
SET(OPENCOLLADA_LIBRARIES
|
||||
GeneratedSaxParser MathMLSolver OpenCOLLADABaseUtils
|
||||
OpenCOLLADAFramework OpenCOLLADASaxFrameworkLoader
|
||||
OpenCOLLADAStreamWriter UTF buffer ftoa pcre
|
||||
)
|
||||
ELSE()
|
||||
MESSAGE(STATUS "OpenCOLLADA header files not found, continuing without COLLADA support")
|
||||
ENDIF()
|
||||
|
||||
INCLUDE(CheckIncludeFileCXX)
|
||||
|
||||
MACRO(CHECK_ADD_OCE_OCC_DEF INCLUDE)
|
||||
STRING(REPLACE . _ STR ${INCLUDE})
|
||||
STRING(TOUPPER ${STR} STR)
|
||||
CHECK_INCLUDE_FILE_CXX("${INCLUDE}" FOUND_${STR})
|
||||
IF(FOUND_${STR})
|
||||
ADD_DEFINITIONS(-DOCE_HAVE_${STR})
|
||||
ADD_DEFINITIONS(-DHAVE_${STR})
|
||||
ENDIF(FOUND_${STR})
|
||||
ENDMACRO(CHECK_ADD_OCE_OCC_DEF)
|
||||
|
||||
CHECK_ADD_OCE_OCC_DEF(limits)
|
||||
CHECK_ADD_OCE_OCC_DEF(climits)
|
||||
CHECK_ADD_OCE_OCC_DEF(limits.h)
|
||||
CHECK_ADD_OCE_OCC_DEF(fstream)
|
||||
CHECK_ADD_OCE_OCC_DEF(fstream.h)
|
||||
CHECK_ADD_OCE_OCC_DEF(iomanip)
|
||||
CHECK_ADD_OCE_OCC_DEF(iomanip.h)
|
||||
CHECK_ADD_OCE_OCC_DEF(iostream)
|
||||
CHECK_ADD_OCE_OCC_DEF(iostream.h)
|
||||
# Make sure cross-referenced symbols between static OCC libraries get
|
||||
# resolved. Also add thread and rt libraries.
|
||||
get_filename_component(libTKernelExt ${libTKernel} EXT)
|
||||
if("${libTKernelExt}" STREQUAL ".a")
|
||||
find_package(Threads)
|
||||
# OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead?
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
if (NOT APPLE AND NOT WIN32)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt")
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "dl")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
IF(NOT CMAKE_BUILD_TYPE)
|
||||
SET(CMAKE_BUILD_TYPE "Release")
|
||||
ENDIF(NOT CMAKE_BUILD_TYPE)
|
||||
|
||||
IF(MSVC)
|
||||
ADD_DEFINITIONS(-D_UNICODE)
|
||||
ElSE(MSVC)
|
||||
ADD_DEFINITIONS(-fPIC -Wno-non-virtual-dtor)
|
||||
ENDIF(MSVC)
|
||||
|
||||
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} /usr/inc /usr/local/inc /usr/local/include/oce ${ICU_INCLUDE_DIR} ${Boost_INCLUDE_DIRS})
|
||||
|
||||
ADD_LIBRARY(IfcParse STATIC
|
||||
../src/ifcparse/Ifc2x3-latebound.cpp
|
||||
../src/ifcparse/Ifc2x3.cpp
|
||||
../src/ifcparse/Ifc4-latebound.cpp
|
||||
../src/ifcparse/Ifc4.cpp
|
||||
../src/ifcparse/IfcCharacterDecoder.cpp
|
||||
../src/ifcparse/IfcGuidHelper.cpp
|
||||
../src/ifcparse/IfcHierarchyHelper.cpp
|
||||
../src/ifcparse/IfcLateBoundEntity.cpp
|
||||
../src/ifcparse/IfcParse.cpp
|
||||
../src/ifcparse/IfcSIPrefix.cpp
|
||||
../src/ifcparse/IfcSpfHeader.cpp
|
||||
../src/ifcparse/IfcUtil.cpp
|
||||
../src/ifcparse/IfcWrite.cpp
|
||||
)
|
||||
|
||||
ADD_LIBRARY(IfcGeom STATIC
|
||||
../src/ifcgeom/IfcGeomCurves.cpp
|
||||
../src/ifcgeom/IfcGeomFaces.cpp
|
||||
../src/ifcgeom/IfcGeomFunctions.cpp
|
||||
../src/ifcgeom/IfcGeomHelpers.cpp
|
||||
../src/ifcgeom/IfcGeomMaterial.cpp
|
||||
../src/ifcgeom/IfcGeomRenderStyles.cpp
|
||||
../src/ifcgeom/IfcGeomRepresentation.cpp
|
||||
../src/ifcgeom/IfcGeomShapes.cpp
|
||||
../src/ifcgeom/IfcGeomWires.cpp
|
||||
../src/ifcgeom/IfcRegister.cpp
|
||||
)
|
||||
|
||||
IF(icu)
|
||||
TARGET_LINK_LIBRARIES(IfcParse icuuc)
|
||||
SET(CMAKE_BUILD_TYPE "Release")
|
||||
ENDIF()
|
||||
|
||||
TARGET_LINK_LIBRARIES(IfcGeom IfcParse)
|
||||
if(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
if(MSVC)
|
||||
# NOTE: RelWithDebInfo and Release use O2 (= /Ox /Gl /Gy/ = Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) by default,
|
||||
# with the exception with RelWithDebInfo has /Ob1 instead. /Ob2 has been observed to improve the performance
|
||||
# of IfcConvert significantly.
|
||||
# TODO Setting of /GL and /LTCG don't seem to apply for static libraries (IfcGeom, IfcParse)
|
||||
# C++
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ob2 /GL")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
|
||||
# Linker
|
||||
# /OPT:REF enables also /OPT:ICF and disables INCREMENTAL
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
|
||||
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
|
||||
else()
|
||||
# GCC-like: Release should use O3 but RelWithDebInfo 02 so enforce 03. Anything other useful that could be added here?
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
LINK_DIRECTORIES (${LINK_DIRECTORIES} ${IfcOpenShell_BINARY_DIR} ${OCC_LIBRARY_DIR} ${OPENCOLLADA_LIBRARY_DIR} /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${ICU_LIBRARY_DIR} ${Boost_LIBRARY_DIRS})
|
||||
IF(MSVC)
|
||||
# Enable solution folders (free VS versions prior to 2012 don't support solution folders)
|
||||
if (MSVC_VERSION GREATER 1600)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
endif()
|
||||
|
||||
ADD_EXECUTABLE(IfcConvert
|
||||
../src/ifcconvert/ColladaSerializer.cpp
|
||||
../src/ifcconvert/IfcConvert.cpp
|
||||
../src/ifcconvert/OpenCascadeBasedSerializer.cpp
|
||||
../src/ifcconvert/WavefrontObjSerializer.cpp
|
||||
../src/ifcconvert/XmlSerializer.cpp
|
||||
IF(USE_VLD)
|
||||
ADD_DEFINITIONS(-DUSE_VLD)
|
||||
ENDIF()
|
||||
# Enforce Unicode for CRT and Win32 API calls
|
||||
ADD_DEFINITIONS(-D_UNICODE -DUNICODE)
|
||||
# Disable warnings about unsafe C functions; we could use the safe C99 & C11 versions if we have no need for supporting old compilers.
|
||||
ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
|
||||
ADD_DEFINITIONS(-bigobj) # required for building the big ifcXXX.objs, https://msdn.microsoft.com/en-us/library/ms173499.aspx
|
||||
# Bump up the warning level from the default 3 to 4.
|
||||
ADD_DEFINITIONS(-W4)
|
||||
IF(MSVC_VERSION GREATER 1800) # > 2013
|
||||
# Disable overeager and false positives causing C4458 ("declaration of 'indentifier' hides class member"), at least for now.
|
||||
ADD_DEFINITIONS(-wd4458)
|
||||
ENDIF()
|
||||
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
|
||||
if (MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
|
||||
add_definitions(-permissive-)
|
||||
endif()
|
||||
# Link against the static VC runtime
|
||||
# TODO Make this configurable
|
||||
FOREACH(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
|
||||
IF(${flag} MATCHES "/MD")
|
||||
STRING(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
|
||||
ENDIF()
|
||||
IF(${flag} MATCHES "/MDd")
|
||||
STRING(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}")
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
ElSE()
|
||||
add_definitions(-Wall -Wextra)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_definitions(-Wno-tautological-constant-out-of-range-compare)
|
||||
else()
|
||||
add_definitions(-Wno-maybe-uninitialized)
|
||||
endif()
|
||||
# -fPIC is not relevant on Windows and creates pointless warnings
|
||||
if (UNIX)
|
||||
add_definitions(-fPIC)
|
||||
endif()
|
||||
ENDIF()
|
||||
|
||||
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS}
|
||||
${ICU_INCLUDE_DIR} ${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES (IfcConvert IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset ${Boost_LIBRARIES} ${OPENCOLLADA_LIBRARIES})
|
||||
function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||
set(IFC_PARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcparse)
|
||||
set(${RESULT_NAME}
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}enum.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}-latebound.h
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}.cpp
|
||||
${IFC_PARSE_DIR}/Ifc${IFC_VERSION}-latebound.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endfunction()
|
||||
|
||||
ADD_EXECUTABLE(IfcGeomServer
|
||||
../src/ifcgeomserver/IfcGeomServer.cpp
|
||||
)
|
||||
if(COMPILE_SCHEMA)
|
||||
find_package(PythonInterp)
|
||||
|
||||
IF(NOT PYTHONINTERP_FOUND)
|
||||
MESSAGE(FATAL_ERROR "A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed.")
|
||||
ENDIF()
|
||||
|
||||
set(IFC_RELEASE_NOT_USED "2x3" "4")
|
||||
|
||||
# Install pyparsing if necessary
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
|
||||
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
|
||||
execute_process(COMMAND pip freeze OUTPUT_VARIABLE PYTHON_PACKAGE_LIST)
|
||||
if ("${PYTHON_PACKAGE_LIST}" STREQUAL "")
|
||||
message(WARNING "Failed to find pip. Pip is required to automatically install pyparsing")
|
||||
endif()
|
||||
endif()
|
||||
string(FIND "${PYTHON_PACKAGE_LIST}" pyparsing PYPARSING_FOUND)
|
||||
if ("${PYPARSING_FOUND}" STREQUAL "-1")
|
||||
message(STATUS "Installing pyparsing")
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
|
||||
if (NOT "${SUCCESS}" STREQUAL "0")
|
||||
execute_process(COMMAND pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
|
||||
if (NOT "${SUCCESS}" STREQUAL "0")
|
||||
message(WARNING "Failed to automatically install pyparsing. Please install manually")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Python interpreter with pyparsing found")
|
||||
endif()
|
||||
|
||||
# Bootstrap the parser
|
||||
message(STATUS "Compiling schema, this will take a while...")
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
|
||||
WORKING_DIRECTORY ../src/ifcexpressparser
|
||||
OUTPUT_FILE express_parser.py
|
||||
RESULT_VARIABLE SUCCESS)
|
||||
|
||||
TARGET_LINK_LIBRARIES (IfcGeomServer IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset)
|
||||
if (NOT "${SUCCESS}" STREQUAL "0")
|
||||
MESSAGE(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
|
||||
endif()
|
||||
|
||||
# Generate code
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
|
||||
WORKING_DIRECTORY ../src/ifcparse
|
||||
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
|
||||
|
||||
# Prevent the schema that had just been compiled from being excluded
|
||||
if("${COMPILED_SCHEMA_NAME}" STREQUAL "IFC2X3")
|
||||
list(REMOVE_ITEM IFC_RELEASE_NOT_USED "2x3")
|
||||
add_definitions(-DUSE_IFC2x3)
|
||||
elseif("${COMPILED_SCHEMA_NAME}" STREQUAL "IFC4")
|
||||
list(REMOVE_ITEM IFC_RELEASE_NOT_USED "4")
|
||||
add_definitions(-DUSE_IFC4)
|
||||
endif()
|
||||
else()
|
||||
if(USE_IFC4)
|
||||
add_definitions(-DUSE_IFC4)
|
||||
set(IFC_RELEASE_NOT_USED "2x3")
|
||||
else()
|
||||
add_definitions(-DUSE_IFC2x3) # TODO Make all caps? i.e. USE_IFC2X3
|
||||
set(IFC_RELEASE_NOT_USED "4")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Build python wrapper using separate CMakeLists.txt
|
||||
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
|
||||
# Boost >= 1.58 requires BOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE to build on some Linux distros.
|
||||
if(NOT Boost_VERSION LESS 105800)
|
||||
add_definitions(-DBOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE)
|
||||
endif()
|
||||
|
||||
# Build IfcParseExamples using separate CMakeLists.txt
|
||||
ADD_SUBDIRECTORY(../src/examples examples)
|
||||
# Detect OCC version on gcc/clang/mingw as
|
||||
# -std=c++11 is needed for OCCT >= 7.0.0
|
||||
if(NOT MSVC)
|
||||
FIND_FILE(Standard_Version "Standard_Version.hxx" ${OCC_INCLUDE_DIR})
|
||||
|
||||
# ADD_SUBDIRECTORY(../src/qtviewer qtviewer)
|
||||
set(CMAKE_CONFIGURABLE_FILE_CONTENT "
|
||||
#include <Standard_Version.hxx>
|
||||
#include <iostream>
|
||||
int main(int argc, char** argv) {
|
||||
std::cout << OCC_VERSION_COMPLETE;
|
||||
}")
|
||||
configure_file(
|
||||
"${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
|
||||
"${CMAKE_BINARY_DIR}/version.cxx" @ONLY)
|
||||
|
||||
try_compile(VERSION_CHECK
|
||||
${CMAKE_BINARY_DIR}
|
||||
"${CMAKE_BINARY_DIR}/version.cxx"
|
||||
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${OCC_INCLUDE_DIR}"
|
||||
COPY_FILE "${CMAKE_BINARY_DIR}/version"
|
||||
OUTPUT_VARIABLE OUT
|
||||
COPY_FILE_ERROR ERR
|
||||
)
|
||||
|
||||
if(${VERSION_CHECK})
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/version OUTPUT_VARIABLE OCC_VERSION)
|
||||
else()
|
||||
message(FATAL_ERROR "Failed to compile OCC version test:
|
||||
${OUT}
|
||||
------
|
||||
${ERR}")
|
||||
endif()
|
||||
|
||||
MESSAGE(STATUS "OCC version is ${OCC_VERSION}. Detected from: ${Standard_Version}")
|
||||
|
||||
if(NOT ("${OCC_VERSION}" LESS "7.0.0"))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
if(COMPILER_SUPPORTS_CXX11)
|
||||
add_definitions(-std=c++11)
|
||||
else()
|
||||
message(FATAL_ERROR "OCCT7 requires a compiler with C++11 support")
|
||||
endif()
|
||||
else()
|
||||
add_definitions(-std=c++0x)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
set(IFCOPENSHELL_LIBRARIES IfcParse IfcGeom)
|
||||
|
||||
# IfcParse
|
||||
file(GLOB IFCPARSE_H_FILES ../src/ifcparse/*.h)
|
||||
file(GLOB IFCPARSE_CPP_FILES ../src/ifcparse/*.cpp)
|
||||
|
||||
foreach(IFC_RELEASE ${IFC_RELEASE_NOT_USED})
|
||||
files_for_ifc_version(${IFC_RELEASE} SOURCE_FILES_NOT_USED)
|
||||
foreach(SOURCE_FILE ${SOURCE_FILES_NOT_USED})
|
||||
list(REMOVE_ITEM IFCPARSE_CPP_FILES ${SOURCE_FILE})
|
||||
list(REMOVE_ITEM IFCPARSE_H_FILES ${SOURCE_FILE})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES})
|
||||
|
||||
add_library(IfcParse ${IFCPARSE_FILES})
|
||||
set_target_properties(IfcParse PROPERTIES COMPILE_FLAGS -DIFC_PARSE_EXPORTS)
|
||||
|
||||
IF(UNICODE_SUPPORT)
|
||||
TARGET_LINK_LIBRARIES(IfcParse ${ICU_LIBRARIES} ${Boost_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
# IfcGeom
|
||||
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/*.h)
|
||||
file(GLOB IFCGEOM_CPP_FILES ../src/ifcgeom/*.cpp)
|
||||
set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES})
|
||||
|
||||
add_library(IfcGeom ${IFCGEOM_FILES})
|
||||
set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS)
|
||||
|
||||
TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${OPENCASCADE_LIBRARIES})
|
||||
|
||||
# IfcConvert
|
||||
file(GLOB IFCCONVERT_CPP_FILES ../src/ifcconvert/*.cpp)
|
||||
file(GLOB IFCCONVERT_H_FILES ../src/ifcconvert/*.h)
|
||||
set(IFCCONVERT_FILES ${IFCCONVERT_CPP_FILES} ${IFCCONVERT_H_FILES})
|
||||
ADD_EXECUTABLE(IfcConvert ${IFCCONVERT_FILES})
|
||||
if (IFCCONVERT_DOUBLE_PRECISION)
|
||||
set_target_properties(IfcConvert PROPERTIES COMPILE_FLAGS -DIFCCONVERT_DOUBLE_PRECISION)
|
||||
endif()
|
||||
|
||||
TARGET_LINK_LIBRARIES(IfcConvert ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${OPENCOLLADA_LIBRARIES} ${ICU_LIBRARIES})
|
||||
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
|
||||
# Only set RPATHs when building shared libraries (i.e. IfcParse and
|
||||
# IfcGeom are dynamically linked). Not necessarily a perfect solution
|
||||
# but probably a good indication of whether RPATHs are necessary.
|
||||
SET_INSTALL_RPATHS(IfcConvert "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${OPENCOLLADA_LIBRARY_DIR};${ICU_LIBRARY_DIR}")
|
||||
endif()
|
||||
|
||||
# IfcGeomServer
|
||||
file(GLOB CPP_FILES ../src/ifcgeomserver/*.cpp)
|
||||
file(GLOB H_FILES ../src/ifcgeomserver/*.h)
|
||||
set(SOURCE_FILES ${CPP_FILES} ${H_FILES})
|
||||
ADD_EXECUTABLE(IfcGeomServer ${SOURCE_FILES})
|
||||
TARGET_LINK_LIBRARIES(IfcGeomServer ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${ICU_LIBRARIES})
|
||||
if ((NOT WIN32) AND BUILD_SHARED_LIBS)
|
||||
SET_INSTALL_RPATHS(IfcGeomServer "${IFCOPENSHELL_LIBARY_DIR};${OCC_LIBRARY_DIR};${Boost_LIBRARY_DIRS};${ICU_LIBRARY_DIR}")
|
||||
endif()
|
||||
|
||||
IF(BUILD_IFCPYTHON)
|
||||
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
|
||||
ENDIF()
|
||||
|
||||
IF(BUILD_EXAMPLES)
|
||||
ADD_SUBDIRECTORY(../src/examples examples)
|
||||
ENDIF()
|
||||
|
||||
IF(BUILD_IFCMAX)
|
||||
ADD_SUBDIRECTORY(../src/ifcmax ifcmax)
|
||||
ENDIF()
|
||||
|
||||
# CMake installation targets
|
||||
SET(include_files_geom
|
||||
../src/ifcgeom/IfcGeom.h
|
||||
../src/ifcgeom/IfcGeomElement.h
|
||||
../src/ifcgeom/IfcGeomIterator.h
|
||||
../src/ifcgeom/IfcGeomIteratorSettings.h
|
||||
../src/ifcgeom/IfcGeomMaterial.h
|
||||
../src/ifcgeom/IfcGeomRenderStyles.h
|
||||
../src/ifcgeom/IfcGeomRepresentation.h
|
||||
../src/ifcgeom/IfcRegister.h
|
||||
../src/ifcgeom/IfcRegisterConvertCurve.h
|
||||
../src/ifcgeom/IfcRegisterConvertFace.h
|
||||
../src/ifcgeom/IfcRegisterConvertShape.h
|
||||
../src/ifcgeom/IfcRegisterConvertShapes.h
|
||||
../src/ifcgeom/IfcRegisterConvertWire.h
|
||||
../src/ifcgeom/IfcRegisterCreateCache.h
|
||||
../src/ifcgeom/IfcRegisterDef.h
|
||||
../src/ifcgeom/IfcRegisterGeomHeader.h
|
||||
../src/ifcgeom/IfcRegisterIsShapeCollection.h
|
||||
../src/ifcgeom/IfcRegisterPurgeCache.h
|
||||
../src/ifcgeom/IfcRegisterUndef.h
|
||||
../src/ifcgeom/IfcRepresentationShapeItem.h
|
||||
INSTALL(FILES ${IFCPARSE_H_FILES}
|
||||
DESTINATION ${INCLUDEDIR}/ifcparse
|
||||
)
|
||||
SET(include_files_parse
|
||||
../src/ifcparse/Ifc2x3-latebound.h
|
||||
../src/ifcparse/Ifc2x3.h
|
||||
../src/ifcparse/Ifc2x3enum.h
|
||||
../src/ifcparse/Ifc4-latebound.h
|
||||
../src/ifcparse/Ifc4.h
|
||||
../src/ifcparse/Ifc4enum.h
|
||||
../src/ifcparse/IfcCharacterDecoder.h
|
||||
../src/ifcparse/IfcEntityDescriptor.h
|
||||
../src/ifcparse/IfcException.h
|
||||
../src/ifcparse/IfcFile.h
|
||||
../src/ifcparse/IfcHierarchyHelper.h
|
||||
../src/ifcparse/IfcLateBoundEntity.h
|
||||
../src/ifcparse/IfcParse.h
|
||||
../src/ifcparse/IfcSIPrefix.h
|
||||
../src/ifcparse/IfcSpfHeader.h
|
||||
../src/ifcparse/IfcSpfStream.h
|
||||
../src/ifcparse/IfcUtil.h
|
||||
../src/ifcparse/IfcWritableEntity.h
|
||||
../src/ifcparse/IfcWrite.h
|
||||
../src/ifcparse/SharedPointer.h
|
||||
|
||||
INSTALL(FILES ${IFCGEOM_H_FILES}
|
||||
DESTINATION ${INCLUDEDIR}/ifcgeom
|
||||
)
|
||||
|
||||
INSTALL(TARGETS IfcParse IfcGeom IfcConvert IfcGeomServer
|
||||
ARCHIVE DESTINATION ${LIBDIR}
|
||||
LIBRARY DESTINATION ${LIBDIR}
|
||||
RUNTIME DESTINATION ${BINDIR}
|
||||
)
|
||||
INSTALL(FILES ${include_files_geom} DESTINATION include/ifcgeom)
|
||||
INSTALL(FILES ${include_files_parse} DESTINATION include/ifcparse)
|
||||
INSTALL(TARGETS IfcConvert DESTINATION bin)
|
||||
INSTALL(TARGETS IfcParse IfcGeom DESTINATION lib)
|
||||
|
||||
@@ -0,0 +1,614 @@
|
||||
#!/usr/bin/python
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
# #
|
||||
# This script builds IfcOpenShell and its dependencies #
|
||||
# #
|
||||
# Prerequisites for this script to function correctly: #
|
||||
# * git * bzip2 * tar * c(++) compilers * yacc * autoconf #
|
||||
# #
|
||||
# if building with USE_OCCT additionally: #
|
||||
# * freetype * glx.h #
|
||||
# #
|
||||
# For python37 to install correctly additionally: #
|
||||
# * 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 #
|
||||
# #
|
||||
# on ubuntu 14.04: #
|
||||
# $ apt-get install git gcc g++ autoconf bison make #
|
||||
# libfreetype6-dev mesa-common-dev libffi-dev #
|
||||
# #
|
||||
# on OS X El Capitan with homebrew: #
|
||||
# $ brew install git bison autoconf automake freetype libffi #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import subprocess as sp
|
||||
import shutil
|
||||
import time
|
||||
import tarfile
|
||||
import multiprocessing
|
||||
import urllib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO)
|
||||
logger.addHandler(ch)
|
||||
|
||||
PROJECT_NAME="IfcOpenShell"
|
||||
OCE_VERSION="0.18"
|
||||
# OCCT_VERSION="7.1.0"
|
||||
# OCCT_HASH="89aebde"
|
||||
PYTHON_VERSIONS=["2.7.16", "3.2.6", "3.3.6", "3.4.6", "3.5.3", "3.6.2", "3.7.3"]
|
||||
# OCCT_VERSION="7.2.0"
|
||||
# OCCT_HASH="88af392"
|
||||
OCCT_VERSION="7.3.0p3"
|
||||
BOOST_VERSION="1.59.0"
|
||||
PCRE_VERSION="8.39"
|
||||
LIBXML_VERSION="2.9.3"
|
||||
CMAKE_VERSION="3.4.1"
|
||||
ICU_VERSION="56.1"
|
||||
SWIG_VERSION="3.0.12"
|
||||
|
||||
# binaries
|
||||
cp="cp"
|
||||
bash="bash"
|
||||
uname="uname"
|
||||
git="git"
|
||||
bunzip2="bunzip2"
|
||||
tar="tar"
|
||||
cc="cc"
|
||||
cplusplus="c++"
|
||||
autoconf="autoconf"
|
||||
automake="automake"
|
||||
yacc="yacc"
|
||||
make="make"
|
||||
date = "date"
|
||||
curl="curl"
|
||||
wget="wget"
|
||||
strip="strip"
|
||||
|
||||
# Helper function for coloured printing
|
||||
|
||||
NO_COLOR="\033[0m" # <ref>http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux</ref>
|
||||
BLACK_ON_WHITE="\033[0;30;107m"
|
||||
RED="\033[31m"
|
||||
GREEN="\033[32m"
|
||||
YELLOW="\033[33m"
|
||||
MAGENTA="\033[35m"
|
||||
|
||||
def cecho(message, color=NO_COLOR):
|
||||
"""Logs message `message` in color `color`."""
|
||||
logger.info("%s%s\033[0m" % (color, message))
|
||||
|
||||
def fullpath(arg):
|
||||
return os.path.realpath(os.path.dirname(sys.argv[1]))
|
||||
|
||||
def which(cmd):
|
||||
for path in os.environ["PATH"].split(":"):
|
||||
if os.path.exists(path) and cmd in os.listdir(path):
|
||||
return cmd
|
||||
return None
|
||||
|
||||
def get_os():
|
||||
ret_value = sp.check_output([uname, "-s"]).strip()
|
||||
return ret_value
|
||||
|
||||
# Set defaults for missing empty environment variables
|
||||
|
||||
USE_OCCT = os.environ.get("USE_OCCT", "true").lower() == "true"
|
||||
|
||||
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"
|
||||
|
||||
try:
|
||||
IFCOS_NUM_BUILD_PROCS = os.environ["IFCOS_NUM_BUILD_PROCS"]
|
||||
except KeyError:
|
||||
IFCOS_NUM_BUILD_PROCS=multiprocessing.cpu_count() + 1
|
||||
os.environ["IFCOS_NUM_BUILD_PROCS"]=str(IFCOS_NUM_BUILD_PROCS)
|
||||
|
||||
try:
|
||||
TARGET_ARCH = os.environ["TARGET_ARCH"]
|
||||
del os.environ["TARGET_ARCH"]
|
||||
except KeyError:
|
||||
TARGET_ARCH = sp.check_output([uname, "-m"]).strip()
|
||||
|
||||
CMAKE_DIR=os.path.realpath(os.path.join("..", "cmake"))
|
||||
|
||||
try:
|
||||
DEPS_DIR = os.environ["DEPS_DIR"]
|
||||
except KeyError:
|
||||
path = ["..", "build", sp.check_output(uname).strip(), TARGET_ARCH]
|
||||
if TOOLSET:
|
||||
path.append(TOOLSET)
|
||||
DEPS_DIR = os.path.realpath(os.path.join(*path))
|
||||
os.environ["DEPS_DIR"] = DEPS_DIR
|
||||
if not os.path.exists(DEPS_DIR):
|
||||
os.makedirs(DEPS_DIR)
|
||||
|
||||
try:
|
||||
BUILD_CFG=os.environ["BUILD_CFG"]
|
||||
except KeyError:
|
||||
BUILD_CFG="RelWithDebInfo"
|
||||
os.environ["BUILD_CFG"]=BUILD_CFG
|
||||
|
||||
|
||||
# Print build configuration information
|
||||
|
||||
cecho ("""This script fetches and builds %s and its dependencies
|
||||
""" % (PROJECT_NAME,), BLACK_ON_WHITE)
|
||||
cecho("""Script configuration:
|
||||
|
||||
""", GREEN)
|
||||
cecho("""* Target Architecture = %s""" % (TARGET_ARCH,), MAGENTA)
|
||||
cecho(" - Whether 32-bit (i686) or 64-bit (x86_64) will be built.")
|
||||
cecho("""* USE_OCCT = %r""" % (USE_OCCT,), MAGENTA)
|
||||
if USE_OCCT:
|
||||
cecho(" - Compiling against official Open Cascade")
|
||||
else:
|
||||
cecho(" - Compiling against Open Cascade Community Edition")
|
||||
cecho("* Dependency Directory = %s" % (DEPS_DIR,), MAGENTA)
|
||||
cecho(" - The directory where %s dependencies are installed." % (PROJECT_NAME,))
|
||||
cecho("* Build Config Type = %s" % (BUILD_CFG,), MAGENTA)
|
||||
cecho(""" - The used build configuration type for the dependencies.
|
||||
Defaults to RelWithDebInfo if not specified.""")
|
||||
|
||||
if BUILD_CFG == "MinSizeRel":
|
||||
cecho(" WARNING: MinSizeRel build can suffer from a significant performance loss.", RED)
|
||||
|
||||
cecho("* IFCOS_NUM_BUILD_PROCS = %s" % (IFCOS_NUM_BUILD_PROCS,), MAGENTA)
|
||||
cecho(""" - How many compiler processes may be run in parallel.
|
||||
""")
|
||||
|
||||
# Check that required tools are in PATH
|
||||
|
||||
for cmd in [git, bunzip2, tar, cc, cplusplus, autoconf, automake, yacc, make, "patch"]:
|
||||
if which(cmd) is None:
|
||||
raise ValueError("Required tool '%s' not installed or not added to PATH" % (cmd,))
|
||||
|
||||
# identifiers for the download tool (could be less memory consuming as ints, but are more verbose as strings)
|
||||
download_tool_curl="curl"
|
||||
download_tool_wget="wget"
|
||||
download_tool_git = "git"
|
||||
|
||||
if which(wget) != None:
|
||||
download_tool_default = download_tool_wget
|
||||
elif which(curl) != None:
|
||||
download_tool_default = download_tool_curl
|
||||
else:
|
||||
raise ValueError("No download application found, tried: curl, wget")
|
||||
|
||||
CURL = ["curl", "-sL"]
|
||||
WGET= ["wget", "-q", "--no-check-certificate"]
|
||||
|
||||
# Create log directory and file
|
||||
|
||||
log_dir = os.path.join(DEPS_DIR, "logs")
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
LOG_FILE="%s.log" % (os.path.join(log_dir, sp.check_output([date, "+%Y%m%d"]).strip()),)
|
||||
if not os.path.exists(LOG_FILE):
|
||||
open(LOG_FILE, "w").close()
|
||||
logger.info("using command log file '%s'" % (LOG_FILE,))
|
||||
|
||||
def __check_call__(cmds, cwd=None):
|
||||
logger.debug("running command %r in directory %r" % (" ".join(cmds), cwd))
|
||||
log_file_handle = open(LOG_FILE, "a")
|
||||
proc = sp.Popen(cmds, cwd=cwd, stdout=log_file_handle, stderr=sp.PIPE)
|
||||
_, stderr = proc.communicate()
|
||||
log_file_handle.write(stderr)
|
||||
log_file_handle.close()
|
||||
|
||||
if proc.returncode != 0:
|
||||
print "-" * 70
|
||||
print stderr
|
||||
print "-" * 70
|
||||
raise Exception("Command `%s` returned exit code %d" % (" ".join(cmds), proc.returncode))
|
||||
|
||||
def __check_output__(cmds, cwd=None):
|
||||
"""Wraps `subprocess.check_output` and logs the command being executed,
|
||||
sets up logging `stderr` to `LOG_FILE` (in append mode) and strips the
|
||||
return value because it's unlikely that the newline at the end of output is
|
||||
useful and it often causes errors"""
|
||||
logger.debug("running command '%s' in directory %r" % (" ".join(cmds), cwd))
|
||||
log_file_handle = open(LOG_FILE, "a")
|
||||
ret_value = sp.check_output(cmds, cwd=cwd, stderr=log_file_handle).strip()
|
||||
logger.debug("command returned %r" % ret_value)
|
||||
log_file_handle.close()
|
||||
return ret_value
|
||||
|
||||
BOOST_VERSION_UNDERSCORE=BOOST_VERSION.replace(".", "_")
|
||||
ICU_VERSION_UNDERSCORE=ICU_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)
|
||||
OPENCOLLADA_LOCATION="https://github.com/KhronosGroup/OpenCOLLADA.git"
|
||||
#OPENCOLLADA_COMMIT="f99d59e73e565a41715eaebc00c7664e1ee5e628"
|
||||
OPENCOLLADA_COMMIT="v1.6.63"
|
||||
|
||||
# Helper functions
|
||||
|
||||
def run_autoconf(arg1, configure_args, cwd):
|
||||
configure_path = os.path.realpath(os.path.join(cwd, "..", "configure"))
|
||||
if not os.path.exists(configure_path):
|
||||
__check_call__([bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things
|
||||
# Using `sh` over `bash` fixes issues with building swig
|
||||
__check_call__(["/bin/sh", "../configure"]+configure_args+["--prefix=%s" % (os.path.realpath("%s/install/%s" % (DEPS_DIR, arg1)),)], cwd=cwd)
|
||||
|
||||
def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
|
||||
if cmake_dir is None:
|
||||
P=".."
|
||||
else:
|
||||
P=cmake_dir
|
||||
cmake_path= os.path.join(DEPS_DIR, "install", "cmake-%s" % (CMAKE_VERSION,), "bin", "cmake")
|
||||
__check_call__([cmake_path, P]+cmake_args+["-DCMAKE_BUILD_TYPE=%s" % (BUILD_CFG,)], cwd=cwd)
|
||||
|
||||
def run_icu(arg1, icu_args, cwd):
|
||||
PLATFORM=get_os()
|
||||
if PLATFORM == "Darwin":
|
||||
PLATFORM="MacOSX"
|
||||
__check_call__([bash, "../source/runConfigureICU", PLATFORM]+icu_args+["--prefix=%s/install/%s" % (DEPS_DIR, arg1)], cwd=cwd)
|
||||
|
||||
def git_clone(clone_url, target_dir, revision=None):
|
||||
"""Lazily clones the `git` repository denoted by `clone_url` into
|
||||
`target_dir`, i.e. skips cloning if `target_dir` exists (naively assumes
|
||||
that a working clone exists there) and optionally checks out a revision
|
||||
`revision` after cloning or in the existing clone if `revision` is not
|
||||
`None`."""
|
||||
if not os.path.exists(target_dir):
|
||||
logger.info("cloning '%s' into '%s'" % (clone_url, target_dir))
|
||||
__check_call__([git, "clone", clone_url, target_dir])
|
||||
else:
|
||||
logger.info("directory '%s' exists, skipping cloning" % (target_dir,))
|
||||
if revision != None:
|
||||
__check_call__([git, "checkout", revision], cwd=target_dir)
|
||||
|
||||
def build_dependency(name, mode, build_tool_args, download_url, download_name, download_tool=download_tool_default, revision=None, patch=None, additional_files={}, no_append_name=False):
|
||||
"""Handles building of dependencies with different tools (which are
|
||||
distinguished with the `mode` argument. `build_tool_args` is expected to be
|
||||
a list which is necessary in order to not mess up quoting of compiler and
|
||||
linker flags."""
|
||||
check_dir = os.path.join(DEPS_DIR, "install", name)
|
||||
if os.path.exists(check_dir):
|
||||
logger.info( "Found existing %s, skipping" % (name,))
|
||||
return
|
||||
build_dir = os.path.join(DEPS_DIR, "build")
|
||||
if not os.path.exists(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
|
||||
logger.info("\rFetching %s... " % (name,))
|
||||
|
||||
if download_tool == download_tool_curl or download_tool == download_tool_wget:
|
||||
if no_append_name:
|
||||
url = download_url
|
||||
else:
|
||||
url = os.path.join(download_url, download_name)
|
||||
|
||||
if download_tool == download_tool_curl:
|
||||
download_path = os.path.join(build_dir, download_name)
|
||||
if not os.path.exists(download_path):
|
||||
__check_call__(CURL + ["-o", download_name, url], cwd=build_dir)
|
||||
else:
|
||||
logger.info("Download '%s' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping" % (download_path,))
|
||||
elif download_tool == download_tool_wget:
|
||||
download_path = os.path.join(build_dir, download_name)
|
||||
if not os.path.exists(download_path):
|
||||
__check_call__(WGET + ["-O", download_name, url], cwd=build_dir)
|
||||
else:
|
||||
logger.info("Download '%s' already exists, assuming it's an undamaged download and that it has been extracted if possible, skipping" % (download_path,))
|
||||
elif download_tool == download_tool_git:
|
||||
git_clone(download_url, target_dir=os.path.join(build_dir, download_name), revision=revision)
|
||||
else:
|
||||
raise ValueError("download tool '%s' is not supported" % (download_tool,))
|
||||
download_dir = os.path.join(build_dir, download_name)
|
||||
|
||||
if os.path.isdir(download_dir):
|
||||
extract_dir_name=download_name
|
||||
extract_dir = os.path.join(build_dir, extract_dir_name)
|
||||
else:
|
||||
download_tarfile_path = os.path.join(build_dir, download_name)
|
||||
if download_name.endswith(".tar.gz") or download_name.endswith(".tgz"):
|
||||
compr = "gz"
|
||||
elif download_name.endswith(".tar.bz2"):
|
||||
compr = "bz2"
|
||||
else:
|
||||
raise RuntimeError("fix source for new download type")
|
||||
download_tarfile = tarfile.open(name=download_tarfile_path, mode="r:%s" % (compr,))
|
||||
extract_dir_name= os.path.commonprefix(download_tarfile.getnames()) # tarfile seriously doesn't have a function to retrieve the root directory more easily
|
||||
#__check_output__([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works
|
||||
if extract_dir_name is None:
|
||||
extract_dir_name= __check_output__([bash, "-c", "tar -tf %s 2> /dev/null | head -n 1 | cut -f1 -d /" % (download_name,)], cwd=build_dir)
|
||||
extract_dir = os.path.join(build_dir, extract_dir_name)
|
||||
if not os.path.exists(extract_dir):
|
||||
__check_call__([tar, "-xf", download_name], cwd=build_dir)
|
||||
|
||||
for path, url in additional_files.items():
|
||||
if not os.path.exists(path):
|
||||
urllib.urlretrieve(url, os.path.join(extract_dir, path))
|
||||
|
||||
if patch is not None:
|
||||
patch_abs = os.path.abspath(os.path.join(os.path.dirname(__file__), patch))
|
||||
if os.path.exists(patch_abs):
|
||||
try: __check_call__(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir)
|
||||
except Exception as e:
|
||||
# Assert that the patch has already been applied
|
||||
__check_call__(["patch", "-p1", "--batch", "--reverse", "--dry-run", "-i", patch_abs], cwd=extract_dir)
|
||||
|
||||
if mode != "bjam":
|
||||
extract_build_dir = os.path.join(extract_dir, "build")
|
||||
if os.path.exists(extract_build_dir):
|
||||
shutil.rmtree(extract_build_dir)
|
||||
os.makedirs(extract_build_dir)
|
||||
|
||||
logger.info("\rConfiguring %s..." % (name,))
|
||||
if mode == "icu":
|
||||
run_icu(name, build_tool_args, cwd=extract_build_dir)
|
||||
elif mode == "autoconf":
|
||||
run_autoconf(name, build_tool_args, cwd=extract_build_dir)
|
||||
elif mode == "cmake":
|
||||
run_cmake(name, build_tool_args, cwd=extract_build_dir)
|
||||
else:
|
||||
raise ValueError()
|
||||
logger.info("\rBuilding %s... " % (name,))
|
||||
__check_call__([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,)], cwd=extract_build_dir)
|
||||
logger.info( "\rInstalling %s... " % (name,))
|
||||
__check_call__([make, "install"], cwd=extract_build_dir)
|
||||
logger.info( "\rInstalled %s \n" % (name,))
|
||||
else:
|
||||
logger.info( "\rConfiguring %s..." % (name,))
|
||||
__check_call__([bash, "./bootstrap.sh"], cwd=extract_dir)
|
||||
logger.info("\rBuilding %s... " % (name,))
|
||||
__check_call__(["./b2", "-j%s" % (IFCOS_NUM_BUILD_PROCS,)]+build_tool_args, cwd=extract_dir)
|
||||
logger.info("\rInstalling %s... " % (name,))
|
||||
shutil.copytree(os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", "boost-%s" % BOOST_VERSION, "boost"))
|
||||
logger.info("\rInstalled %s \n" % (name,))
|
||||
|
||||
cecho("Collecting dependencies:", GREEN)
|
||||
|
||||
# Set compiler flags for 32bit builds on 64bit system
|
||||
# TODO: This is untested
|
||||
|
||||
ADDITIONAL_ARGS=[]
|
||||
BOOST_ADDRESS_MODEL=[]
|
||||
if TARGET_ARCH == "i686" and __check_output__([uname, "-m"]).strip() == "x86_64":
|
||||
ADDITIONAL_ARGS=["-m32", "-arch i386"]
|
||||
BOOST_ADDRESS_MODEL=["architecture=x86", "address-model=32"]
|
||||
|
||||
if get_os() == "Darwin":
|
||||
ADDITIONAL_ARGS=["-mmacosx-version-min=%s" % TOOLSET]+ADDITIONAL_ARGS
|
||||
|
||||
# If the linker supports GC sections, set it up to reduce binary file size
|
||||
# -fPIC is required for the shared libraries to work
|
||||
|
||||
try:
|
||||
CXXFLAGS=os.environ["CXXFLAGS"]
|
||||
except KeyError:
|
||||
CXXFLAGS=""
|
||||
try:
|
||||
CFLAGS=os.environ["CFLAGS"]
|
||||
except KeyError:
|
||||
CFLAGS=""
|
||||
try:
|
||||
LDFLAGS=os.environ["LDFLAGS"]
|
||||
except KeyError:
|
||||
LDFLAGS=""
|
||||
|
||||
if sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0:
|
||||
CXXFLAGS_MINIMAL="%s -fPIC %s" % (CXXFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CXXFLAGS_MINIMAL"]=CXXFLAGS_MINIMAL
|
||||
CFLAGS_MINIMAL="%s -fPIC %s" % (CFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CFLAGS_MINIMAL"]=CFLAGS_MINIMAL
|
||||
CXXFLAGS="%s -fPIC -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CXXFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CXXFLAGS"]=CXXFLAGS
|
||||
CFLAGS="%s -fPIC -fdata-sections -ffunction-sections -fvisibility=hidden %s"% (CFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CFLAGS"]=CFLAGS
|
||||
LDFLAGS="%s -Wl,--gc-sections %s" % (LDFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["LDFLAGS"]=LDFLAGS
|
||||
else:
|
||||
CXXFLAGS_MINIMAL="%s -fPIC %s" % (CXXFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CXXFLAGS_MINIMAL"]=CXXFLAGS_MINIMAL
|
||||
CFLAGS_MINIMAL="%s -fPIC %s" % (CFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CFLAGS_MINIMAL"]=CFLAGS_MINIMAL
|
||||
CXXFLAGS="%s -fPIC -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CXXFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CXXFLAGS"]=CXXFLAGS
|
||||
CFLAGS="%s -fPIC -fvisibility=hidden -fvisibility-inlines-hidden %s" % (CFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
os.environ["CFLAGS"]=CFLAGS
|
||||
LDFLAGS="%s %s" % (LDFLAGS, str.join(" ", ADDITIONAL_ARGS))
|
||||
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"]:
|
||||
__check_call__([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)
|
||||
|
||||
build_dependency(name="pcre-%s" % (PCRE_VERSION,), mode="autoconf", build_tool_args=["--disable-shared"], download_url="https://downloads.sourceforge.net/project/pcre/pcre/%s/" % (PCRE_VERSION,), download_name="pcre-%s.tar.bz2" % (PCRE_VERSION,))
|
||||
|
||||
# An issue exists with swig-1.3 and python >= 3.2
|
||||
# Therefore, build a recent copy from source
|
||||
build_dependency(name="swig", mode="autoconf", build_tool_args=["--with-pcre-prefix=%s/install/pcre-%s" % (DEPS_DIR, PCRE_VERSION)], download_url="https://github.com/swig/swig.git", download_name="swig", download_tool=download_tool_git, revision="rel-%s" % SWIG_VERSION)
|
||||
|
||||
if USE_OCCT:
|
||||
build_dependency(
|
||||
name="occt-%s" % OCCT_VERSION,
|
||||
mode="cmake",
|
||||
build_tool_args=[
|
||||
"-DINSTALL_DIR=%s/install/occt-%s" % (DEPS_DIR, OCCT_VERSION),
|
||||
"-DBUILD_LIBRARY_TYPE=Static",
|
||||
"-DBUILD_MODULE_Draw=0",
|
||||
],
|
||||
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",
|
||||
revision="V%s" % OCCT_VERSION.replace('.', '_'))
|
||||
else:
|
||||
build_dependency(name="oce-%s" % (OCE_VERSION,), mode="cmake", build_tool_args=["-DOCE_DISABLE_TKSERVICE_FONT=ON", "-DOCE_TESTING=OFF", "-DOCE_BUILD_SHARED_LIB=OFF", "-DOCE_DISABLE_X11=ON", "-DOCE_VISUALISATION=OFF", "-DOCE_OCAF=OFF", "-DOCE_INSTALL_PREFIX=%s/install/oce-%s" % (DEPS_DIR, OCE_VERSION)], download_url="https://github.com/tpaviot/oce/archive/", download_name="OCE-%s.tar.gz" % (OCE_VERSION,))
|
||||
|
||||
build_dependency("libxml2-%s" % (LIBXML_VERSION,), "autoconf", build_tool_args=["--without-python", "--disable-shared", "--without-zlib", "--without-iconv", "--without-lzma"], download_url="ftp://xmlsoft.org/libxml2/", download_name="libxml2-%s.tar.gz" % (LIBXML_VERSION,))
|
||||
build_dependency("OpenCOLLADA", "cmake", build_tool_args=["-DLIBXML2_INCLUDE_DIR=%s/install/libxml2-%s/include/libxml2" % (DEPS_DIR, LIBXML_VERSION), "-DLIBXML2_LIBRARIES=%s/install/libxml2-%s/lib/libxml2.a" % (DEPS_DIR, LIBXML_VERSION), "-DPCRE_INCLUDE_DIR=%s/install/pcre-%s/include" % (DEPS_DIR, PCRE_VERSION), "-DPCRE_PCREPOSIX_LIBRARY=%s/install/pcre-%s/lib/libpcreposix.a" % (DEPS_DIR, PCRE_VERSION), "-DPCRE_PCRE_LIBRARY=%s/install/pcre-%s/lib/libpcre.a" % (DEPS_DIR, PCRE_VERSION), "-DCMAKE_INSTALL_PREFIX=%s/install/OpenCOLLADA/" % (DEPS_DIR,)], download_url="https://github.com/KhronosGroup/OpenCOLLADA.git", download_name="OpenCOLLADA", download_tool=download_tool_git, revision=OPENCOLLADA_COMMIT)
|
||||
|
||||
# Python should not be built with -fvisibility=hidden, from experience that introduces segfaults
|
||||
|
||||
OLD_CXX_FLAGS=os.environ["CXXFLAGS"]
|
||||
OLD_C_FLAGS=os.environ["CFLAGS"]
|
||||
os.environ["CXXFLAGS"]=CXXFLAGS_MINIMAL
|
||||
os.environ["CFLAGS"]=CFLAGS_MINIMAL
|
||||
|
||||
# On OSX a dynamic python library is built or it would not be compatible
|
||||
# with the system python because of some threading initialization
|
||||
PYTHON_CONFIGURE_ARGS=[]
|
||||
if get_os() == "Darwin":
|
||||
PYTHON_CONFIGURE_ARGS=["--disable-static", "--enable-shared"]
|
||||
|
||||
def get_python_unicode_confs(py_ver):
|
||||
if py_ver < "3.3":
|
||||
return [("--enable-unicode=ucs2",""), ("--enable-unicode=ucs4","u")]
|
||||
else: return [("","")]
|
||||
|
||||
def PYTHON_VERSION_CONFS():
|
||||
for v in PYTHON_VERSIONS:
|
||||
for unicode_conf, abi_tag in get_python_unicode_confs(v):
|
||||
yield v, unicode_conf, abi_tag
|
||||
|
||||
for PYTHON_VERSION, unicode_conf, abi_tag in PYTHON_VERSION_CONFS():
|
||||
build_dependency("python-%s%s" % (PYTHON_VERSION,abi_tag), "autoconf", PYTHON_CONFIGURE_ARGS + [unicode_conf], "http://www.python.org/ftp/python/%s/" % (PYTHON_VERSION,), "Python-%s.tgz" % (PYTHON_VERSION,))
|
||||
|
||||
os.environ["CXXFLAGS"]=OLD_CXX_FLAGS
|
||||
os.environ["CFLAGS"]=OLD_C_FLAGS
|
||||
|
||||
str_concat = lambda prefix: lambda postfix: "" if postfix.strip() == "" else "=".join((prefix, postfix.strip()))
|
||||
build_dependency("boost-%s" % (BOOST_VERSION,), mode="bjam", build_tool_args=["--stagedir=%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION), "--with-system", "--with-program_options", "--with-regex", "--with-thread", "--with-date_time", "--with-iostreams", "link=static"]+BOOST_ADDRESS_MODEL+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/%s/" % (BOOST_VERSION,), download_name="boost_%s.tar.bz2" % (BOOST_VERSION_UNDERSCORE,))
|
||||
|
||||
build_dependency(name="icu-%s" % (ICU_VERSION,), mode="icu", build_tool_args=["--enable-static", "--disable-shared"], download_url="http://download.icu-project.org/files/icu4c/%s/" % (ICU_VERSION,), download_name="icu4c-%s-src.tgz" % (ICU_VERSION_UNDERSCORE,))
|
||||
|
||||
cecho("Building IfcOpenShell:", GREEN)
|
||||
|
||||
IFCOS_DIR=os.path.join(DEPS_DIR, "build", "ifcopenshell")
|
||||
if os.path.exists(IFCOS_DIR):
|
||||
shutil.rmtree(IFCOS_DIR)
|
||||
os.makedirs(IFCOS_DIR)
|
||||
|
||||
executables_dir = os.path.join(IFCOS_DIR, "executables")
|
||||
if not os.path.exists(executables_dir):
|
||||
os.makedirs(executables_dir)
|
||||
|
||||
logger.info("\rConfiguring executables...")
|
||||
|
||||
if USE_OCCT:
|
||||
occ_include_dir = "%s/install/occt-%s/include/opencascade" % (DEPS_DIR, OCCT_VERSION)
|
||||
occ_library_dir = "%s/install/occt-%s/lib" % (DEPS_DIR, OCCT_VERSION)
|
||||
else:
|
||||
occ_include_dir = "%s/install/oce-%s/include/oce" % (DEPS_DIR, OCE_VERSION)
|
||||
occ_library_dir = "%s/install/oce-%s/lib" % (DEPS_DIR, OCE_VERSION)
|
||||
|
||||
run_cmake("", cmake_args=[
|
||||
"-DBOOST_ROOT=" "%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION),
|
||||
"-DOCC_INCLUDE_DIR=" +occ_include_dir,
|
||||
"-DOCC_LIBRARY_DIR=" +occ_library_dir,
|
||||
"-DOPENCOLLADA_INCLUDE_DIR=" "%s/install/OpenCOLLADA/include/opencollada" % (DEPS_DIR,),
|
||||
"-DOPENCOLLADA_LIBRARY_DIR=" "%s/install/OpenCOLLADA/lib/opencollada" % (DEPS_DIR,),
|
||||
"-DICU_INCLUDE_DIR=" "%s/install/icu-%s/include" % (DEPS_DIR, ICU_VERSION),
|
||||
"-DICU_LIBRARY_DIR=" "%s/install/icu-%s/lib" % (DEPS_DIR, ICU_VERSION),
|
||||
"-DPCRE_LIBRARY_DIR=" "%s/install/pcre-%s/lib" % (DEPS_DIR, PCRE_VERSION),
|
||||
"-DBUILD_IFCPYTHON=" "OFF",
|
||||
"-DUSE_MMAP=" "OFF",
|
||||
"-DCMAKE_INSTALL_PREFIX=" "%s/install/ifcopenshell" % (DEPS_DIR,)], cmake_dir=CMAKE_DIR, cwd=executables_dir)
|
||||
|
||||
logger.info("\rBuilding executables... ")
|
||||
|
||||
__check_call__([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,)], cwd=executables_dir)
|
||||
__check_call__([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=executables_dir)
|
||||
|
||||
# On OSX the actual Python library is not linked against.
|
||||
ADDITIONAL_ARGS=""
|
||||
if get_os() == "Darwin":
|
||||
ADDITIONAL_ARGS="-Wl,-flat_namespace,-undefined,suppress"
|
||||
|
||||
os.environ["CXXFLAGS"]="%s %s" % (CXXFLAGS_MINIMAL, ADDITIONAL_ARGS)
|
||||
os.environ["CFLAGS"]="%s %s" % (CFLAGS_MINIMAL, ADDITIONAL_ARGS)
|
||||
os.environ["LDFLAGS"]="%s %s" % (LDFLAGS, ADDITIONAL_ARGS)
|
||||
|
||||
for PYTHON_VERSION, _, TAG in PYTHON_VERSION_CONFS():
|
||||
logger.info("\rConfiguring python %s%s wrapper..." % (PYTHON_VERSION, TAG))
|
||||
|
||||
python_dir = os.path.join(IFCOS_DIR, "python-%s%s" % (PYTHON_VERSION, TAG))
|
||||
if not os.path.exists(python_dir):
|
||||
os.makedirs(python_dir)
|
||||
|
||||
PYTHON_LIBRARY=__check_output__([bash, "-c", "ls %s/install/python-%s%s/lib/libpython*.*" % (DEPS_DIR, PYTHON_VERSION, TAG)], cwd=None).strip()
|
||||
PYTHON_INCLUDE=__check_output__([bash, "-c", "ls -d %s/install/python-%s%s/include/python*" % (DEPS_DIR, PYTHON_VERSION, TAG)], cwd=None).strip()
|
||||
PYTHON_EXECUTABLE=os.path.join(DEPS_DIR, "install", "python-%s%s" % (PYTHON_VERSION, TAG), "bin", "python%s" % (PYTHON_VERSION[0],))
|
||||
os.environ["PYTHON_LIBRARY_BASENAME"]=os.path.basename(PYTHON_LIBRARY)
|
||||
|
||||
run_cmake("", cmake_args=["-DBOOST_ROOT=%s/install/boost-%s" % (DEPS_DIR, BOOST_VERSION),
|
||||
"-DOCC_INCLUDE_DIR="+occ_include_dir,
|
||||
"-DOCC_LIBRARY_DIR="+occ_library_dir,
|
||||
"-DOPENCOLLADA_INCLUDE_DIR=%s/install/OpenCOLLADA/include/opencollada" % (DEPS_DIR,),
|
||||
"-DOPENCOLLADA_LIBRARY_DIR=%s/install/OpenCOLLADA/lib/opencollada" % (DEPS_DIR,),
|
||||
"-DICU_INCLUDE_DIR=%s/install/icu-%s/include" % (DEPS_DIR, ICU_VERSION),
|
||||
"-DICU_LIBRARY_DIR=%s/install/icu-%s/lib" % (DEPS_DIR, ICU_VERSION),
|
||||
"-DPYTHON_LIBRARY=%s" % (PYTHON_LIBRARY,),
|
||||
"-DPYTHON_EXECUTABLE=%s" % (PYTHON_EXECUTABLE,),
|
||||
"-DPYTHON_INCLUDE_DIR=%s" % (PYTHON_INCLUDE,),
|
||||
"-DSWIG_EXECUTABLE=%s/install/swig/bin/swig" % (DEPS_DIR,),
|
||||
"-DCMAKE_INSTALL_PREFIX=%s/install/ifcopenshell/tmp" % (DEPS_DIR,),
|
||||
"-DCOLLADA_SUPPORT=OFF"], cmake_dir=CMAKE_DIR, cwd=python_dir)
|
||||
|
||||
logger.info("\rBuilding python %s%s wrapper... " % (PYTHON_VERSION, TAG))
|
||||
|
||||
__check_call__([make, "-j%s" % (IFCOS_NUM_BUILD_PROCS,), "_ifcopenshell_wrapper"], cwd=python_dir)
|
||||
__check_call__([make, "install/local"], cwd=os.path.join(python_dir, "ifcwrap"))
|
||||
|
||||
module_dir = os.path.dirname(__check_output__([PYTHON_EXECUTABLE, "-c", "from __future__ import print_function; import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"]))
|
||||
|
||||
if get_os() != "Darwin":
|
||||
# TODO: This symbol name depends on the Python version?
|
||||
__check_call__([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir)
|
||||
|
||||
__check_call__([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", "python-%s%s" % (PYTHON_VERSION, TAG))])
|
||||
|
||||
logger.info("\rBuilt IfcOpenShell...\n\n")
|
||||
@@ -0,0 +1,32 @@
|
||||
http://git.dev.opencascade.org/gitweb/?p=occt.git;a=commitdiff;h=0ab4e621833f4eae945a3762c9a29ee12e2eec53#patch1
|
||||
diff --git a/src/HLRBRep/HLRBRep_InternalAlgo.cxx b/src/HLRBRep/HLRBRep_InternalAlgo.cxx
|
||||
index ca885ca..c13cb06 100644 (file)
|
||||
--- a/src/HLRBRep/HLRBRep_InternalAlgo.cxx
|
||||
+++ b/src/HLRBRep/HLRBRep_InternalAlgo.cxx
|
||||
@@ -165,7 +165,7 @@ void HLRBRep_InternalAlgo::Update ()
|
||||
SB.Bounds(v1,v2,e1,e2,f1,f2);
|
||||
|
||||
for (Standard_Integer e = e1; e <= e2; e++) {
|
||||
- HLRBRep_EdgeData ed = aEDataArray.ChangeValue(e);
|
||||
+ HLRBRep_EdgeData& ed = aEDataArray.ChangeValue(e);
|
||||
HLRAlgo::DecodeMinMax(ed.MinMax(), TheMin, TheMax);
|
||||
if (FirstTime) {
|
||||
FirstTime = Standard_False;
|
||||
@@ -307,7 +307,7 @@ void HLRBRep_InternalAlgo::InitEdgeStatus ()
|
||||
Standard_Integer nf = myDS->NbFaces();
|
||||
|
||||
for (Standard_Integer e = 1; e <= ne; e++) {
|
||||
- HLRBRep_EdgeData ed = aEDataArray.ChangeValue(e);
|
||||
+ HLRBRep_EdgeData& ed = aEDataArray.ChangeValue(e);
|
||||
if (ed.Selected()) ed.Status().ShowAll();
|
||||
}
|
||||
// for (Standard_Integer f = 1; f <= nf; f++) {
|
||||
@@ -368,7 +368,7 @@ void HLRBRep_InternalAlgo::Select ()
|
||||
Standard_Integer nf = myDS->NbFaces();
|
||||
|
||||
for (Standard_Integer e = 1; e <= ne; e++) {
|
||||
- HLRBRep_EdgeData ed = aEDataArray.ChangeValue(e);
|
||||
+ HLRBRep_EdgeData& ed = aEDataArray.ChangeValue(e);
|
||||
ed.Selected(Standard_True);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Description: Enable exception handling
|
||||
Upstream defaults to no exception handling for performance reasons,
|
||||
but in OCCT's role as a shared library it's better for Debian to
|
||||
enable it.
|
||||
Author: Kurt Kremitzki <kkremitzki@gmail.com>
|
||||
Last-Update: 2018-06-10
|
||||
|
||||
--- a/adm/cmake/occt_defs_flags.cmake
|
||||
+++ b/adm/cmake/occt_defs_flags.cmake
|
||||
@@ -138,5 +138,5 @@
|
||||
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
|
||||
endif()
|
||||
|
||||
-set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNo_Exception")
|
||||
-set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNo_Exception")
|
||||
+#set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNo_Exception")
|
||||
+#set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNo_Exception")
|
||||
@@ -1,5 +1,30 @@
|
||||
################################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
ADD_EXECUTABLE(IfcParseExamples IfcParseExamples.cpp)
|
||||
TARGET_LINK_LIBRARIES (IfcParseExamples IfcParse)
|
||||
TARGET_LINK_LIBRARIES(IfcParseExamples IfcParse)
|
||||
set_target_properties(IfcParseExamples PROPERTIES FOLDER Examples)
|
||||
|
||||
ADD_EXECUTABLE(IfcOpenHouse IfcOpenHouse.cpp)
|
||||
TARGET_LINK_LIBRARIES (IfcOpenHouse IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKOffset)
|
||||
TARGET_LINK_LIBRARIES(IfcOpenHouse ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES})
|
||||
set_target_properties(IfcOpenHouse PROPERTIES FOLDER Examples)
|
||||
|
||||
ADD_EXECUTABLE(IfcAdvancedHouse IfcAdvancedHouse.cpp)
|
||||
TARGET_LINK_LIBRARIES(IfcAdvancedHouse ${IFCOPENSHELL_LIBRARIES} ${OPENCASCADE_LIBRARIES})
|
||||
set_target_properties(IfcAdvancedHouse PROPERTIES FOLDER Examples)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <TColgp_Array2OfPnt.hxx>
|
||||
#include <TColgp_Array1OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepBuilderAPI_NurbsConvert.hxx>
|
||||
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <BRepPrimAPI_MakeSphere.hxx>
|
||||
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
#endif
|
||||
|
||||
// The creation of Nurbs-surface for the IfcSite mesh, to be implemented lateron
|
||||
void createGroundShape(TopoDS_Shape& shape);
|
||||
|
||||
int main() {
|
||||
|
||||
// The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several
|
||||
// convenience functions for working with geometry in IFC files.
|
||||
IfcHierarchyHelper file;
|
||||
file.header().file_name().name("IfcAdvancedHouse.ifc");
|
||||
|
||||
IfcSchema::IfcBuilding* building = file.addBuilding();
|
||||
// By adding a building, a hierarchy has been automatically created that consists of the following
|
||||
// structure: IfcProject > IfcSite > IfcBuilding
|
||||
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// project, which has been created automatically.
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("IfcOpenHouse");
|
||||
|
||||
// To demonstrate the ability to serialize arbitrary opencascade solids a building envelope is
|
||||
// constructed by applying boolean operations. Naturally, in IFC, building elements should be
|
||||
// modeled separately, with rich parametric and relational semantics. Creating geometry in this
|
||||
// way does not preserve any history and is merely a demonstration of technical capabilities.
|
||||
TopoDS_Shape outer = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., -2000.), gp_Pnt(5000., 5180., 3000.)).Shape();
|
||||
TopoDS_Shape inner = BRepPrimAPI_MakeBox(gp_Pnt(-4640., 180., 0.), gp_Pnt(4640., 4820., 3000.)).Shape();
|
||||
TopoDS_Shape window1 = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., 400.), gp_Pnt( 500., 1180., 2000.)).Shape();
|
||||
TopoDS_Shape window2 = BRepPrimAPI_MakeBox(gp_Pnt( 2070., -180., 400.), gp_Pnt(3930., 180., 2000.)).Shape();
|
||||
|
||||
TopoDS_Shape building_shell = BRepAlgoAPI_Cut(
|
||||
BRepAlgoAPI_Cut(
|
||||
BRepAlgoAPI_Cut(outer, inner),
|
||||
window1
|
||||
),
|
||||
window2
|
||||
);
|
||||
|
||||
// Since the solid consists only of planar faces and straight edges it can be serialized as an
|
||||
// IfcFacetedBRep. If it would not be a polyhedron, serialise() can only be successful when linked
|
||||
// to the IFC4 model and with `advanced` set to `true` which introduces IfcAdvancedFace. It would
|
||||
// return `0` otherwise.
|
||||
IfcSchema::IfcProductDefinitionShape* building_shape = IfcGeom::serialise(building_shell, false);
|
||||
|
||||
file.addEntity(building_shape);
|
||||
IfcSchema::IfcRepresentation* rep = *building_shape->Representations()->begin();
|
||||
rep->setContextOfItems(file.getRepresentationContext("model"));
|
||||
|
||||
building->setRepresentation(building_shape);
|
||||
|
||||
// A pale white colour is assigned to the building.
|
||||
file.setSurfaceColour(
|
||||
building_shape, 0.75, 0.73, 0.68);
|
||||
|
||||
// For the ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. Only
|
||||
// in IFC4 the surface can be directly serialized. In IFC2X3 the it will have to be tesselated.
|
||||
TopoDS_Shape shape;
|
||||
createGroundShape(shape);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::serialise(shape, true);
|
||||
if (!ground_representation) {
|
||||
ground_representation = IfcGeom::tesselate(shape, 100.);
|
||||
}
|
||||
file.getSingle<IfcSchema::IfcSite>()->setRepresentation(ground_representation);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle<IfcSchema::IfcSite>()->Representation()->Representations();
|
||||
for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
|
||||
(*it)->setContextOfItems(file.getRepresentationContext("Model"));
|
||||
}
|
||||
file.addEntity(ground_representation);
|
||||
file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05);
|
||||
|
||||
/*
|
||||
// Note that IFC lacks elementary surfaces that STEP does have, such as spherical_surface.
|
||||
// BRepBuilderAPI_NurbsConvert can be used to serialize such surfaces as nurbs surfaces.
|
||||
TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(gp_Pnt(), 1000.).Shape();
|
||||
IfcSchema::IfcProductDefinitionShape* sphere_representation = IfcGeom::serialise(sphere, true);
|
||||
if (S(IfcSchema::Identifier) == "IFC4") {
|
||||
sphere = BRepBuilderAPI_NurbsConvert(sphere, true).Shape();
|
||||
sphere_representation = IfcGeom::serialise(sphere, true);
|
||||
}
|
||||
*/
|
||||
|
||||
// Finally create a file stream for our output and write the IFC file to it.
|
||||
std::ofstream f("IfcAdvancedHouse.ifc");
|
||||
f << file;
|
||||
}
|
||||
|
||||
void createGroundShape(TopoDS_Shape& shape) {
|
||||
TColgp_Array2OfPnt cv (0, 4, 0, 4);
|
||||
cv.SetValue(0, 0, gp_Pnt(-10000, -10000, -4130));
|
||||
cv.SetValue(0, 1, gp_Pnt(-10000, -4330, -4130));
|
||||
cv.SetValue(0, 2, gp_Pnt(-10000, 0, -5130));
|
||||
cv.SetValue(0, 3, gp_Pnt(-10000, 4330, -7130));
|
||||
cv.SetValue(0, 4, gp_Pnt(-10000, 10000, -7130));
|
||||
cv.SetValue(1, 0, gp_Pnt( -3330, -10000, -5130));
|
||||
cv.SetValue(1, 1, gp_Pnt( -7670, -3670, 5000));
|
||||
cv.SetValue(1, 2, gp_Pnt( -9000, 0, 1000));
|
||||
cv.SetValue(1, 3, gp_Pnt( -7670, 7670, 6000));
|
||||
cv.SetValue(1, 4, gp_Pnt( -3330, 10000, -4130));
|
||||
cv.SetValue(2, 0, gp_Pnt( 0, -10000, -5530));
|
||||
cv.SetValue(2, 1, gp_Pnt( 0, -3670, 3000));
|
||||
cv.SetValue(2, 2, gp_Pnt( 0, 0, -12000));
|
||||
cv.SetValue(2, 3, gp_Pnt( 0, 7670, 1500));
|
||||
cv.SetValue(2, 4, gp_Pnt( 0, 10000, -4130));
|
||||
cv.SetValue(3, 0, gp_Pnt( 3330, -10000, -6130));
|
||||
cv.SetValue(3, 1, gp_Pnt( 7670, -3670, 6000));
|
||||
cv.SetValue(3, 2, gp_Pnt( 9000, 0, 5000));
|
||||
cv.SetValue(3, 3, gp_Pnt( 7670, 9000, 7000));
|
||||
cv.SetValue(3, 4, gp_Pnt( 3330, 10000, -4130));
|
||||
cv.SetValue(4, 0, gp_Pnt( 10000, -10000, -6130));
|
||||
cv.SetValue(4, 1, gp_Pnt( 10000, -4330, -5130));
|
||||
cv.SetValue(4, 2, gp_Pnt( 10000, 0, -4130));
|
||||
cv.SetValue(4, 3, gp_Pnt( 10000, 4330, -4130));
|
||||
cv.SetValue(4, 4, gp_Pnt( 10000, 10000, -8130));
|
||||
TColStd_Array1OfReal knots(0, 1);
|
||||
knots(0) = 0;
|
||||
knots(1) = 1;
|
||||
TColStd_Array1OfInteger mult(0, 1);
|
||||
mult(0) = 5;
|
||||
mult(1) = 5;
|
||||
Handle(Geom_BSplineSurface) surf = new Geom_BSplineSurface(cv, knots, knots, mult, mult, 4, 4);
|
||||
#if OCC_VERSION_HEX < 0x60502
|
||||
shape = BRepBuilderAPI_MakeFace(surf);
|
||||
#else
|
||||
shape = BRepBuilderAPI_MakeFace(surf, Precision::Confusion());
|
||||
#endif
|
||||
}
|
||||
@@ -37,20 +37,24 @@
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
#endif
|
||||
|
||||
// Some convenience typedefs and definitions.
|
||||
typedef std::string S;
|
||||
typedef IfcWrite::IfcGuidHelper guid;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef std::pair<double, double> XY;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
// The creation of Nurbs-surface for the IfcSite mesh, to be implemented lateron
|
||||
void createGroundShape(TopoDS_Shape& shape);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main() {
|
||||
|
||||
// The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several
|
||||
// convenience functions for working with geometry in IFC files.
|
||||
@@ -60,12 +64,12 @@ int main(int argc, char** argv) {
|
||||
// Start by adding a wall to the file, initially leaving most attributes blank.
|
||||
IfcSchema::IfcWallStandardCase* south_wall = new IfcSchema::IfcWallStandardCase(
|
||||
guid(), // GlobalId
|
||||
0, // OwnerHistory
|
||||
0, // OwnerHistory
|
||||
S("South wall"), // Name
|
||||
null, // Description
|
||||
null, // ObjectType
|
||||
null, // ObjectPlacement
|
||||
null, // Representation
|
||||
0, // ObjectPlacement
|
||||
0, // Representation
|
||||
null // Tag
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
@@ -78,10 +82,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// project, which has been created automatically.
|
||||
file.getSingle<IfcSchema::IfcProject>()->Name(S("IfcOpenHouse"));
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("IfcOpenHouse");
|
||||
|
||||
// An IfcOwnerHistory has been initialized as well, which should be assigned to the wall.
|
||||
south_wall->OwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
south_wall->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
// The wall will be shaped as a box, with the dimensions specified in millimeters. The resulting
|
||||
// product definition will consist of both a body representation as well as an axis representation
|
||||
@@ -90,12 +94,12 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Obtain a reference to the placement of the IfcBuildingStorey in order to create a hierarchy
|
||||
// of placements for the products
|
||||
IfcSchema::IfcObjectPlacement* storey_placement = *file.getSingle<IfcSchema::IfcBuildingStorey>()->ObjectPlacement();
|
||||
IfcSchema::IfcObjectPlacement* storey_placement = file.getSingle<IfcSchema::IfcBuildingStorey>()->ObjectPlacement();
|
||||
|
||||
// The shape has to be assigned to the representation of the wall and is placed at the origin
|
||||
// of the coordinate system.
|
||||
south_wall->Representation(south_wall_shape);
|
||||
south_wall->ObjectPlacement(file.addLocalPlacement(storey_placement));
|
||||
south_wall->setRepresentation(south_wall_shape);
|
||||
south_wall->setObjectPlacement(file.addLocalPlacement(storey_placement));
|
||||
|
||||
// A pale white colour is assigned to the wall.
|
||||
IfcSchema::IfcPresentationStyleAssignment* wall_colour = file.setSurfaceColour(
|
||||
@@ -103,17 +107,17 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Now create a footing for the wall to rest on.
|
||||
IfcSchema::IfcFooting* footing = new IfcSchema::IfcFooting(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
S("Footing"), null, null, null, null, null, IfcSchema::IfcFootingTypeEnum::IfcFootingType_STRIP_FOOTING);
|
||||
S("Footing"), null, null, 0, 0, null, IfcSchema::IfcFootingTypeEnum::IfcFootingType_STRIP_FOOTING);
|
||||
|
||||
file.addBuildingProduct(footing);
|
||||
|
||||
// The footing will span the entire floor plan of our building. The IfcRepresentationContext is
|
||||
// something that has been created automatically as well, but representations could have been
|
||||
// assigned to a specific context, for example to add a two dimensional plan representation as well.
|
||||
footing->Representation(file.addBox(10100, 5460, 2000));
|
||||
footing->ObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000));
|
||||
footing->setRepresentation(file.addBox(10100, 5460, 2000));
|
||||
footing->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000));
|
||||
// The footing will have a dark gray colour
|
||||
IfcSchema::IfcPresentationStyleAssignment* footing_colour = file.setSurfaceColour(*footing->Representation(), 0.26, 0.22, 0.18);
|
||||
IfcSchema::IfcPresentationStyleAssignment* footing_colour = file.setSurfaceColour(footing->Representation(), 0.26, 0.22, 0.18);
|
||||
|
||||
// IFC has two ways to apply boolean operations to geometry. IfcBooleanResults are commonly used
|
||||
// to clip geometry to a surface, for example to a slanted roof. For openings that are filled
|
||||
@@ -121,7 +125,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
// An opening element is created with rectangular geometry:
|
||||
IfcSchema::IfcOpeningElement* west_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(*south_wall->ObjectPlacement(), -2500, 0, 400),
|
||||
null, null, null, file.addLocalPlacement(south_wall->ObjectPlacement(), -2500, 0, 400),
|
||||
file.addBox(6000, 3630, 1600), null
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
|
||||
@@ -147,7 +151,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Create a roof element that will consist of two slabs:
|
||||
IfcSchema::IfcRoof* roof = new IfcSchema::IfcRoof(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), S("Roof"), null, null,
|
||||
file.addLocalPlacement(storey_placement), null, null, IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
|
||||
file.addLocalPlacement(storey_placement), 0, null, IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
|
||||
|
||||
// The roof geometry is slanted 45 degrees by specifying a direction for the box extrusion
|
||||
IfcSchema::IfcShapeRepresentation* roof_rep = file.addEmptyRepresentation();
|
||||
@@ -156,21 +160,21 @@ int main(int argc, char** argv) {
|
||||
|
||||
// CV-2x3-144: Roofs are aggregates and shall have at least one contained element and no own geometry
|
||||
IfcSchema::IfcSlab* south_roof_part = new IfcSchema::IfcSlab(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), S("South roof"),
|
||||
null, null, null, null, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
null, null, 0, 0, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
|
||||
// The geometry is instantiated by using IfcMappedItems. This way geometry definitions can
|
||||
// be reused while maintaining the cardinality constraint that the ShapeOfProduct relation
|
||||
// imposes on the IfcProductDefinitionShape. Note that this constrained is lifted in IFC4.
|
||||
south_roof_part->Representation(file.addMappedItem(roof_rep));
|
||||
south_roof_part->ObjectPlacement(file.addLocalPlacement(*roof->ObjectPlacement(), 0, -400, 2700));
|
||||
south_roof_part->setRepresentation(file.addMappedItem(roof_rep));
|
||||
south_roof_part->setObjectPlacement(file.addLocalPlacement(roof->ObjectPlacement(), 0, -400, 2700));
|
||||
|
||||
// The same roof geometry is re-used on the north side of the roof, by inverting the X-axis of
|
||||
// the local placement the roof is rotated 180 degrees around the Z-axis
|
||||
IfcSchema::IfcSlab* north_roof_part = new IfcSchema::IfcSlab(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), S("North roof"),
|
||||
null, null, null, null, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
north_roof_part->OwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
north_roof_part->Representation(file.addMappedItem(roof_rep));
|
||||
north_roof_part->ObjectPlacement(file.addLocalPlacement(*roof->ObjectPlacement(), 0, 5400, 2700, 0, 0, 1, -1, 0, 0));
|
||||
null, null, 0, 0, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
north_roof_part->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
north_roof_part->setRepresentation(file.addMappedItem(roof_rep));
|
||||
north_roof_part->setObjectPlacement(file.addLocalPlacement(roof->ObjectPlacement(), 0, 5400, 2700, 0, 0, 1, -1, 0, 0));
|
||||
|
||||
IfcSchema::IfcObjectDefinition::list::ptr roof_parts(new IfcSchema::IfcObjectDefinition::list);
|
||||
roof_parts->push(south_roof_part);
|
||||
@@ -193,47 +197,50 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(north_wall);
|
||||
file.setSurfaceColour(*north_wall->Representation(), wall_colour);
|
||||
file.setSurfaceColour(north_wall->Representation(), wall_colour);
|
||||
|
||||
IfcSchema::IfcShapeRepresentation* clipped_wall_body_rep = file.addEmptyRepresentation();
|
||||
file.addBox(clipped_wall_body_rep, 5000, 360, 6000);
|
||||
// The east wall geometry is clipped using two IfcHalfSpaceSolids, created from an
|
||||
// 'axis 3d placement' that specifies the plane against which the geometry is clipped.
|
||||
file.clipRepresentation(clipped_wall_body_rep, file.addPlacement3d(-2500, 0, 3000, -1, 0, 1), false);
|
||||
file.clipRepresentation(clipped_wall_body_rep, file.addPlacement3d(2500, 0, 3000, 1, 0, 1), false);
|
||||
// Two identical representations are created for the two remaining walls. Mapped items
|
||||
// are not used, because it is not allowed by the standard for wall body representations.
|
||||
// MappedItems are not allowed for Axis representations as per CV-2x3-161
|
||||
IfcSchema::IfcProductDefinitionShape* clipped_wall_body_reps[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
IfcSchema::IfcShapeRepresentation* body = file.addEmptyRepresentation();
|
||||
file.addBox(body, 5000, 360, 6000);
|
||||
// The wall geometry is clipped using two IfcHalfSpaceSolids, created from an
|
||||
// 'axis 3d placement' that specifies the plane against which the geometry is clipped.
|
||||
file.clipRepresentation(body, file.addPlacement3d(-2500, 0, 3000, -1, 0, 1), false);
|
||||
file.clipRepresentation(body, file.addPlacement3d(2500, 0, 3000, 1, 0, 1), false);
|
||||
file.setSurfaceColour(body, wall_colour);
|
||||
|
||||
IfcSchema::IfcShapeRepresentation* axis = file.addEmptyRepresentation("Axis", "Curve2D");
|
||||
file.addAxis(axis, 5000);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
|
||||
reps->push(body);
|
||||
reps->push(axis);
|
||||
clipped_wall_body_reps[i] = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
}
|
||||
|
||||
// Now create a wall on the east of the building, again starting with just a box shape
|
||||
IfcSchema::IfcWallStandardCase* east_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
S("East wall"), null, null, file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0), file.addMappedItem(clipped_wall_body_rep), null
|
||||
S("East wall"), null, null, file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0), clipped_wall_body_reps[0], null
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(east_wall);
|
||||
|
||||
file.setSurfaceColour(clipped_wall_body_rep, wall_colour);
|
||||
|
||||
// The east wall is copied to the west location of the house
|
||||
IfcSchema::IfcWallStandardCase* west_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
S("West wall"), null, null, file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0), file.addMappedItem(clipped_wall_body_rep), null
|
||||
S("West wall"), null, null, file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0), clipped_wall_body_reps[1], null
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(west_wall);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// CV-2x3-161: MappedItems are not allowed for Axis representations
|
||||
IfcSchema::IfcWallStandardCase* wall = i == 0 ? east_wall : west_wall;
|
||||
IfcSchema::IfcShapeRepresentation* wall_axis_rep = file.addEmptyRepresentation("Axis", "Curve2D");
|
||||
file.addAxis(wall_axis_rep, 5000);
|
||||
IfcSchema::IfcRepresentation::list::ptr reps = wall->Representation().get()->Representations();
|
||||
reps->push(wall_axis_rep);
|
||||
wall->Representation().get()->Representations(reps);
|
||||
}
|
||||
|
||||
// The west wall is assigned an opening element we created for the south wall, opening elements are
|
||||
// not shared accross building elements, even if they share the same representation. Hence, the east
|
||||
// not shared across building elements, even if they share the same representation. Hence, the east
|
||||
// wall will not feature this opening.
|
||||
// NB: an Opening Element can only be used to create a single void within a single Element, as per:
|
||||
// http://www.buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcproductextension/lexical/ifcfeatureelementsubtraction.htm
|
||||
@@ -241,7 +248,7 @@ int main(int argc, char** argv) {
|
||||
// Not all viewers support opening elements with mapped representations, hence an exact copy of the
|
||||
// same subtraction box is instantiated for the otherwise identical opening element.
|
||||
IfcSchema::IfcOpeningElement* west_opening_copy = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(*west_wall->ObjectPlacement(), 2500, -2500+4820, 400, 0, 0, 1, 0, 1, 0),
|
||||
null, null, null, file.addLocalPlacement(west_wall->ObjectPlacement(), 2500, -2500+4820, 400, 0, 0, 1, 0, 1, 0),
|
||||
file.addBox(6000, 3630, 1600), null
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
|
||||
@@ -255,14 +262,28 @@ int main(int argc, char** argv) {
|
||||
// will be tesselated using the deflection specified.
|
||||
TopoDS_Shape shape;
|
||||
createGroundShape(shape);
|
||||
IfcEntityList::ptr geometrical_entities(new IfcEntityList);
|
||||
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100., geometrical_entities);
|
||||
file.getSingle<IfcSchema::IfcSite>()->Representation(ground_representation);
|
||||
file.addEntities(geometrical_entities);
|
||||
IfcSchema::IfcShapeRepresentation::list::ptr ground_reps = geometrical_entities->as<IfcSchema::IfcShapeRepresentation>();
|
||||
for (IfcSchema::IfcShapeRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
|
||||
(*it)->ContextOfItems(file.getRepresentationContext("Model"));
|
||||
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100.);
|
||||
file.getSingle<IfcSchema::IfcSite>()->setRepresentation(ground_representation);
|
||||
|
||||
// Relate a property set to the IfcSite
|
||||
const double site_area = IfcGeom::Kernel::face_area(TopoDS::Face(shape)) / 1000 / 1000;
|
||||
IfcSchema::IfcProperty::list::ptr properties(new IfcSchema::IfcProperty::list);
|
||||
properties->push(new IfcSchema::IfcPropertySingleValue("TotalArea", null, new IfcSchema::IfcAreaMeasure(site_area), 0));
|
||||
IfcSchema::IfcPropertySet* pset = new IfcSchema::IfcPropertySet(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), S("Pset_SiteCommon"), null, properties);
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcObjectDefinition::list::ptr related_objs(new IfcSchema::IfcObjectDefinition::list);
|
||||
#else
|
||||
IfcSchema::IfcObject::list::ptr related_objs(new IfcSchema::IfcObject::list);
|
||||
#endif
|
||||
related_objs->push(file.getSingle<IfcSchema::IfcSite>());
|
||||
IfcSchema::IfcRelDefinesByProperties* site_prop = new IfcSchema::IfcRelDefinesByProperties(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, related_objs, pset);
|
||||
file.addEntity(site_prop);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle<IfcSchema::IfcSite>()->Representation()->Representations();
|
||||
for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
|
||||
(*it)->setContextOfItems(file.getRepresentationContext("Model"));
|
||||
}
|
||||
file.addEntity(ground_representation);
|
||||
file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05);
|
||||
|
||||
// According to the Ifc2x3 schema an IfcWallStandardCase needs to have an IfcMaterialLayerSet
|
||||
@@ -343,7 +364,7 @@ int main(int argc, char** argv) {
|
||||
);
|
||||
|
||||
file.addBuildingProduct(stair);
|
||||
file.setSurfaceColour(*stair->Representation(), footing_colour);
|
||||
file.setSurfaceColour(stair->Representation(), footing_colour);
|
||||
|
||||
IfcSchema::IfcOpeningElement* door_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(storey_placement, 5000-180, 2500-900, 0), file.addBox(1000, 1000, 2200), null
|
||||
@@ -358,19 +379,19 @@ int main(int argc, char** argv) {
|
||||
// can be a composition of multiple solids. The following door will be composed of four boxes
|
||||
// which constitute the door and its frame.
|
||||
IfcSchema::IfcDoor* door = new IfcSchema::IfcDoor(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, null,
|
||||
file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0), null, null, 2200, 1000
|
||||
file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0), 0, null, 2200, 1000
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR
|
||||
, IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT
|
||||
, null
|
||||
#endif
|
||||
);
|
||||
door->Representation(file.addBox(80, 80, 2120, 0, file.addPlacement3d(460, 0, 0)));
|
||||
IfcSchema::IfcRepresentation::list::ptr door_representations = door->Representation().get()->Representations();
|
||||
door->setRepresentation(file.addBox(80, 80, 2120, 0, file.addPlacement3d(460, 0, 0)));
|
||||
IfcSchema::IfcRepresentation::list::ptr door_representations = door->Representation()->Representations();
|
||||
IfcSchema::IfcShapeRepresentation* door_body = 0;
|
||||
for (IfcSchema::IfcRepresentation::list::it i = door_representations->begin(); i != door_representations->end(); ++i) {
|
||||
IfcSchema::IfcRepresentation* rep = *i;
|
||||
if (rep->is(IfcSchema::Type::IfcShapeRepresentation) && *rep->RepresentationIdentifier() == "Body") {
|
||||
if (rep->is(IfcSchema::Type::IfcShapeRepresentation) && rep->RepresentationIdentifier() == "Body") {
|
||||
door_body = (IfcSchema::IfcShapeRepresentation*) rep;
|
||||
}
|
||||
}
|
||||
@@ -378,16 +399,20 @@ int main(int argc, char** argv) {
|
||||
file.addBox(door_body, 1000, 80, 80, 0, file.addPlacement3d( 0, 0, 2120));
|
||||
file.addBox(door_body, 860, 30, 2120);
|
||||
file.addBuildingProduct(door);
|
||||
file.setSurfaceColour(*door->Representation(), 0.9, 0.9, 0.9);
|
||||
file.setSurfaceColour(door->Representation(), 0.9, 0.9, 0.9);
|
||||
file.addEntity(new IfcSchema::IfcRelFillsElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, door_opening, door));
|
||||
|
||||
IfcSchema::IfcDoorStyle* door_style = new IfcSchema::IfcDoorStyle(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), S("Door type"), null, null, null, null, null,
|
||||
IfcSchema::IfcDoorStyleOperationEnum::IfcDoorStyleOperation_SINGLE_SWING_LEFT, IfcSchema::IfcDoorStyleConstructionEnum::IfcDoorStyleConstruction_WOOD, false, false);
|
||||
file.addRelatedObject<IfcSchema::IfcRelDefinesByType>(door_style, door);
|
||||
|
||||
// Surface styles are assigned to representation items, hence there is no real limitation to
|
||||
// assign different colours within the same representation. However, some viewers have
|
||||
// difficulties rendering products with representation items with different surface styles.
|
||||
// Therefore we will construct the window as a decomposition of beams and a plate, in which
|
||||
// only the plate will have a transparent material assigned.
|
||||
|
||||
// The window frame will consists of four seperate beams.
|
||||
// The window frame will consists of four separate beams.
|
||||
// AutoCAD Architecture will create an internal window type for the IfcWindow created.
|
||||
// Therefore the OverallWidth and OverallHeight of the window attributes will need to
|
||||
// match the bounding box of the representation. Furthermore, the window placement needs
|
||||
@@ -429,7 +454,7 @@ int main(int argc, char** argv) {
|
||||
// Create the window at the current location
|
||||
IfcSchema::IfcLocalPlacement* place = *it;
|
||||
IfcSchema::IfcWindow* window = new IfcSchema::IfcWindow(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, place, null, null, 1600, 1860
|
||||
null, null, null, place, 0, null, 1600, 1860
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW
|
||||
, IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL
|
||||
@@ -438,10 +463,10 @@ int main(int argc, char** argv) {
|
||||
);
|
||||
file.addBuildingProduct(window);
|
||||
|
||||
// Initalize a list of parts for the window to be composed of
|
||||
// Initialize a list of parts for the window to be composed of
|
||||
IfcSchema::IfcObjectDefinition::list::ptr window_parts(new IfcTemplatedEntityList<IfcSchema::IfcObjectDefinition>());
|
||||
|
||||
// The placements for the beams are not shared accross the different windows because every
|
||||
// The placements for the beams are not shared across the different windows because every
|
||||
// beam is placed relative to its parent window entity.
|
||||
IfcSchema::IfcLocalPlacement::list::ptr frame_placements (new IfcTemplatedEntityList<IfcSchema::IfcLocalPlacement>());
|
||||
frame_placements->push(file.addLocalPlacement(storey_placement, 930,45));
|
||||
@@ -477,7 +502,7 @@ int main(int argc, char** argv) {
|
||||
file.addEntity(glass_part);
|
||||
window_parts->push(glass_part);
|
||||
file.relatePlacements(window, glass_part);
|
||||
file.setSurfaceColour(*glass_part->Representation(), 0.6, 0.7, 0.75, 0.1);
|
||||
file.setSurfaceColour(glass_part->Representation(), 0.6, 0.7, 0.75, 0.1);
|
||||
|
||||
// Now create a decomposition relation between the window and the parts. Most viewers and authoring
|
||||
// tools will consider the window a single entity that can be selected as a whole.
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include "../ifcparse/IfcFile.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
#endif
|
||||
|
||||
using namespace IfcSchema;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -66,8 +70,8 @@ int main(int argc, char** argv) {
|
||||
if ( element->is(IfcWindow::Class()) ) {
|
||||
const IfcWindow* window = (IfcWindow*)element;
|
||||
|
||||
if ( window->OverallWidth() && window->OverallHeight() ) {
|
||||
const double area = window->OverallWidth().get() * window->OverallHeight().get();
|
||||
if ( window->hasOverallWidth() && window->hasOverallHeight() ) {
|
||||
const double area = window->OverallWidth()*window->OverallHeight();
|
||||
std::cout << "The area of this window is " << area << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcWrite::IfcGuidHelper guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
static int i = 0;
|
||||
|
||||
void create_product_from_item(IfcHierarchyHelper& file, IfcSchema::IfcRepresentationItem* item, const std::string& s) {
|
||||
@@ -43,16 +43,16 @@ void create_product_from_item(IfcHierarchyHelper& file, IfcSchema::IfcRepresenta
|
||||
file.addBuildingProduct(product);
|
||||
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement(120 * i++));
|
||||
product->setObjectPlacement(file.addLocalPlacement(0, 120 * i++));
|
||||
|
||||
IfcSchema::IfcRepresentation::list reps (new IfcTemplatedEntityList<IfcSchema::IfcRepresentation>());
|
||||
IfcSchema::IfcRepresentationItem::list items (new IfcTemplatedEntityList<IfcSchema::IfcRepresentationItem>());
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
|
||||
items->push(item);
|
||||
|
||||
if (s == "GeometricSet") {
|
||||
IfcSchema::IfcGeometricSet* set = new IfcSchema::IfcGeometricSet(items->generalize());
|
||||
file.addEntity(set);
|
||||
items = IfcSchema::IfcRepresentationItem::list(new IfcTemplatedEntityList<IfcSchema::IfcRepresentationItem>());
|
||||
items = IfcSchema::IfcRepresentationItem::list::ptr(new IfcSchema::IfcRepresentationItem::list());
|
||||
items->push(set);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ void create_product_from_item(IfcHierarchyHelper& file, IfcSchema::IfcRepresenta
|
||||
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), s, items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
|
||||
@@ -109,11 +109,11 @@ void create_products_from_curve(IfcHierarchyHelper& file, IfcSchema::IfcBoundedC
|
||||
int main(int argc, char** argv) {
|
||||
const char filename[] = "IfcArbitraryOpenProfileDef.ifc";
|
||||
IfcHierarchyHelper file;
|
||||
file.filename(filename);
|
||||
file.header().file_name().name(filename);
|
||||
|
||||
double coords1[] = {-50.0, 0.0};
|
||||
double coords2[] = { 50.0, 0.0};
|
||||
IfcSchema::IfcCartesianPoint::list points (new IfcTemplatedEntityList<IfcSchema::IfcCartesianPoint>());
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list());
|
||||
points->push(new IfcSchema::IfcCartesianPoint(std::vector<double>(coords1, coords1+2)));
|
||||
points->push(new IfcSchema::IfcCartesianPoint(std::vector<double>(coords2, coords2+2)));
|
||||
file.addEntities(points->generalize());
|
||||
@@ -126,8 +126,8 @@ int main(int argc, char** argv) {
|
||||
file.addEntity(ellipse);
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
trim1->push(new IfcWrite::IfcSelectHelper( 0., Ifc2x3::Type::IfcParameterValue));
|
||||
trim2->push(new IfcWrite::IfcSelectHelper(180., Ifc2x3::Type::IfcParameterValue));
|
||||
trim1->push(new IfcSchema::IfcParameterValue( 0.));
|
||||
trim2->push(new IfcSchema::IfcParameterValue(180.));
|
||||
IfcSchema::IfcTrimmedCurve* trim = new IfcSchema::IfcTrimmedCurve(ellipse, trim1, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
file.addEntity(trim);
|
||||
|
||||
|
||||
@@ -32,19 +32,19 @@
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcWrite::IfcGuidHelper guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char filename[] = "IfcCompositeProfileDef.ifc";
|
||||
IfcHierarchyHelper file;
|
||||
file.filename(filename);
|
||||
file.header().file_name().name(filename);
|
||||
|
||||
double coords1[] = {100.0, 0.0};
|
||||
double coords2[] = {200.0, 0.0};
|
||||
double coords3[] = {300.0, 0.0};
|
||||
|
||||
IfcSchema::IfcProfileDef::list profiles (new IfcTemplatedEntityList<IfcSchema::IfcProfileDef>());
|
||||
IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list());
|
||||
|
||||
IfcSchema::IfcCartesianTransformationOperator2D* transform1 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet<IfcSchema::IfcDirection>(1, 0), file.addDoublet<IfcSchema::IfcDirection>(0, -1), file.addDoublet<IfcSchema::IfcCartesianPoint>(40, 0), null);
|
||||
IfcSchema::IfcCartesianTransformationOperator2D* transform2 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet<IfcSchema::IfcDirection>(0, -1), file.addDoublet<IfcSchema::IfcDirection>(1, 0), file.addDoublet<IfcSchema::IfcCartesianPoint>(40, 0), 0.3);
|
||||
@@ -98,15 +98,15 @@ int main(int argc, char** argv) {
|
||||
file.addEntity(composite);
|
||||
file.addEntity(solid);
|
||||
|
||||
IfcSchema::IfcRepresentation::list reps (new IfcTemplatedEntityList<IfcSchema::IfcRepresentation>());
|
||||
IfcSchema::IfcRepresentationItem::list items (new IfcTemplatedEntityList<IfcSchema::IfcRepresentationItem>());
|
||||
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("SweptSolid"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcWrite::IfcGuidHelper guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
class Node {
|
||||
private:
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
int main(int argc, char** argv) {
|
||||
const char filename[] = "IfcCsgPrimitive.ifc";
|
||||
IfcHierarchyHelper file;
|
||||
file.filename(filename);
|
||||
file.header().file_name().name(filename);
|
||||
|
||||
IfcSchema::IfcRepresentationItem* csg1 = Node::Box(8000.,6000.,3000.).subtract(
|
||||
Node::Box(7600.,5600.,2800.).move(200.,200.,200.)
|
||||
@@ -171,8 +171,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement());
|
||||
|
||||
IfcSchema::IfcRepresentation::list reps (new IfcTemplatedEntityList<IfcSchema::IfcRepresentation>());
|
||||
IfcSchema::IfcRepresentationItem::list items (new IfcTemplatedEntityList<IfcSchema::IfcRepresentationItem>());
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
|
||||
|
||||
items->push(csg1);
|
||||
items->push(csg2);
|
||||
@@ -180,7 +180,7 @@ int main(int argc, char** argv) {
|
||||
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("CSG"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcWrite::IfcGuidHelper guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
typedef struct {
|
||||
double r1;
|
||||
@@ -58,7 +58,7 @@ void create_testcase_for(IfcHierarchyHelper& file, const EllipsePie& pie, Ifc2x3
|
||||
Ifc2x3::IfcCartesianPoint* p2 = new Ifc2x3::IfcCartesianPoint(coords2);
|
||||
Ifc2x3::IfcCartesianPoint* p3 = new Ifc2x3::IfcCartesianPoint(coords3);
|
||||
|
||||
Ifc2x3::IfcCartesianPoint::list points(new IfcTemplatedEntityList<Ifc2x3::IfcCartesianPoint>());
|
||||
Ifc2x3::IfcCartesianPoint::list::ptr points(new Ifc2x3::IfcCartesianPoint::list());
|
||||
points->push(p3);
|
||||
points->push(p1);
|
||||
points->push(p2);
|
||||
@@ -70,8 +70,8 @@ void create_testcase_for(IfcHierarchyHelper& file, const EllipsePie& pie, Ifc2x3
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
if (pref == Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER) {
|
||||
trim1->push(new IfcWrite::IfcSelectHelper(pie.t1, Ifc2x3::Type::IfcParameterValue));
|
||||
trim2->push(new IfcWrite::IfcSelectHelper(pie.t2, Ifc2x3::Type::IfcParameterValue));
|
||||
trim1->push(new Ifc2x3::IfcParameterValue(pie.t1));
|
||||
trim2->push(new Ifc2x3::IfcParameterValue(pie.t2));
|
||||
} else {
|
||||
trim1->push(p2);
|
||||
trim2->push(p3);
|
||||
@@ -79,7 +79,7 @@ void create_testcase_for(IfcHierarchyHelper& file, const EllipsePie& pie, Ifc2x3
|
||||
Ifc2x3::IfcTrimmedCurve* trim = new Ifc2x3::IfcTrimmedCurve(ellipse, trim1, trim2, true, pref);
|
||||
file.addEntity(trim);
|
||||
|
||||
Ifc2x3::IfcCompositeCurveSegment::list segments(new IfcTemplatedEntityList<Ifc2x3::IfcCompositeCurveSegment>());
|
||||
Ifc2x3::IfcCompositeCurveSegment::list::ptr segments(new Ifc2x3::IfcCompositeCurveSegment::list());
|
||||
Ifc2x3::IfcCompositeCurveSegment* s2 = new Ifc2x3::IfcCompositeCurveSegment(Ifc2x3::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, trim);
|
||||
|
||||
Ifc2x3::IfcPolyline* poly = new Ifc2x3::IfcPolyline(points);
|
||||
@@ -100,22 +100,22 @@ void create_testcase_for(IfcHierarchyHelper& file, const EllipsePie& pie, Ifc2x3
|
||||
file.addBuildingProduct(product);
|
||||
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement(200 * i++));
|
||||
product->setObjectPlacement(file.addLocalPlacement(0, 200 * i++));
|
||||
|
||||
IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(profile,
|
||||
file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);
|
||||
|
||||
file.addEntity(solid);
|
||||
|
||||
IfcSchema::IfcRepresentation::list reps (new IfcTemplatedEntityList<IfcSchema::IfcRepresentation>());
|
||||
IfcSchema::IfcRepresentationItem::list items (new IfcTemplatedEntityList<IfcSchema::IfcRepresentationItem>());
|
||||
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("SweptSolid"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 that generates various forms of IfcFace *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
|
||||
static int x = 0;
|
||||
|
||||
void create_testcase(IfcHierarchyHelper& file, IfcSchema::IfcFace* face, const std::string& name) {
|
||||
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
|
||||
faces->push(face);
|
||||
IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces);
|
||||
|
||||
IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list);
|
||||
shells->push(shell);
|
||||
IfcSchema::IfcFaceBasedSurfaceModel* model = new IfcSchema::IfcFaceBasedSurfaceModel(shells);
|
||||
|
||||
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
|
||||
guid(), 0, name, null, null, 0, 0, null, null);
|
||||
file.addBuildingProduct(product);
|
||||
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement(0, 1000 * x++, 0));
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);
|
||||
|
||||
items->push(model);
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
file.addEntity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
IfcHierarchyHelper file;
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
|
||||
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
|
||||
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound);
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
|
||||
create_testcase(file, face, "polyloop");
|
||||
}
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint* point1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, 0., 0.);
|
||||
IfcSchema::IfcCartesianPoint* point2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, 0., 0.);
|
||||
IfcSchema::IfcVertexPoint* vertex1 = new IfcSchema::IfcVertexPoint(point1);
|
||||
IfcSchema::IfcVertexPoint* vertex2 = new IfcSchema::IfcVertexPoint(point2);
|
||||
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(file.addPlacement2d(), 400.);
|
||||
IfcSchema::IfcEdgeCurve* edge1 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, circle, true);
|
||||
IfcSchema::IfcEdgeCurve* edge2 = new IfcSchema::IfcEdgeCurve(vertex2, vertex1, circle, true);
|
||||
IfcSchema::IfcOrientedEdge* oriented_edge1 = new IfcSchema::IfcOrientedEdge(edge1, true);
|
||||
IfcSchema::IfcOrientedEdge* oriented_edge2 = new IfcSchema::IfcOrientedEdge(edge2, true);
|
||||
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
|
||||
edges->push(oriented_edge1);
|
||||
edges->push(oriented_edge2);
|
||||
IfcSchema::IfcEdgeLoop* loop = new IfcSchema::IfcEdgeLoop(edges);
|
||||
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound);
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
|
||||
create_testcase(file, face, "circle");
|
||||
}
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
|
||||
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
|
||||
IfcSchema::IfcFaceOuterBound* outer_bound = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points2 (new IfcSchema::IfcCartesianPoint::list);
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-300, -300, 0));
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, -300, 0));
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, +300, 0));
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-300, +300, 0));
|
||||
IfcSchema::IfcPolyLoop* loop2 = new IfcSchema::IfcPolyLoop(points2);
|
||||
IfcSchema::IfcFaceBound* inner_bound1 = new IfcSchema::IfcFaceBound(loop2, false);
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points3 (new IfcSchema::IfcCartesianPoint::list);
|
||||
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, +300, 0));
|
||||
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+300, +300, 0));
|
||||
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+300, -300, 0));
|
||||
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, -300, 0));
|
||||
IfcSchema::IfcPolyLoop* loop3 = new IfcSchema::IfcPolyLoop(points3);
|
||||
IfcSchema::IfcFaceBound* inner_bound2 = new IfcSchema::IfcFaceBound(loop3, true);
|
||||
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(inner_bound1);
|
||||
bounds->push(outer_bound);
|
||||
bounds->push(inner_bound2);
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
|
||||
create_testcase(file, face, "polyloop with holes");
|
||||
}
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, +400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
|
||||
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
|
||||
IfcSchema::IfcFaceOuterBound* bound1 = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points2 (new IfcSchema::IfcCartesianPoint::list);
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, +400, 0));
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
|
||||
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, -400, 0));
|
||||
IfcSchema::IfcPolyLoop* loop2 = new IfcSchema::IfcPolyLoop(points2);
|
||||
IfcSchema::IfcFaceOuterBound* bound2 = new IfcSchema::IfcFaceOuterBound(loop2, false);
|
||||
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound1);
|
||||
bounds->push(bound2);
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
|
||||
create_testcase(file, face, "multiple outer boundaries (invalid)");
|
||||
}
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 1e-6));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
|
||||
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
|
||||
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound);
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
|
||||
create_testcase(file, face, "imprecise polyloop");
|
||||
}
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint* point1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, 0., 0.);
|
||||
IfcSchema::IfcCartesianPoint* point2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, 0., 0.);
|
||||
IfcSchema::IfcVertexPoint* vertex1 = new IfcSchema::IfcVertexPoint(point1);
|
||||
IfcSchema::IfcVertexPoint* vertex2 = new IfcSchema::IfcVertexPoint(point2);
|
||||
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(file.addPlacement2d(), 400.);
|
||||
IfcSchema::IfcEdgeCurve* edge1 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, circle, true);
|
||||
IfcSchema::IfcEdgeCurve* edge2 = new IfcSchema::IfcEdgeCurve(vertex2, vertex1, circle, true);
|
||||
IfcSchema::IfcOrientedEdge* oriented_edge1 = new IfcSchema::IfcOrientedEdge(edge1, true);
|
||||
IfcSchema::IfcOrientedEdge* oriented_edge2 = new IfcSchema::IfcOrientedEdge(edge2, true);
|
||||
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
|
||||
edges->push(oriented_edge1);
|
||||
edges->push(oriented_edge2);
|
||||
IfcSchema::IfcEdgeLoop* loop = new IfcSchema::IfcEdgeLoop(edges);
|
||||
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound);
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr trim1(new IfcSchema::IfcCartesianPoint::list);
|
||||
IfcSchema::IfcCartesianPoint::list::ptr trim2(new IfcSchema::IfcCartesianPoint::list);
|
||||
trim1->push(point1);
|
||||
trim2->push(point2);
|
||||
IfcSchema::IfcTrimmedCurve* trimmed_curve = new IfcSchema::IfcTrimmedCurve(circle, trim1->generalize(), trim2->generalize(), true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);
|
||||
IfcSchema::IfcArbitraryOpenProfileDef* profile = new IfcSchema::IfcArbitraryOpenProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_CURVE, boost::none, trimmed_curve);
|
||||
IfcSchema::IfcAxis1Placement* place = new IfcSchema::IfcAxis1Placement(file.addTriplet<IfcSchema::IfcCartesianPoint>(0., 0., 0.), file.addTriplet<IfcSchema::IfcDirection>(1., 0., 0.));
|
||||
IfcSchema::IfcSurfaceOfRevolution* surface = new IfcSchema::IfcSurfaceOfRevolution(profile, file.addPlacement3d(), place);
|
||||
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFaceSurface(bounds, surface, true);
|
||||
create_testcase(file, face, "face surface");
|
||||
}
|
||||
const std::string filename = "faces.ifc";
|
||||
file.header().file_name().name(filename);
|
||||
std::ofstream f(filename.c_str());
|
||||
f << file;
|
||||
}
|
||||
@@ -0,0 +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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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 that generates an IfcTriangulatedFaceSet *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
#include "suzanne_geometry.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
|
||||
template <typename T>
|
||||
std::vector< std::vector<T> > create_vector_from_array(const T* arr, unsigned size) {
|
||||
std::vector< std::vector<T> > result;
|
||||
result.reserve(size);
|
||||
|
||||
for (unsigned i = 0; i < size; ) {
|
||||
std::vector<T> ts; ts.reserve(3);
|
||||
for (unsigned j = 0; j < 3; ++i, ++j) {
|
||||
ts.push_back(arr[i]);
|
||||
}
|
||||
result.push_back(ts);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
IfcHierarchyHelper file;
|
||||
|
||||
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
|
||||
guid(), 0, S("Blender's Suzanne"), null, null, 0, 0, null, null);
|
||||
file.addBuildingProduct(product);
|
||||
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement());
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);
|
||||
|
||||
std::vector< std::vector< double > > vertices_vector = create_vector_from_array(vertices, sizeof(vertices) / sizeof(vertices[0]));
|
||||
std::vector< std::vector< int > > indices_vector = create_vector_from_array(indices, sizeof(indices) / sizeof(indices[0]));
|
||||
|
||||
IfcSchema::IfcCartesianPointList3D* coordinates = new IfcSchema::IfcCartesianPointList3D(vertices_vector);
|
||||
IfcSchema::IfcTriangulatedFaceSet* faceset = new IfcSchema::IfcTriangulatedFaceSet(coordinates, null, null, indices_vector, null);
|
||||
|
||||
items->push(faceset);
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
file.addEntity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
|
||||
const std::string filename = "tesselated_faceset.ifc";
|
||||
file.header().file_name().name(filename);
|
||||
std::ofstream f(filename.c_str());
|
||||
f << file;
|
||||
}
|
||||
@@ -27,46 +27,50 @@
|
||||
|
||||
bl_info = {
|
||||
"name": "IfcBlender",
|
||||
"description": "Import files in the "\
|
||||
"description": "Import files in the "
|
||||
"Industry Foundation Classes (.ifc) file format",
|
||||
"author": "Thomas Krijnen, IfcOpenShell",
|
||||
"blender": (2, 73, 0),
|
||||
"blender": (2, 80, 0),
|
||||
"location": "File > Import",
|
||||
"tracker_url": "https://sourceforge.net/p/ifcopenshell/"\
|
||||
"tracker_url": "https://sourceforge.net/p/ifcopenshell/"
|
||||
"_list/tickets?source=navbar",
|
||||
"category": "Import-Export"}
|
||||
|
||||
if "bpy" in locals():
|
||||
import imp
|
||||
import importlib
|
||||
if "ifcopenshell" in locals():
|
||||
imp.reload(ifcopenshell)
|
||||
importlib.reload(ifcopenshell)
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
from bpy.props import StringProperty, IntProperty, BoolProperty
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
|
||||
major,minor = bpy.app.version[0:2]
|
||||
major, minor = bpy.app.version[0:2]
|
||||
transpose_matrices = minor >= 62
|
||||
|
||||
bpy.types.Object.ifc_id = IntProperty(name="IFC Entity ID",
|
||||
bpy.types.Object.ifc_id = IntProperty(
|
||||
name="IFC Entity ID",
|
||||
description="The STEP entity instance name")
|
||||
bpy.types.Object.ifc_guid = StringProperty(name="IFC Entity GUID",
|
||||
bpy.types.Object.ifc_guid = StringProperty(
|
||||
name="IFC Entity GUID",
|
||||
description="The IFC Globally Unique Identifier")
|
||||
bpy.types.Object.ifc_name = StringProperty(name="IFC Entity Name",
|
||||
bpy.types.Object.ifc_name = StringProperty(
|
||||
name="IFC Entity Name",
|
||||
description="The optional name attribute")
|
||||
bpy.types.Object.ifc_type = StringProperty(name="IFC Entity Type",
|
||||
bpy.types.Object.ifc_type = StringProperty(
|
||||
name="IFC Entity Type",
|
||||
description="The STEP Datatype keyword")
|
||||
|
||||
|
||||
def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
from . import ifcopenshell
|
||||
from .ifcopenshell import geom as ifcopenshell_geom
|
||||
print("Reading %s..."%bpy.path.basename(filename))
|
||||
print(f"Reading {bpy.path.basename(filename)}...")
|
||||
settings = ifcopenshell_geom.settings()
|
||||
settings.set(settings.DISABLE_OPENING_SUBTRACTIONS, blender_booleans)
|
||||
iterator = ifcopenshell_geom.iterator(settings, filename)
|
||||
valid_file = iterator.findContext()
|
||||
valid_file = iterator.initialize()
|
||||
if not valid_file:
|
||||
return False
|
||||
print("Done reading file")
|
||||
@@ -76,9 +80,14 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
openings = []
|
||||
old_progress = -1
|
||||
print("Creating geometry...")
|
||||
collection = bpy.data.collections.new(f"{bpy.path.basename(filename)}")
|
||||
bpy.context.scene.collection.children.link(collection)
|
||||
if process_relations:
|
||||
rel_collection = bpy.data.collections.new("Relations")
|
||||
collection.children.link(rel_collection)
|
||||
while True:
|
||||
ob = iterator.get()
|
||||
|
||||
|
||||
f = ob.geometry.faces
|
||||
v = ob.geometry.verts
|
||||
mats = ob.geometry.materials
|
||||
@@ -86,52 +95,77 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
m = ob.transformation.matrix.data
|
||||
t = ob.type[0:21]
|
||||
nm = ob.name if len(ob.name) and use_names else ob.guid
|
||||
# MESH CREATION
|
||||
# Depending on version, geometry.id will be either int or str
|
||||
mesh_name = 'mesh-%r' % ob.geometry.id
|
||||
if mesh_name in bpy.data.meshes:
|
||||
me = bpy.data.meshes[mesh_name]
|
||||
else:
|
||||
verts = [[v[i], v[i + 1], v[i + 2]]
|
||||
for i in range(0, len(v), 3)]
|
||||
faces = [[f[i], f[i + 1], f[i + 2]]
|
||||
for i in range(0, len(f), 3)]
|
||||
|
||||
verts = [[v[i], v[i + 1], v[i + 2]] \
|
||||
for i in range(0, len(v), 3)]
|
||||
faces = [[f[i], f[i + 1], f[i + 2]] \
|
||||
for i in range(0, len(f), 3)]
|
||||
me = bpy.data.meshes.new(mesh_name)
|
||||
me.from_pydata(verts, [], faces)
|
||||
me.validate()
|
||||
# MATERIAL CREATION
|
||||
def add_material(mname, props):
|
||||
if mname in bpy.data.materials:
|
||||
mat = bpy.data.materials[mname]
|
||||
mat.use_fake_user = True
|
||||
else:
|
||||
mat = bpy.data.materials.new(mname)
|
||||
for k, v in props.items():
|
||||
if k == 'transparency':
|
||||
mat.blend_method = 'HASHED'
|
||||
mat.use_screen_refraction = True
|
||||
mat.refraction_depth = 0.1
|
||||
mat.use_nodes = True
|
||||
mat.node_tree.nodes["Principled BSDF"].inputs[15].default_value = v
|
||||
else:
|
||||
setattr(mat, k, v)
|
||||
me.materials.append(mat)
|
||||
|
||||
me = bpy.data.meshes.new('mesh%d' % ob.geometry.id)
|
||||
me.from_pydata(verts, [], faces)
|
||||
|
||||
def add_material(mname, props):
|
||||
if mname in bpy.data.materials:
|
||||
mat = bpy.data.materials[mname]
|
||||
mat.use_fake_user = True
|
||||
else:
|
||||
mat = bpy.data.materials.new(mname)
|
||||
for k,v in props.items():
|
||||
setattr(mat, k, v)
|
||||
me.materials.append(mat)
|
||||
|
||||
needs_default = -1 in matids
|
||||
if needs_default: add_material(t, {})
|
||||
|
||||
for mat in mats:
|
||||
props = {}
|
||||
if mat.has_diffuse: props['diffuse_color'] = mat.diffuse
|
||||
if mat.has_specular: props['specular_color'] = mat.specular
|
||||
if mat.has_transparency and mat.transparency > 0:
|
||||
props['alpha'] = 1.0 - mat.transparency
|
||||
props['use_transparency'] = True
|
||||
if mat.has_specularity: props['specular_hardness'] = mat.specularity
|
||||
add_material(mat.name, props)
|
||||
needs_default = -1 in matids
|
||||
if needs_default:
|
||||
add_material(t, {})
|
||||
|
||||
for mat in mats:
|
||||
props = {}
|
||||
if mat.has_diffuse:
|
||||
alpha = 1.
|
||||
if mat.has_transparency and mat.transparency > 0:
|
||||
alpha = 1. - mat.transparency
|
||||
props['diffuse_color'] = mat.diffuse + (alpha,)
|
||||
# @todo
|
||||
# if mat.has_specular:
|
||||
# props['specular_color'] = mat.specular
|
||||
# if mat.has_specularity:
|
||||
# props['specular_intensity'] = mat.specularity
|
||||
add_material(mat.name, props)
|
||||
|
||||
faces = me.polygons if hasattr(me, 'polygons') else me.faces
|
||||
if len(faces) == len(matids):
|
||||
for face, matid in zip(faces, matids):
|
||||
face.material_index = matid + (1 if needs_default else 0)
|
||||
|
||||
# OBJECT CREATION
|
||||
bob = bpy.data.objects.new(nm, me)
|
||||
mat = mathutils.Matrix(([m[0], m[1], m[2], 0],
|
||||
[m[3], m[4], m[5], 0],
|
||||
[m[6], m[7], m[8], 0],
|
||||
[m[9], m[10], m[11], 1]))
|
||||
if transpose_matrices: mat.transpose()
|
||||
|
||||
[m[3], m[4], m[5], 0],
|
||||
[m[6], m[7], m[8], 0],
|
||||
[m[9], m[10], m[11], 1]))
|
||||
if transpose_matrices:
|
||||
mat.transpose()
|
||||
|
||||
if process_relations:
|
||||
id_to_matrix[ob.id] = mat
|
||||
else:
|
||||
bob.matrix_world = mat
|
||||
bpy.context.scene.objects.link(bob)
|
||||
collection.objects.link(bob)
|
||||
|
||||
bpy.context.scene.objects.active = bob
|
||||
bpy.context.view_layer.objects.active = bob
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.normals_make_consistent()
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
@@ -141,23 +175,19 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
|
||||
if ob.type == 'IfcSpace' or ob.type == 'IfcOpeningElement':
|
||||
if not (ob.type == 'IfcOpeningElement' and blender_booleans):
|
||||
bob.hide = bob.hide_render = True
|
||||
bob.draw_type = 'WIRE'
|
||||
|
||||
if ob.id not in id_to_object: id_to_object[ob.id] = []
|
||||
bob.hide_viewport = bob.hide_render = True
|
||||
bob.display_type = 'WIRE'
|
||||
|
||||
if ob.id not in id_to_object:
|
||||
id_to_object[ob.id] = []
|
||||
id_to_object[ob.id].append(bob)
|
||||
|
||||
if ob.parent_id > 0:
|
||||
id_to_parent[ob.id] = ob.parent_id
|
||||
|
||||
|
||||
if blender_booleans and ob.type == 'IfcOpeningElement':
|
||||
openings.append(ob.id)
|
||||
|
||||
faces = me.polygons if hasattr(me, 'polygons') else me.faces
|
||||
if len(faces) == len(matids):
|
||||
for face, matid in zip(faces, matids):
|
||||
face.material_index = matid + (1 if needs_default else 0)
|
||||
|
||||
|
||||
progress = iterator.progress() // 2
|
||||
if progress > old_progress:
|
||||
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
|
||||
@@ -168,13 +198,13 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
print("\rDone creating geometry" + " " * 30)
|
||||
|
||||
id_to_parent_temp = dict(id_to_parent)
|
||||
|
||||
|
||||
if process_relations:
|
||||
print("Processing relations...")
|
||||
|
||||
while len(id_to_parent_temp) and process_relations:
|
||||
id, parent_id = id_to_parent_temp.popitem()
|
||||
|
||||
|
||||
if parent_id in id_to_object:
|
||||
bob = id_to_object[parent_id][0]
|
||||
else:
|
||||
@@ -186,16 +216,17 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
nm = parent_ob.name if len(parent_ob.name) and use_names \
|
||||
else parent_ob.guid
|
||||
bob = bpy.data.objects.new(nm, None)
|
||||
|
||||
|
||||
mat = mathutils.Matrix((
|
||||
[m[0], m[1], m[2], 0],
|
||||
[m[3], m[4], m[5], 0],
|
||||
[m[6], m[7], m[8], 0],
|
||||
[m[9], m[10], m[11], 1]))
|
||||
if transpose_matrices: mat.transpose()
|
||||
if transpose_matrices:
|
||||
mat.transpose()
|
||||
id_to_matrix[parent_ob.id] = mat
|
||||
|
||||
bpy.context.scene.objects.link(bob)
|
||||
|
||||
rel_collection.objects.link(bob)
|
||||
|
||||
bob.ifc_id = parent_ob.id
|
||||
bob.ifc_name, bob.ifc_type, bob.ifc_guid = \
|
||||
@@ -218,13 +249,13 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
parent_matrix = id_to_matrix.get(parent_id, None)
|
||||
for ob in id_to_object[id]:
|
||||
if parent_matrix:
|
||||
ob.matrix_local = parent_matrix.inverted() * matrix
|
||||
ob.matrix_local = parent_matrix.inverted() @ matrix
|
||||
else:
|
||||
ob.matrix_world = matrix
|
||||
|
||||
|
||||
if process_relations:
|
||||
print("Done processing relations")
|
||||
|
||||
|
||||
for opening_id in openings:
|
||||
parent_id = id_to_parent[opening_id]
|
||||
if parent_id in id_to_object:
|
||||
@@ -233,9 +264,11 @@ def import_ifc(filename, use_names, process_relations, blender_booleans):
|
||||
mod = parent_ob.modifiers.new("opening", "BOOLEAN")
|
||||
mod.operation = "DIFFERENCE"
|
||||
mod.object = opening_ob
|
||||
|
||||
txt = bpy.data.texts.new("%s.log"%bpy.path.basename(filename))
|
||||
txt.from_string(iterator.getLog())
|
||||
|
||||
if hasattr(iterator, 'getLog'):
|
||||
# @todo
|
||||
txt = bpy.data.texts.new(f"{bpy.path.basename(filename)}.log")
|
||||
txt.from_string(iterator.getLog())
|
||||
|
||||
return True
|
||||
|
||||
@@ -245,42 +278,51 @@ class ImportIFC(bpy.types.Operator, ImportHelper):
|
||||
bl_label = "Import .ifc file"
|
||||
|
||||
filename_ext = ".ifc"
|
||||
filter_glob = StringProperty(default="*.ifc", options={'HIDDEN'})
|
||||
filter_glob: StringProperty(default="*.ifc", options={'HIDDEN'})
|
||||
|
||||
use_names = BoolProperty(name="Use entity names",
|
||||
description="Use entity names rather than GlobalIds for objects",
|
||||
default=True)
|
||||
process_relations = BoolProperty(name="Process relations",
|
||||
description="Convert containment and aggregation" \
|
||||
" relations to parenting" \
|
||||
" (warning: may be slow on large files)",
|
||||
default=False)
|
||||
blender_booleans = BoolProperty(name="Use Blender booleans",
|
||||
description="Use Blender boolean modifiers for opening" \
|
||||
" elements",
|
||||
default=False)
|
||||
use_names: BoolProperty(name="Use entity names",
|
||||
description="Use entity names rather than "
|
||||
"GlobalIds for objects",
|
||||
default=True)
|
||||
process_relations: BoolProperty(name="Process relations",
|
||||
description="Convert containment and "
|
||||
"aggregation relations to parenting"
|
||||
" (warning: may be slow on large files)",
|
||||
default=False)
|
||||
blender_booleans: BoolProperty(name="Use Blender booleans",
|
||||
description="Use Blender boolean modifiers "
|
||||
"for opening elements",
|
||||
default=False)
|
||||
|
||||
def execute(self, context):
|
||||
if not import_ifc(self.filepath, self.use_names, self.process_relations, self.blender_booleans):
|
||||
if not import_ifc(self.filepath, self.use_names,
|
||||
self.process_relations, self.blender_booleans):
|
||||
self.report({'ERROR'},
|
||||
'Unable to parse .ifc file or no geometrical entities found'
|
||||
)
|
||||
'Unable to parse .ifc file or no geometrical entities found'
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(ImportIFC.bl_idname,
|
||||
text="Industry Foundation Classes (.ifc)")
|
||||
text="Industry Foundation Classes (.ifc)")
|
||||
|
||||
|
||||
classes = (
|
||||
ImportIFC,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.INFO_MT_file_import.append(menu_func_import)
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
bpy.types.INFO_MT_file_import.remove(menu_func_import)
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -19,53 +19,69 @@
|
||||
|
||||
#ifdef WITH_OPENCOLLADA
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ColladaSerializer.h"
|
||||
|
||||
std::string collada_id(const std::string& s) {
|
||||
std::string id;
|
||||
id.reserve(s.size());
|
||||
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
|
||||
const std::string::value_type c = *it;
|
||||
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_')) {
|
||||
id.push_back(c);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
#include <COLLADASWPrimitves.h>
|
||||
#include <COLLADASWSource.h>
|
||||
#include <COLLADASWScene.h>
|
||||
#include <COLLADASWNode.h>
|
||||
#include <COLLADASWInstanceGeometry.h>
|
||||
#include <COLLADASWBaseInputElement.h>
|
||||
#include <COLLADASWAsset.h>
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
#include "../ifcparse/utils.h"
|
||||
|
||||
using namespace IfcSchema;
|
||||
|
||||
static std::string& collada_id(std::string& s)
|
||||
{
|
||||
IfcUtil::sanitate_material_name(s);
|
||||
IfcUtil::escape_xml(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const std::string& mesh_id, const std::string& suffix, const std::vector<double>& floats, const char* coords /* = "XYZ" */) {
|
||||
void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const std::string& mesh_id,
|
||||
const std::string& suffix, const std::vector<real_t>& floats, const char* coords /* = "XYZ" */)
|
||||
{
|
||||
COLLADASW::FloatSource source(mSW);
|
||||
source.setId(mesh_id + suffix);
|
||||
source.setArrayId(mesh_id + suffix + COLLADASW::LibraryGeometries::ARRAY_ID_SUFFIX);
|
||||
source.setAccessorStride(strlen(coords));
|
||||
source.setAccessorCount(floats.size() / 3);
|
||||
for (unsigned int i = 0; i < source.getAccessorStride(); ++i) {
|
||||
const size_t num_elems = strlen(coords);
|
||||
source.setAccessorStride(static_cast<unsigned long>(num_elems));
|
||||
source.setAccessorCount(static_cast<unsigned long>(floats.size() / num_elems));
|
||||
for (size_t i = 0; i < num_elems; ++i) {
|
||||
source.getParameterNameList().push_back(std::string(1, coords[i]));
|
||||
}
|
||||
source.prepareToAppendValues();
|
||||
for (std::vector<double>::const_iterator it = floats.begin(); it != floats.end(); ++it) {
|
||||
for (std::vector<real_t>::const_iterator it = floats.begin(); it != floats.end(); ++it) {
|
||||
source.appendValues(*it);
|
||||
}
|
||||
source.finish();
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::string mesh_id, const std::string& default_material_name, const std::vector<double>& positions, const std::vector<double>& normals, const std::vector<int>& indices, const std::vector<int> material_ids, const std::vector<IfcGeom::Material>& materials) {
|
||||
// The goal of the IfcGeom::Iterator is to filter out empty geometries, but
|
||||
// since this function would crash trying to deference the material_ids in
|
||||
// that case, a hard return statement is added just in case.
|
||||
if (indices.empty()) return;
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaGeometries::write(
|
||||
const std::string &mesh_id, const std::string &/**<@todo 'default_material_name' unused, remove? */,
|
||||
const std::vector<real_t>& positions, const std::vector<real_t>& normals,
|
||||
const std::vector<int>& faces, const std::vector<int>& edges,
|
||||
const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& /**<@todo 'materials' unused, remove? */,
|
||||
const std::vector<real_t>& uvs, const std::vector<std::string>& material_references)
|
||||
{
|
||||
openMesh(mesh_id);
|
||||
|
||||
|
||||
// The normals vector can be empty for example when the WELD_VERTICES setting is used.
|
||||
// IfcOpenShell does not provide them with multiple face normals collapsed into a single vertex.
|
||||
const bool has_normals = !normals.empty();
|
||||
|
||||
const bool has_uvs = !uvs.empty();
|
||||
|
||||
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions);
|
||||
if (has_normals) {
|
||||
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, normals);
|
||||
if (has_uvs) {
|
||||
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, uvs, "UV");
|
||||
}
|
||||
}
|
||||
|
||||
COLLADASW::VerticesElement vertices(mSW);
|
||||
@@ -73,25 +89,39 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::str
|
||||
vertices.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::POSITION, "#" + mesh_id + COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX));
|
||||
vertices.add();
|
||||
|
||||
std::vector<int>::const_iterator index_range_start = indices.begin();
|
||||
std::vector<int>::const_iterator index_range_start = faces.begin();
|
||||
std::vector<int>::const_iterator material_it = material_ids.begin();
|
||||
int previous_material_id = -1;
|
||||
for (std::vector<int>::const_iterator it = indices.begin(); ; it += 3) {
|
||||
const int current_material_id = *(material_it++);
|
||||
const int num_triangles = std::distance(index_range_start, it) / 3;
|
||||
if ((previous_material_id != current_material_id && num_triangles > 0) || (it == indices.end())) {
|
||||
for (std::vector<int>::const_iterator it = faces.begin(); !faces.empty(); it += 3) {
|
||||
|
||||
int current_material_id = 0;
|
||||
if (material_it != material_ids.end()) {
|
||||
// In order for the last range of equal material ids to be output as well, this loop iterates
|
||||
// one element past the end of the vector. This needs to be observed when incrementing.
|
||||
current_material_id = *(material_it++);
|
||||
}
|
||||
|
||||
const size_t num_triangles = std::distance(index_range_start, it) / 3;
|
||||
if ((previous_material_id != current_material_id && num_triangles > 0) || (it == faces.end())) {
|
||||
COLLADASW::Triangles triangles(mSW);
|
||||
triangles.setMaterial(materials[previous_material_id].name());
|
||||
triangles.setCount(num_triangles);
|
||||
|
||||
std::string material_name = material_references[previous_material_id];
|
||||
triangles.setMaterial(material_name);
|
||||
triangles.setCount((unsigned long)num_triangles);
|
||||
int offset = 0;
|
||||
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX,"#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++ ) );
|
||||
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX,"#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++));
|
||||
if (has_normals) {
|
||||
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++ ) );
|
||||
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++));
|
||||
}
|
||||
if (has_uvs) {
|
||||
triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::TEXCOORD,"#" + mesh_id + COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, offset++));
|
||||
}
|
||||
triangles.prepareToAppendValues();
|
||||
for (std::vector<int>::const_iterator jt = index_range_start; jt != it; ++jt) {
|
||||
const int idx = *jt;
|
||||
if (has_normals) {
|
||||
if (has_normals && has_uvs) {
|
||||
triangles.appendValues(idx, idx, idx);
|
||||
} else if(has_normals) {
|
||||
triangles.appendValues(idx, idx);
|
||||
} else {
|
||||
triangles.appendValues(idx);
|
||||
@@ -101,11 +131,44 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::str
|
||||
index_range_start = it;
|
||||
}
|
||||
previous_material_id = current_material_id;
|
||||
if (it == indices.end()) {
|
||||
if (it == faces.end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::set<int> faces_set (faces.begin(), faces.end());
|
||||
typedef std::vector< std::pair<int, std::vector<unsigned long> > > linelist_t;
|
||||
linelist_t linelist;
|
||||
|
||||
int num_lines = 0;
|
||||
for ( std::vector<int>::const_iterator it = edges.begin(); it != edges.end(); ++num_lines) {
|
||||
const int i1 = *(it++);
|
||||
const int i2 = *(it++);
|
||||
|
||||
if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int current_material_id = *(material_it++);
|
||||
if ((previous_material_id != current_material_id) || (num_lines == 0)) {
|
||||
linelist.resize(linelist.size() + 1);
|
||||
}
|
||||
|
||||
linelist.rbegin()->second.push_back(i1);
|
||||
linelist.rbegin()->second.push_back(i2);
|
||||
}
|
||||
|
||||
for (linelist_t::const_iterator it = linelist.begin(); it != linelist.end(); ++it) {
|
||||
COLLADASW::Lines lines(mSW);
|
||||
lines.setMaterial(material_references[it->first]);
|
||||
lines.setCount((unsigned long)it->second.size());
|
||||
int offset = 0;
|
||||
lines.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX, "#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset));
|
||||
lines.prepareToAppendValues();
|
||||
lines.appendValues(it->second);
|
||||
lines.finish();
|
||||
}
|
||||
|
||||
closeMesh();
|
||||
closeGeometry();
|
||||
}
|
||||
@@ -113,8 +176,11 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::str
|
||||
void ColladaSerializer::ColladaExporter::ColladaGeometries::close() {
|
||||
closeLibrary();
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::add(const std::string& node_id, const std::string& node_name, const std::string& geom_name, const std::vector<std::string>& material_ids, const std::vector<double>& matrix) {
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::add(
|
||||
const std::string& node_id, const std::string& node_name, const std::string& geom_name,
|
||||
const std::vector<std::string>& material_ids, const IfcGeom::Transformation<real_t>& transformation)
|
||||
{
|
||||
if (!scene_opened) {
|
||||
openVisualScene(scene_id);
|
||||
scene_opened = true;
|
||||
@@ -127,37 +193,127 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add(const std::string& no
|
||||
|
||||
// The matrix attribute of an entity is basically a 4x3 representation of its ObjectPlacement.
|
||||
// Note that this placement is absolute, ie it is multiplied with all parent placements.
|
||||
|
||||
IfcGeom::Transformation<real_t>* relative_trsf = 0;
|
||||
const IfcGeom::Transformation<real_t>* transformation_towrite = &transformation;
|
||||
|
||||
// If this is not the first parent, get the relative placement
|
||||
if (parentNodes.size() > 0)
|
||||
{
|
||||
relative_trsf = new IfcGeom::Transformation<real_t>(matrixStack.top().multiplied(transformation));
|
||||
transformation_towrite = relative_trsf;
|
||||
}
|
||||
|
||||
const std::vector<real_t>& posmatrix = transformation_towrite->matrix().data();
|
||||
|
||||
double matrix_array[4][4] = {
|
||||
{matrix[0], matrix[3], matrix[6], matrix[ 9]},
|
||||
{matrix[1], matrix[4], matrix[7], matrix[10]},
|
||||
{matrix[2], matrix[5], matrix[8], matrix[11]},
|
||||
{ 0, 0, 0, 1}
|
||||
{ (double)posmatrix[0], (double)posmatrix[3], (double)posmatrix[6], (double)posmatrix[9] },
|
||||
{ (double)posmatrix[1], (double)posmatrix[4], (double)posmatrix[7], (double)posmatrix[10] },
|
||||
{ (double)posmatrix[2], (double)posmatrix[5], (double)posmatrix[8], (double)posmatrix[11] },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
|
||||
/// @todo: TFK: Rather than applying this offset to all leafs (which might be undesirable) should this offset be applied to a node higher up in the hierarchy?
|
||||
matrix_array[0][3] += serializer->settings().offset[0];
|
||||
matrix_array[1][3] += serializer->settings().offset[1];
|
||||
matrix_array[2][3] += serializer->settings().offset[2];
|
||||
|
||||
delete relative_trsf;
|
||||
|
||||
node.start();
|
||||
node.addMatrix(matrix_array);
|
||||
COLLADASW::InstanceGeometry instanceGeometry(mSW);
|
||||
instanceGeometry.setUrl ("#" + geom_name);
|
||||
for (std::vector<std::string>::const_iterator it = material_ids.begin(); it != material_ids.end(); ++it) {
|
||||
COLLADASW::InstanceMaterial material (*it, "#" + *it);
|
||||
instanceGeometry.setUrl("#" + geom_name);
|
||||
BOOST_FOREACH(const std::string &material_name, material_ids) {
|
||||
// Unescape to avoid double escaping beucase OpenCollada's material URI parameter escapes XML internally
|
||||
std::string unescaped = material_name;
|
||||
IfcUtil::unescape_xml(unescaped);
|
||||
|
||||
COLLADASW::InstanceMaterial material(material_name, "#" + unescaped);
|
||||
instanceGeometry.getBindMaterial().getInstanceMaterialList().push_back(material);
|
||||
}
|
||||
instanceGeometry.add();
|
||||
node.end();
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const IfcGeom::Element<real_t>& parent){
|
||||
//we open the visual scene tag if it's not.
|
||||
if (!scene_opened) {
|
||||
openVisualScene(scene_id);
|
||||
scene_opened = true;
|
||||
}
|
||||
|
||||
const IfcGeom::Transformation<real_t>& parent_trsf = parent.transformation();
|
||||
|
||||
IfcGeom::Transformation<real_t>* relative_trsf = 0;
|
||||
const IfcGeom::Transformation<real_t>* transformation_towrite = &parent_trsf;
|
||||
|
||||
// If this is not the first parent, get the relative placement
|
||||
if (parentNodes.size() > 0)
|
||||
{
|
||||
relative_trsf = new IfcGeom::Transformation<real_t>(matrixStack.top().multiplied(parent_trsf));
|
||||
transformation_towrite = relative_trsf;
|
||||
}
|
||||
|
||||
const std::vector<real_t>& parentMatrix = transformation_towrite->matrix().data();
|
||||
|
||||
double matrix_array[4][4] = {
|
||||
{ (double)parentMatrix[0], (double)parentMatrix[3], (double)parentMatrix[6], (double)parentMatrix[9] },
|
||||
{ (double)parentMatrix[1], (double)parentMatrix[4], (double)parentMatrix[7], (double)parentMatrix[10] },
|
||||
{ (double)parentMatrix[2], (double)parentMatrix[5], (double)parentMatrix[8], (double)parentMatrix[11] },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
|
||||
std::string name = serializer->object_id(&parent);
|
||||
collada_id(name);
|
||||
|
||||
COLLADASW::Node *current_node;
|
||||
current_node = new COLLADASW::Node(mSW);
|
||||
current_node->setNodeId(name);
|
||||
/// @todo redundant information using ID as both ID and Name, maybe omit Name or allow specifying what would be used as the name
|
||||
current_node->setNodeName(name);
|
||||
current_node->setType(COLLADASW::Node::NODE);
|
||||
current_node->start();
|
||||
current_node->addMatrix(matrix_array);
|
||||
|
||||
// Add the node to the parent stack
|
||||
matrixStack.push(parent_trsf.inverted());
|
||||
parentNodes.push(current_node);
|
||||
serializer->parentStackId.push(parent.id());
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::closeParent()
|
||||
{
|
||||
// Get the top element
|
||||
COLLADASW::Node *current_node = parentNodes.top();
|
||||
|
||||
// Close the node
|
||||
current_node->end();
|
||||
|
||||
// Remove it from the stack
|
||||
parentNodes.pop();
|
||||
matrixStack.pop();
|
||||
serializer->parentStackId.pop();
|
||||
|
||||
// Free the memory
|
||||
delete current_node;
|
||||
current_node = NULL;
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::write() {
|
||||
if (scene_opened) {
|
||||
closeVisualScene();
|
||||
closeLibrary();
|
||||
|
||||
|
||||
COLLADASW::Scene scene (mSW, COLLADASW::URI ("#" + scene_id));
|
||||
scene.add();
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write(const IfcGeom::Material& material) {
|
||||
openEffect(collada_id(material.name()) + "-fx");
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write(
|
||||
const IfcGeom::Material &material, const std::string &material_uri)
|
||||
{
|
||||
openEffect(material_uri + "-fx");
|
||||
COLLADASW::EffectProfile effect(mSW);
|
||||
effect.setShaderType(COLLADASW::EffectProfile::LAMBERT);
|
||||
if (material.hasDiffuse()) {
|
||||
@@ -186,29 +342,49 @@ void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write
|
||||
void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::close() {
|
||||
closeLibrary();
|
||||
}
|
||||
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const IfcGeom::Material& material) {
|
||||
if (!contains(material)) {
|
||||
effects.write(material);
|
||||
std::string material_name = (serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES)
|
||||
? material.original_name() : material.name());
|
||||
|
||||
if (material_name.empty()) {
|
||||
material_name = "missing-material-" + material.name();
|
||||
}
|
||||
|
||||
collada_id(material_name);
|
||||
|
||||
effects.write(material, material_name);
|
||||
materials.push_back(material);
|
||||
material_uris.push_back(material_name);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ColladaSerializer::ColladaExporter::ColladaMaterials::getMaterialUri(const IfcGeom::Material& material) {
|
||||
std::vector<IfcGeom::Material>::iterator it = std::find(materials.begin(), materials.end(), material);
|
||||
ptrdiff_t index = std::distance(materials.begin(), it);
|
||||
return material_uris.at(index);
|
||||
}
|
||||
|
||||
bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeom::Material& material) {
|
||||
return std::find(materials.begin(), materials.end(), material) != materials.end();
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaMaterials::write() {
|
||||
effects.close();
|
||||
for (std::vector<IfcGeom::Material>::const_iterator it = materials.begin(); it != materials.end(); ++it) {
|
||||
const std::string& material_name = collada_id((*it).name());
|
||||
BOOST_FOREACH(const IfcGeom::Material& material, materials) {
|
||||
std::string material_name = getMaterialUri(material);
|
||||
openMaterial(material_name);
|
||||
addInstanceEffect("#" + material_name + "-fx");
|
||||
|
||||
// Unescape to avoid double escaping beucase OpenCollada's addInstanceEffect escapes XML internally
|
||||
IfcUtil::unescape_xml(material_name);
|
||||
|
||||
addInstanceEffect("#" + material_name + "-fx");
|
||||
closeMaterial();
|
||||
}
|
||||
closeLibrary();
|
||||
}
|
||||
|
||||
|
||||
void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_name, float unit_magnitude) {
|
||||
stream.startDocument();
|
||||
|
||||
@@ -219,45 +395,135 @@ void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_n
|
||||
asset.add();
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::write(const std::string& guid, const std::string& name, const std::string& type, int obj_id, const std::vector<double>& matrix, const std::vector<double>& vertices, const std::vector<double>& normals, const std::vector<int>& indices, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& _materials) {
|
||||
void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationElement<real_t>* o)
|
||||
{
|
||||
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
|
||||
|
||||
std::string name = serializer->object_id(o);
|
||||
collada_id(name);
|
||||
|
||||
std::string representation_id = "representation-" + o->geometry().id();
|
||||
collada_id(representation_id);
|
||||
|
||||
std::vector<std::string> material_references;
|
||||
for (std::vector<IfcGeom::Material>::const_iterator it = _materials.begin(); it != _materials.end(); ++it) {
|
||||
const IfcGeom::Material& material = *it;
|
||||
if (!materials.contains(material)) {
|
||||
materials.add(material);
|
||||
}
|
||||
material_references.push_back(collada_id(material.name()));
|
||||
BOOST_FOREACH(const IfcGeom::Material& material, mesh.materials()) {
|
||||
materials.add(material);
|
||||
|
||||
std::string material_name = materials.getMaterialUri(material);
|
||||
material_references.push_back(material_name);
|
||||
}
|
||||
deferreds.push_back(DeferredObject(guid, name, type, obj_id, matrix, vertices, normals, indices, material_ids, _materials, material_references));
|
||||
|
||||
DeferredObject deferred(name, representation_id, o->type(), o->transformation(), mesh.verts(), mesh.normals(),
|
||||
mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs());
|
||||
|
||||
if (serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY)) {
|
||||
deferred.parents() = o->parents();
|
||||
}
|
||||
|
||||
deferreds.push_back(deferred);
|
||||
}
|
||||
|
||||
const std::string ColladaSerializer::ColladaExporter::DeferredObject::Name() const {
|
||||
std::stringstream ss;
|
||||
if (!this->name.empty()) {
|
||||
ss << this->obj_id << "_" << this->name;
|
||||
} else {
|
||||
ss << this->guid;
|
||||
}
|
||||
return collada_id(ss.str());
|
||||
std::string ColladaSerializer::differentiateSlabTypes(const IfcSchema::IfcSlab* slab)
|
||||
{
|
||||
if (!slab->hasPredefinedType()) {
|
||||
return "_Unknown";
|
||||
}
|
||||
|
||||
switch (slab->PredefinedType()) {
|
||||
case IfcSlabTypeEnum::IfcSlabType_FLOOR: return "_Floor";
|
||||
case IfcSlabTypeEnum::IfcSlabType_ROOF: return "_Roof";
|
||||
case IfcSlabTypeEnum::IfcSlabType_LANDING: return "_Landing";
|
||||
case IfcSlabTypeEnum::IfcSlabType_BASESLAB: return "_BaseSlab";
|
||||
case IfcSlabTypeEnum::IfcSlabType_NOTDEFINED: return "_NotDefined";
|
||||
default: return slab->hasObjectType() ? "_" + slab->ObjectType() : "_Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ColladaSerializer::object_id(const IfcGeom::Element<real_t>* o) /*override*/
|
||||
{
|
||||
if (settings_.get(SerializerSettings::USE_ELEMENT_TYPES)) {
|
||||
const std::string slabSuffix = (o->product() && o->product()->is(IfcSchema::IfcSlab::Class()))
|
||||
? differentiateSlabTypes(o->product()->as<IfcSchema::IfcSlab>())
|
||||
: "";
|
||||
return o->type() + slabSuffix;
|
||||
}
|
||||
return GeometrySerializer::object_id(o);
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::endDocument() {
|
||||
// In fact due the XML based nature of Collada and its dependency on library nodes,
|
||||
// only at this point all objects are written to the stream.
|
||||
materials.write();
|
||||
bool use_hierarchy = serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY);
|
||||
|
||||
std::set<std::string> geometries_written;
|
||||
|
||||
//if the setting USE_ELEMENT_HIERARCHY is in use, we sort the deferreds objects by their parents.
|
||||
|
||||
if (use_hierarchy) {
|
||||
std::sort(deferreds.begin(), deferreds.end());
|
||||
}
|
||||
|
||||
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
|
||||
const std::string object_name = it->Name();
|
||||
geometries.write(object_name, it->type, it->vertices, it->normals, it->indices, it->material_ids, it->materials);
|
||||
if (geometries_written.find(it->representation_id) != geometries_written.end()) {
|
||||
continue;
|
||||
}
|
||||
geometries_written.insert(it->representation_id);
|
||||
geometries.write(it->representation_id, it->type, it->vertices, it->normals, it->faces, it->edges,
|
||||
it->material_ids, it->materials, it->uvs, it->material_references);
|
||||
}
|
||||
geometries.close();
|
||||
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
|
||||
const std::string object_name = it->Name();
|
||||
scene.add(object_name + "-instance", object_name, object_name, it->material_references, it->matrix);
|
||||
|
||||
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it){
|
||||
const std::string object_name = it->unique_id;
|
||||
|
||||
if (use_hierarchy)
|
||||
{
|
||||
size_t parentsNumber = it->parents_.size();
|
||||
bool finished = false;
|
||||
|
||||
// If we have no parent in the stack and the object has no parent, nothing to do : skip the loop
|
||||
if (parentsNumber == 0 && serializer->parentStackId.size() == 0) { finished = true; }
|
||||
|
||||
while (!finished)
|
||||
{
|
||||
// If we need to add a parent
|
||||
if (serializer->parentStackId.size() <= parentsNumber)
|
||||
{
|
||||
if (serializer->parentStackId.empty()) { scene.addParent(*(it->parents_.at(0))); }
|
||||
else
|
||||
{
|
||||
size_t diff = parentsNumber - serializer->parentStackId.size();
|
||||
|
||||
// If we have the wrong parent in the list
|
||||
if (serializer->parentStackId.top() != it->parents_.at(parentsNumber - diff - 1)->id()) {
|
||||
scene.closeParent();
|
||||
} else {
|
||||
// So far we have the right parents, we just need to add the missing ones
|
||||
for (size_t i = parentsNumber - diff; i < parentsNumber; i++) { scene.addParent(*(it->parents_.at(i))); }
|
||||
|
||||
// if diff == 0, we can leave the loop. In fact we have the right number of parents, and the last one is ok
|
||||
if (diff == 0) { finished = true; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Close the finished nodes. After this we get the first case (serializer->parentStackId.size() <= parentsNumber)
|
||||
while (serializer->parentStackId.size() > parentsNumber) { scene.closeParent(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @todo redundant information using ID as both ID and Name, maybe omit Name or allow specifying what would be used as the name
|
||||
scene.add(object_name, object_name, it->representation_id, it->material_references, it->transformation);
|
||||
}
|
||||
|
||||
//close the remaining parent tags.
|
||||
while (serializer->parentStackId.size() > 0) { scene.closeParent(); }
|
||||
|
||||
scene.write();
|
||||
stream.endDocument();
|
||||
}
|
||||
|
||||
|
||||
bool ColladaSerializer::ready() {
|
||||
return true;
|
||||
}
|
||||
@@ -266,9 +532,8 @@ void ColladaSerializer::writeHeader() {
|
||||
exporter.startDocument(unit_name, unit_magnitude);
|
||||
}
|
||||
|
||||
void ColladaSerializer::write(const IfcGeom::TriangulationElement<double>* o) {
|
||||
const IfcGeom::Representation::Triangulation<double>& mesh = o->geometry();
|
||||
exporter.write(o->guid(), o->name(), o->type(), o->id(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(), mesh.faces(), mesh.material_ids(), mesh.materials());
|
||||
void ColladaSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
|
||||
exporter.write(o);
|
||||
}
|
||||
|
||||
void ColladaSerializer::finalize() {
|
||||
|
||||
@@ -22,135 +22,215 @@
|
||||
#ifndef COLLADASERIALIZER_H
|
||||
#define COLLADASERIALIZER_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201 4512)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#endif
|
||||
#include <COLLADASWStreamWriter.h>
|
||||
#include <COLLADASWPrimitves.h>
|
||||
#include <COLLADASWLibraryGeometries.h>
|
||||
#include <COLLADASWSource.h>
|
||||
#include <COLLADASWScene.h>
|
||||
#include <COLLADASWNode.h>
|
||||
#include <COLLADASWInstanceGeometry.h>
|
||||
#include <COLLADASWLibraryGeometries.h>
|
||||
#include <COLLADASWLibraryVisualScenes.h>
|
||||
#include <COLLADASWLibraryEffects.h>
|
||||
#include <COLLADASWLibraryMaterials.h>
|
||||
#include <COLLADASWBaseInputElement.h>
|
||||
#include <COLLADASWAsset.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "../ifcgeom/IfcGeomIterator.h"
|
||||
|
||||
#include "../ifcconvert/GeometrySerializer.h"
|
||||
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/io.hpp>
|
||||
|
||||
|
||||
class ColladaSerializer : public GeometrySerializer
|
||||
{
|
||||
// TODO The vast amount of implement details of ColladaSerializer could be hidden to the cpp file.
|
||||
private:
|
||||
std::stack<int> parentStackId;
|
||||
|
||||
class ColladaExporter
|
||||
{
|
||||
private:
|
||||
class ColladaGeometries : public COLLADASW::LibraryGeometries
|
||||
{
|
||||
ColladaGeometries(const ColladaGeometries&); //N/A
|
||||
ColladaGeometries& operator =(const ColladaGeometries&); //N/A
|
||||
public:
|
||||
explicit ColladaGeometries(COLLADASW::StreamWriter& stream)
|
||||
explicit ColladaGeometries(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
|
||||
: COLLADASW::LibraryGeometries(&stream)
|
||||
, serializer(_serializer)
|
||||
{}
|
||||
void addFloatSource(const std::string& mesh_id, const std::string& suffix, const std::vector<double>& floats, const char* coords = "XYZ");
|
||||
void write(const std::string mesh_id, const std::string& default_material_name, const std::vector<double>& positions, const std::vector<double>& normals, const std::vector<int>& indices, const std::vector<int> material_ids, const std::vector<IfcGeom::Material>& materials);
|
||||
void addFloatSource(const std::string& mesh_id, const std::string& suffix,
|
||||
const std::vector<real_t>& floats, const char* coords = "XYZ");
|
||||
/// @todo pass simply DeferredObject?
|
||||
void write(
|
||||
const std::string &mesh_id, const std::string &default_material_name,
|
||||
const std::vector<real_t>& positions, const std::vector<real_t>& normals,
|
||||
const std::vector<int>& faces, const std::vector<int>& edges,
|
||||
const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
|
||||
const std::vector<real_t>& uvs, const std::vector<std::string>& material_references);
|
||||
void close();
|
||||
ColladaSerializer *serializer;
|
||||
};
|
||||
class ColladaScene : public COLLADASW::LibraryVisualScenes
|
||||
{
|
||||
private:
|
||||
ColladaScene(const ColladaScene&); //N/A
|
||||
ColladaScene& operator =(const ColladaScene&); //N/A
|
||||
|
||||
const std::string scene_id;
|
||||
bool scene_opened;
|
||||
std::stack<COLLADASW::Node*> parentNodes;
|
||||
std::stack<IfcGeom::Transformation<double> > matrixStack;
|
||||
public:
|
||||
ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream)
|
||||
ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
|
||||
: COLLADASW::LibraryVisualScenes(&stream)
|
||||
, scene_id(scene_id)
|
||||
, scene_opened(false)
|
||||
, scene_opened(false)
|
||||
, serializer(_serializer)
|
||||
{}
|
||||
void add(const std::string& node_id, const std::string& node_name, const std::string& geom_name, const std::vector<std::string>& material_ids, const std::vector<double>& matrix);
|
||||
void add(const std::string& node_id, const std::string& node_name, const std::string& geom_name,
|
||||
const std::vector<std::string>& material_ids, const IfcGeom::Transformation<real_t>& matrix);
|
||||
void addParent(const IfcGeom::Element<real_t>& parent);
|
||||
void closeParent();
|
||||
COLLADASW::Node* GetDirectParent();
|
||||
void write();
|
||||
ColladaSerializer *serializer;
|
||||
};
|
||||
class ColladaMaterials : public COLLADASW::LibraryMaterials
|
||||
{
|
||||
ColladaMaterials(const ColladaMaterials&); //N/A
|
||||
ColladaMaterials& operator =(const ColladaMaterials&); //N/A
|
||||
private:
|
||||
class ColladaEffects : public COLLADASW::LibraryEffects
|
||||
{
|
||||
ColladaEffects(const ColladaEffects&); //N/A
|
||||
ColladaEffects& operator =(const ColladaEffects&); //N/A
|
||||
public:
|
||||
explicit ColladaEffects(COLLADASW::StreamWriter& stream)
|
||||
: COLLADASW::LibraryEffects(&stream)
|
||||
{}
|
||||
void write(const IfcGeom::Material& material);
|
||||
void write(const IfcGeom::Material &material, const std::string &material_uri);
|
||||
void close();
|
||||
ColladaSerializer *serializer;
|
||||
};
|
||||
std::vector<IfcGeom::Material> materials;
|
||||
ColladaEffects effects;
|
||||
std::vector<std::string> material_uris;
|
||||
public:
|
||||
explicit ColladaMaterials(COLLADASW::StreamWriter& stream)
|
||||
explicit ColladaMaterials(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
|
||||
: COLLADASW::LibraryMaterials(&stream)
|
||||
, effects(stream)
|
||||
, serializer(_serializer)
|
||||
, effects(stream)
|
||||
{}
|
||||
void add(const IfcGeom::Material& material);
|
||||
std::string getMaterialUri(const IfcGeom::Material& material);
|
||||
bool contains(const IfcGeom::Material& material);
|
||||
void write();
|
||||
ColladaSerializer *serializer;
|
||||
ColladaEffects effects;
|
||||
};
|
||||
|
||||
class DeferredObject {
|
||||
|
||||
friend bool operator < (const DeferredObject& def_obj1, const DeferredObject& def_obj2) {
|
||||
size_t size = (def_obj1.parents_.size() < def_obj2.parents_.size() ? def_obj1.parents_.size() : def_obj2.parents_.size());
|
||||
size_t cpt = 0;
|
||||
|
||||
// Skip the shared parents
|
||||
while (cpt < size && *(def_obj1.parents_.at(cpt)) == *(def_obj2.parents_.at(cpt))) {
|
||||
cpt++;
|
||||
}
|
||||
|
||||
// If a parent list container the other one
|
||||
if (cpt >= size) {
|
||||
return def_obj1.parents_.size() < def_obj2.parents_.size();
|
||||
} else {
|
||||
return *(def_obj1.parents_.at(cpt)) < *(def_obj2.parents_.at(cpt));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::string guid, name, type;
|
||||
int obj_id;
|
||||
std::vector<double> matrix;
|
||||
std::vector<double> vertices;
|
||||
std::vector<double> normals;
|
||||
std::vector<int> indices;
|
||||
std::string unique_id, representation_id, type;
|
||||
IfcGeom::Transformation<real_t> transformation;
|
||||
std::vector<real_t> vertices;
|
||||
std::vector<real_t> normals;
|
||||
std::vector<int> faces;
|
||||
std::vector<int> edges;
|
||||
std::vector<int> material_ids;
|
||||
std::vector<IfcGeom::Material> materials;
|
||||
std::vector<std::string> material_references;
|
||||
DeferredObject(const std::string& guid, const std::string& name, const std::string& type, int obj_id, const std::vector<double>& matrix, const std::vector<double>& vertices,
|
||||
const std::vector<double>& normals, const std::vector<int>& indices, const std::vector<int>& material_ids,
|
||||
const std::vector<IfcGeom::Material>& materials, const std::vector<std::string>& material_references)
|
||||
: guid(guid)
|
||||
, name(name)
|
||||
std::vector<real_t> uvs;
|
||||
std::vector<const IfcGeom::Element<real_t>*> parents_;
|
||||
|
||||
DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const IfcGeom::Transformation<real_t>& transformation,
|
||||
const std::vector<real_t>& vertices, const std::vector<real_t>& normals, const std::vector<int>& faces,
|
||||
const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
|
||||
const std::vector<std::string>& material_references, const std::vector<real_t>& uvs)
|
||||
: unique_id(unique_id)
|
||||
, representation_id(representation_id)
|
||||
, type(type)
|
||||
, obj_id(obj_id)
|
||||
, matrix(matrix)
|
||||
, transformation(transformation)
|
||||
, vertices(vertices)
|
||||
, normals(normals)
|
||||
, indices(indices)
|
||||
, faces(faces)
|
||||
, edges(edges)
|
||||
, material_ids(material_ids)
|
||||
, materials(materials)
|
||||
, material_references(material_references)
|
||||
, uvs(uvs)
|
||||
{}
|
||||
const std::string Name() const;
|
||||
|
||||
std::vector<const IfcGeom::Element<real_t>*>& parents() { return parents_; }
|
||||
const std::vector<const IfcGeom::Element<real_t>*>& parents() const { return parents_; }
|
||||
};
|
||||
COLLADABU::NativeString filename;
|
||||
COLLADASW::StreamWriter stream;
|
||||
ColladaGeometries geometries;
|
||||
ColladaScene scene;
|
||||
ColladaMaterials materials;
|
||||
public:
|
||||
ColladaExporter(const std::string& scene_name, const std::string& fn)
|
||||
: filename(fn.c_str())
|
||||
, stream(filename)
|
||||
, geometries(stream)
|
||||
, scene(scene_name, stream)
|
||||
, materials(stream)
|
||||
{}
|
||||
/// @param double_precision Whether to use "double precision" (up to 16 decimals) or not (6 or 7 decimals).
|
||||
ColladaExporter(const std::string& scene_name, const std::string& fn, ColladaSerializer *_serializer,
|
||||
bool double_precision)
|
||||
: filename(fn)
|
||||
, stream(COLLADASW::NativeString(filename.c_str(), COLLADASW::NativeString::ENCODING_UTF8), double_precision)
|
||||
, scene(scene_name, stream, _serializer)
|
||||
, materials(stream, _serializer)
|
||||
, geometries(stream, _serializer)
|
||||
, serializer(_serializer)
|
||||
{
|
||||
}
|
||||
ColladaMaterials materials;
|
||||
ColladaGeometries geometries;
|
||||
ColladaSerializer *serializer;
|
||||
std::vector<DeferredObject> deferreds;
|
||||
virtual ~ColladaExporter() {}
|
||||
void startDocument(const std::string& unit_name, float unit_magnitude);
|
||||
void write(const std::string& guid, const std::string& name, const std::string& type, int obj_id, const std::vector<double>& matrix, const std::vector<double>& vertices, const std::vector<double>& normals, const std::vector<int>& indices, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void endDocument();
|
||||
};
|
||||
ColladaExporter exporter;
|
||||
std::string unit_name;
|
||||
float unit_magnitude;
|
||||
public:
|
||||
ColladaSerializer(const std::string& dae_filename)
|
||||
: GeometrySerializer()
|
||||
, exporter("IfcOpenShell", dae_filename)
|
||||
{}
|
||||
ColladaSerializer(const std::string& dae_filename, const SerializerSettings& settings)
|
||||
: GeometrySerializer(settings)
|
||||
, exporter("IfcOpenShell", dae_filename, this, settings.precision >= 15)
|
||||
{
|
||||
exporter.serializer = this;
|
||||
exporter.materials.serializer = this;
|
||||
exporter.materials.effects.serializer = this;
|
||||
exporter.geometries.serializer = this;
|
||||
}
|
||||
bool ready();
|
||||
void writeHeader();
|
||||
void write(const IfcGeom::TriangulationElement<double>* o);
|
||||
void write(const IfcGeom::BRepElement<double>* o) {}
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
|
||||
void finalize();
|
||||
bool isTesselated() const { return true; }
|
||||
void setUnitNameAndMagnitude(const std::string& name, float magnitude) {
|
||||
@@ -158,6 +238,11 @@ public:
|
||||
unit_magnitude = magnitude;
|
||||
}
|
||||
void setFile(IfcParse::IfcFile*) {}
|
||||
|
||||
std::string object_id(const IfcGeom::Element<real_t>* o) /*override*/;
|
||||
|
||||
private:
|
||||
static std::string differentiateSlabTypes(const IfcSchema::IfcSlab *slab);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,17 +20,78 @@
|
||||
#ifndef GEOMETRYSERIALIZER_H
|
||||
#define GEOMETRYSERIALIZER_H
|
||||
|
||||
#ifdef IFCCONVERT_DOUBLE_PRECISION
|
||||
typedef double real_t;
|
||||
#else
|
||||
typedef float real_t;
|
||||
#endif
|
||||
|
||||
#include "../ifcconvert/Serializer.h"
|
||||
#include "../ifcgeom/IfcGeomIterator.h"
|
||||
|
||||
class SerializerSettings : public IfcGeom::IteratorSettings
|
||||
{
|
||||
public:
|
||||
enum Setting
|
||||
{
|
||||
/// Use entity names instead of unique IDs for naming elements.
|
||||
/// Applicable for OBJ, DAE, and SVG output.
|
||||
USE_ELEMENT_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 1),
|
||||
/// Use entity GUIDs instead of unique IDs for naming elements.
|
||||
/// Applicable for OBJ, DAE, and SVG output.
|
||||
USE_ELEMENT_GUIDS = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 2),
|
||||
/// Use material names instead of unique IDs for naming materials.
|
||||
/// Applicable for OBJ and DAE output.
|
||||
USE_MATERIAL_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 3),
|
||||
/// Use element types instead of unique IDs for naming elements.
|
||||
/// Applicable for DAE output.
|
||||
USE_ELEMENT_TYPES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 4),
|
||||
/// Order the elements using their IfcBuildingStorey parent
|
||||
/// Applicable for DAE output
|
||||
USE_ELEMENT_HIERARCHY = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 5),
|
||||
/// Number of different setting flags.
|
||||
NUM_SETTINGS = 5
|
||||
};
|
||||
|
||||
SerializerSettings()
|
||||
: precision(DEFAULT_PRECISION)
|
||||
{
|
||||
memset(offset, 0, sizeof(offset));
|
||||
}
|
||||
|
||||
/// Optional offset that is applied to serialized objects, (0,0,0) by default.
|
||||
double offset[3];
|
||||
|
||||
/// Sets the precision used to format floating-point values, 15 by default.
|
||||
/// Use a negative value to use the system's default precision (should be 6 typically).
|
||||
short precision;
|
||||
|
||||
enum { DEFAULT_PRECISION = 15 };
|
||||
};
|
||||
|
||||
class GeometrySerializer : public Serializer {
|
||||
public:
|
||||
GeometrySerializer(const SerializerSettings& settings) : settings_(settings) {}
|
||||
virtual ~GeometrySerializer() {}
|
||||
|
||||
virtual bool isTesselated() const = 0;
|
||||
virtual void write(const IfcGeom::TriangulationElement<double>* o) = 0;
|
||||
virtual void write(const IfcGeom::BRepElement<double>* o) = 0;
|
||||
virtual void write(const IfcGeom::TriangulationElement<real_t>* o) = 0;
|
||||
virtual void write(const IfcGeom::BRepElement<real_t>* o) = 0;
|
||||
virtual void setUnitNameAndMagnitude(const std::string& name, float magnitude) = 0;
|
||||
|
||||
const SerializerSettings& settings() const { return settings_; }
|
||||
SerializerSettings& settings() { return settings_; }
|
||||
|
||||
/// Returns ID for the object depending on the used setting.
|
||||
virtual std::string object_id(const IfcGeom::Element<real_t>* o)
|
||||
{
|
||||
if (settings_.get(SerializerSettings::USE_ELEMENT_GUIDS)) return o->guid();
|
||||
if (settings_.get(SerializerSettings::USE_ELEMENT_NAMES)) return o->name();
|
||||
return o->unique_id();
|
||||
}
|
||||
|
||||
protected:
|
||||
SerializerSettings settings_;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+918
-194
File diff suppressed because it is too large
Load Diff
@@ -20,21 +20,20 @@
|
||||
#ifndef IGESSERIALIZER_H
|
||||
#define IGESSERIALIZER_H
|
||||
|
||||
#include <IGESControl_Controller.hxx>
|
||||
#include "OpenCascadeBasedSerializer.h"
|
||||
|
||||
#include <IGESControl_Writer.hxx>
|
||||
#include <Interface_Static.hxx>
|
||||
|
||||
#include "../ifcgeom/IfcGeomIterator.h"
|
||||
|
||||
#include "../ifcconvert/OpenCascadeBasedSerializer.h"
|
||||
|
||||
class IgesSerializer : public OpenCascadeBasedSerializer
|
||||
{
|
||||
private:
|
||||
IGESControl_Writer writer;
|
||||
IGESControl_Writer writer;
|
||||
public:
|
||||
explicit IgesSerializer(const std::string& out_filename)
|
||||
: OpenCascadeBasedSerializer(out_filename)
|
||||
/// @note IGESControl_Controller::Init() must be called prior to instantiating IgesSerializer.
|
||||
/// See http://tracker.dev.opencascade.org/view.php?id=23679 for more information.
|
||||
IgesSerializer(const std::string& out_filename, const SerializerSettings& settings)
|
||||
: OpenCascadeBasedSerializer(out_filename, settings)
|
||||
{}
|
||||
virtual ~IgesSerializer() {}
|
||||
void writeShape(const TopoDS_Shape& shape) {
|
||||
@@ -43,12 +42,13 @@ public:
|
||||
void finalize() {
|
||||
writer.Write(out_filename.c_str());
|
||||
}
|
||||
void setUnitNameAndMagnitude(const std::string& name, float magnitude) {
|
||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float magnitude) {
|
||||
const char* symbol = getSymbolForUnitMagnitude(magnitude);
|
||||
if (symbol) {
|
||||
Interface_Static::SetCVal("xstep.cascade.unit", symbol);
|
||||
Interface_Static::SetCVal("write.iges.unit", symbol);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -17,56 +17,39 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "OpenCascadeBasedSerializer.h"
|
||||
|
||||
#include "../ifcparse/utils.h"
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
|
||||
#include <BRepBuilderAPI_GTransform.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include "OpenCascadeBasedSerializer.h"
|
||||
|
||||
bool OpenCascadeBasedSerializer::ready() {
|
||||
std::ofstream test_file(out_filename.c_str(), std::ios_base::binary);
|
||||
std::ofstream test_file(IfcUtil::path::from_utf8(out_filename).c_str(), std::ios_base::binary);
|
||||
bool succeeded = test_file.is_open();
|
||||
test_file.close();
|
||||
remove(out_filename.c_str());
|
||||
IfcUtil::path::delete_file(out_filename);
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void OpenCascadeBasedSerializer::write(const IfcGeom::BRepElement<double>* o) {
|
||||
void OpenCascadeBasedSerializer::write(const IfcGeom::BRepElement<real_t>* o) {
|
||||
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = o->geometry().begin(); it != o->geometry().end(); ++ it) {
|
||||
gp_GTrsf gtrsf = it->Placement();
|
||||
|
||||
const std::vector<double>& matrix = o->transformation().matrix().data();
|
||||
|
||||
// Convert the matrix back into a transformation object. The tolerance values
|
||||
// are taken into consideration to reconstruct the form of the transformation.
|
||||
gp_Trsf o_trsf;
|
||||
o_trsf.SetValues(
|
||||
matrix[0], matrix[3], matrix[6], matrix[ 9],
|
||||
matrix[1], matrix[4], matrix[7], matrix[10],
|
||||
matrix[2], matrix[5], matrix[8], matrix[11]
|
||||
#if OCC_VERSION_HEX < 0x60800
|
||||
, Precision::Angular(), Precision::Confusion()
|
||||
#endif
|
||||
);
|
||||
const gp_Trsf& o_trsf = o->transformation().data();
|
||||
gtrsf.PreMultiply(o_trsf);
|
||||
|
||||
if (o->geometry().settings().get(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS)) {
|
||||
gp_Trsf scale;
|
||||
scale.SetScaleFactor(1.0 / o->geometry().settings().unit_magnitude());
|
||||
gtrsf.PreMultiply(scale);
|
||||
}
|
||||
|
||||
const TopoDS_Shape& s = it->Shape();
|
||||
|
||||
bool trsf_valid = false;
|
||||
gp_Trsf trsf;
|
||||
try {
|
||||
trsf = gtrsf.Trsf();
|
||||
trsf_valid = true;
|
||||
} catch (...) {}
|
||||
|
||||
const TopoDS_Shape moved_shape = trsf_valid
|
||||
? BRepBuilderAPI_Transform(s, trsf, true).Shape()
|
||||
: BRepBuilderAPI_GTransform(s, gtrsf, true).Shape();
|
||||
const TopoDS_Shape& s = it->Shape();
|
||||
const TopoDS_Shape moved_shape = IfcGeom::Kernel::apply_transformation(s, gtrsf);
|
||||
|
||||
writeShape(moved_shape);
|
||||
}
|
||||
|
||||
@@ -25,23 +25,24 @@
|
||||
#include "../ifcconvert/GeometrySerializer.h"
|
||||
|
||||
class OpenCascadeBasedSerializer : public GeometrySerializer {
|
||||
OpenCascadeBasedSerializer(const OpenCascadeBasedSerializer&); //N/A
|
||||
OpenCascadeBasedSerializer& operator =(const OpenCascadeBasedSerializer&); //N/A
|
||||
protected:
|
||||
const std::string& out_filename;
|
||||
const std::string out_filename;
|
||||
const char* getSymbolForUnitMagnitude(float mag);
|
||||
public:
|
||||
explicit OpenCascadeBasedSerializer(const std::string& out_filename)
|
||||
: GeometrySerializer()
|
||||
explicit OpenCascadeBasedSerializer(const std::string& out_filename, const SerializerSettings& settings)
|
||||
: GeometrySerializer(settings)
|
||||
, out_filename(out_filename)
|
||||
{}
|
||||
virtual ~OpenCascadeBasedSerializer() {}
|
||||
void writeHeader() {}
|
||||
void writeMaterial(const IfcGeom::SurfaceStyle& style) {}
|
||||
bool ready();
|
||||
virtual void writeShape(const TopoDS_Shape& shape) = 0;
|
||||
void write(const IfcGeom::TriangulationElement<double>* o) {}
|
||||
void write(const IfcGeom::BRepElement<double>* o);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* /*o*/) {}
|
||||
void write(const IfcGeom::BRepElement<real_t>* o);
|
||||
bool isTesselated() const { return false; }
|
||||
void setFile(IfcParse::IfcFile*) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#ifndef STEPSERIALIZER_H
|
||||
#define STEPSERIALIZER_H
|
||||
|
||||
#include <STEPControl_Controller.hxx>
|
||||
#include <STEPControl_Writer.hxx>
|
||||
#include <Interface_Static.hxx>
|
||||
|
||||
@@ -33,8 +32,8 @@ class StepSerializer : public OpenCascadeBasedSerializer
|
||||
private:
|
||||
STEPControl_Writer writer;
|
||||
public:
|
||||
explicit StepSerializer(const std::string& out_filename)
|
||||
: OpenCascadeBasedSerializer(out_filename)
|
||||
explicit StepSerializer(const std::string& out_filename, const SerializerSettings& settings)
|
||||
: OpenCascadeBasedSerializer(out_filename, settings)
|
||||
{}
|
||||
virtual ~StepSerializer() {}
|
||||
void writeShape(const TopoDS_Shape& shape) {
|
||||
@@ -49,9 +48,10 @@ public:
|
||||
writer.Write(out_filename.c_str());
|
||||
std::cout.rdbuf(sb);
|
||||
}
|
||||
void setUnitNameAndMagnitude(const std::string& name, float magnitude) {
|
||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float magnitude) {
|
||||
const char* symbol = getSymbolForUnitMagnitude(magnitude);
|
||||
if (symbol) {
|
||||
Interface_Static::SetCVal("xstep.cascade.unit", symbol);
|
||||
Interface_Static::SetCVal("write.step.unit", symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +0,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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file defines default materials for several IFC datatypes *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef SURFACESTYLE_H
|
||||
#define SURFACESTYLE_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <array>
|
||||
|
||||
class SurfaceStyle {
|
||||
public:
|
||||
class ColorComponent {
|
||||
private:
|
||||
std::array<double, 3> data;
|
||||
public:
|
||||
ColorComponent(double r, double g, double b) {
|
||||
data[0] = r; data[1] = g; data[2] = b;
|
||||
}
|
||||
const double& R() const { return data[0]; }
|
||||
const double& G() const { return data[1]; }
|
||||
const double& B() const { return data[2]; }
|
||||
double& R() { return data[0]; }
|
||||
double& G() { return data[1]; }
|
||||
double& B() { return data[2]; }
|
||||
};
|
||||
private:
|
||||
std::string name;
|
||||
ColorComponent diffuse, specular, ambient;
|
||||
double transparency;
|
||||
double specularity;
|
||||
public:
|
||||
SurfaceStyle(const std::string& name,
|
||||
double dr = 0.7, double dg = 0.7, double db = 0.7,
|
||||
double sr = 0.2, double sg = 0.2, double sb = 0.2,
|
||||
double ar = 0.1, double ag = 0.1, double ab = 0.1,
|
||||
double Ns = 10.0, double Tr = 1.0)
|
||||
: name(name)
|
||||
, diffuse(dr, dg, db)
|
||||
, specular(sr, sg, sb)
|
||||
, ambient(ar, ag, ab)
|
||||
, transparency(Tr)
|
||||
, specularity(Ns)
|
||||
{}
|
||||
const std::string& Name() const { return name; }
|
||||
const ColorComponent& Diffuse() const { return diffuse; }
|
||||
const ColorComponent& Specular() const { return specular; }
|
||||
const ColorComponent& Ambient() const { return ambient; }
|
||||
double Transparency() const { return transparency; }
|
||||
double Specularity() const { return specularity; }
|
||||
};
|
||||
|
||||
SurfaceStyle GetDefaultMaterial(const std::string& s);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,527 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Copyright 2015 IfcOpenShell and ROOT B.V. *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
#include <gp_Elips.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepAlgo_Section.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
#include <BRepAlgoAPI_Section.hxx>
|
||||
#include <ShapeAnalysis_FreeBounds.hxx>
|
||||
#include <TopTools_HSequenceOfShape.hxx>
|
||||
|
||||
#include <BRepAdaptor_Curve.hxx>
|
||||
#include <GCPnts_QuasiUniformDeflection.hxx>
|
||||
|
||||
#include <Geom_Curve.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <gp_Ax22d.hxx>
|
||||
#include <Standard_Version.hxx>
|
||||
#include <GeomAPI.hxx>
|
||||
|
||||
#include "../ifcparse/IfcGlobalId.h"
|
||||
|
||||
#include "SvgSerializer.h"
|
||||
|
||||
const double PI2 = M_PI * 2.;
|
||||
|
||||
bool SvgSerializer::ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
|
||||
/* ShapeFix_Wire fix;
|
||||
Handle(ShapeExtend_WireData) data = new ShapeExtend_WireData;
|
||||
for (TopExp_Explorer edges(result, TopAbs_EDGE); edges.More(); edges.Next()) {
|
||||
data->Add(edges.Current());
|
||||
}
|
||||
fix.Load(data);
|
||||
fix.FixReorder();
|
||||
fix.FixConnected();
|
||||
const TopoDS_Wire fixed_wire = fix.Wire(); */
|
||||
|
||||
bool first = true;
|
||||
util::string_buffer path;
|
||||
for (TopExp_Explorer edges(wire, TopAbs_EDGE); edges.More(); edges.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(edges.Current());
|
||||
|
||||
double u1, u2;
|
||||
Handle(Geom_Curve) curve = BRep_Tool::Curve(edge, u1, u2);
|
||||
Handle(Geom2d_Curve) curve2d;
|
||||
if (curve.IsNull()) {
|
||||
TopLoc_Location loc;
|
||||
Handle_Geom_Surface surf;
|
||||
|
||||
BRep_Tool::CurveOnSurface(edge, curve2d, surf, loc, u1, u2);
|
||||
|
||||
if (curve2d.IsNull()) {
|
||||
Logger::Error("Failed to obtain 2d and 3d curve from edge");
|
||||
continue;
|
||||
}
|
||||
|
||||
Handle(Standard_Type) sty = surf->DynamicType();
|
||||
if (sty != STANDARD_TYPE(Geom_Plane)) {
|
||||
Logger::Error("Non-planar p-curves are not supported by this serializer");
|
||||
continue;
|
||||
}
|
||||
|
||||
gp_Pln pln = Handle(Geom_Plane)::DownCast(surf)->Pln();
|
||||
curve = GeomAPI::To3d(curve2d, pln);
|
||||
}
|
||||
|
||||
Handle(Standard_Type) ty = curve->DynamicType();
|
||||
bool conical = (ty == STANDARD_TYPE(Geom_Circle) || ty == STANDARD_TYPE(Geom_Ellipse));
|
||||
bool closed = ALMOST_THE_SAME(u1 + PI2, u2);
|
||||
|
||||
if (conical && closed) {
|
||||
if (first) {
|
||||
if (ty == STANDARD_TYPE(Geom_Circle)) {
|
||||
Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(curve);
|
||||
double r = circle->Radius();
|
||||
gp_Circ c = circle->Circ();
|
||||
gp_Pnt center = c.Location();
|
||||
path.add(" <circle style=\"stroke:black; fill:none;\" r=\"");
|
||||
radii.push_back(path.add(r));
|
||||
path.add("\" cx=\"");
|
||||
xcoords.push_back(path.add(center.X()));
|
||||
path.add("\" cy=\"");
|
||||
ycoords.push_back(path.add(center.Y()));
|
||||
|
||||
growBoundingBox(center.X() - r, center.Y() - r);
|
||||
growBoundingBox(center.X() + r, center.Y() + r);
|
||||
|
||||
first = false;
|
||||
continue;
|
||||
} else if (ty == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
Handle(Geom_Ellipse) ellipse = Handle(Geom_Ellipse)::DownCast(curve);
|
||||
gp_Elips e = ellipse->Elips();
|
||||
gp_Pnt center = e.Location();
|
||||
|
||||
// Write the ellipse with major radius along X axis:
|
||||
path.add(" <ellipse style=\"stroke:black; fill:none;\" rx=\"");
|
||||
radii.push_back(path.add(e.MajorRadius()));
|
||||
path.add("\" ry=\"");
|
||||
radii.push_back(path.add(e.MinorRadius()));
|
||||
path.add("\" cx=\"");
|
||||
xcoords.push_back(path.add(center.X()));
|
||||
path.add("\" cy=\"");
|
||||
ycoords.push_back(path.add(center.Y()));
|
||||
path.add("\"");
|
||||
|
||||
// Rotate it with "transform":
|
||||
gp_Ax1 major_axis = e.XAxis();
|
||||
double z_rotation = major_axis.Direction().AngleWithRef(gp_Dir(1., 0., 0.), gp_Dir(0., 0., 1.));
|
||||
path.add(" transform=\"rotate(");
|
||||
path.add(z_rotation);
|
||||
path.add(" ");
|
||||
path.add(center.X());
|
||||
path.add(" ");
|
||||
path.add(center.Y());
|
||||
|
||||
// Bounding box:
|
||||
// More important to have all geometry in bounding box than to be minimal
|
||||
growBoundingBox(center.X() - e.MajorRadius(), center.Y() - e.MajorRadius());
|
||||
growBoundingBox(center.X() + e.MajorRadius(), center.Y() + e.MajorRadius());
|
||||
|
||||
first = false;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << "Skipping full circle/ellipse inside aggregated <path> (id "
|
||||
<< p.first << ")";
|
||||
Logger::Warning(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
const bool reversed = edge.Orientation() == TopAbs_REVERSED;
|
||||
|
||||
gp_Pnt p1, p2;
|
||||
curve->D0(u1, p1);
|
||||
curve->D0(u2, p2);
|
||||
|
||||
if (reversed) {
|
||||
std::swap(p1, p2);
|
||||
}
|
||||
|
||||
if (first) {
|
||||
path.add(" <path style=\"stroke:black; fill:none;\" d=\"");
|
||||
path.add("M");
|
||||
addXCoordinate(path.add(p1.X()));
|
||||
path.add(",");
|
||||
addYCoordinate(path.add(p1.Y()));
|
||||
|
||||
growBoundingBox(p1.X(), p1.Y());
|
||||
}
|
||||
|
||||
growBoundingBox(p2.X(), p2.Y());
|
||||
|
||||
|
||||
if (ty == STANDARD_TYPE(Geom_Circle) || ty == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(curve);
|
||||
const bool mirrored = conic->Position().Axis().Direction().Z() < 0;
|
||||
|
||||
double r1, r2;
|
||||
bool larger_arc_segment = (fmod(u2 - u1 + PI2, PI2) > M_PI);
|
||||
bool positive_direction = (u2 > u1);
|
||||
|
||||
if (mirrored != reversed) {
|
||||
// In case the local coordinate system is mirrored
|
||||
// the direction is reversed.
|
||||
positive_direction = !positive_direction;
|
||||
}
|
||||
|
||||
gp_Pnt center;
|
||||
if (ty == STANDARD_TYPE(Geom_Circle)) {
|
||||
Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(curve);
|
||||
r1 = r2 = circle->Radius();
|
||||
center = circle->Location();
|
||||
} else {
|
||||
Handle(Geom_Ellipse) ellipse = Handle(Geom_Ellipse)::DownCast(curve);
|
||||
r1 = ellipse->MajorRadius();
|
||||
r2 = ellipse->MinorRadius();
|
||||
center = ellipse->Location();
|
||||
}
|
||||
|
||||
// Make sure the arc segment is entirely inside bounding box:
|
||||
growBoundingBox(center.X() - r1, center.Y() - r1);
|
||||
growBoundingBox(center.X() + r1, center.Y() + r1);
|
||||
|
||||
// Calculate the angle between 2d vecs to have signed result
|
||||
const gp_Dir& d = conic->Position().XDirection();
|
||||
const gp_Dir2d d2(d.X(), d.Y());
|
||||
const double ang = d2.Angle(gp::DX2d());
|
||||
|
||||
// Write radii
|
||||
path.add(" A");
|
||||
addSizeComponent(path.add(r1));
|
||||
path.add(",");
|
||||
addSizeComponent(path.add(r2));
|
||||
|
||||
// Write X-axis rotation
|
||||
{ std::stringstream ss; ss << " " << ang << " ";
|
||||
path.add(ss.str()); }
|
||||
|
||||
// Write large-arc-flag and sweep-flag
|
||||
path.add(std::string(1, '0'+static_cast<int>(larger_arc_segment)));
|
||||
path.add(",");
|
||||
path.add(std::string(1, '0'+static_cast<int>(positive_direction)));
|
||||
|
||||
path.add(" ");
|
||||
|
||||
// Write arc end point
|
||||
xcoords.push_back(path.add(p2.X()));
|
||||
path.add(",");
|
||||
ycoords.push_back(path.add(p2.Y()));
|
||||
} else if (ty != STANDARD_TYPE(Geom_Line)) {
|
||||
BRepAdaptor_Curve crv(edge);
|
||||
GCPnts_QuasiUniformDeflection tessellater(crv, settings().deflection_tolerance());
|
||||
// NB: Start at 2: 1-based and skip the first point, assume it coincides with p1.
|
||||
for (int i = 2; i <= tessellater.NbPoints(); ++i) {
|
||||
gp_Pnt pi = tessellater.Value(i);
|
||||
path.add(" L");
|
||||
xcoords.push_back(path.add(pi.X()));
|
||||
path.add(",");
|
||||
ycoords.push_back(path.add(pi.Y()));
|
||||
|
||||
growBoundingBox(pi.X(), pi.Y());
|
||||
}
|
||||
} else {
|
||||
// Either a Geom_Line or something unimplemented,
|
||||
// drawn as a straight line segment.
|
||||
path.add(" L");
|
||||
xcoords.push_back(path.add(p2.X()));
|
||||
path.add(",");
|
||||
ycoords.push_back(path.add(p2.Y()));
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
path.add("\"/>\n");
|
||||
p.second.push_back(path);
|
||||
}
|
||||
|
||||
SvgSerializer::path_object& SvgSerializer::start_path(IfcSchema::IfcBuildingStorey* storey, const std::string& id) {
|
||||
SvgSerializer::path_object& p = paths.insert(std::make_pair(storey, path_object()))->second;
|
||||
p.first = id;
|
||||
return p;
|
||||
}
|
||||
|
||||
void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
{
|
||||
IfcSchema::IfcBuildingStorey* storey = storey_;
|
||||
boost::optional<double> storey_elevation = boost::none;
|
||||
IfcSchema::IfcObjectDefinition* obdef = static_cast<IfcSchema::IfcObjectDefinition*>(file->entityById(o->id()));
|
||||
|
||||
#ifndef USE_IFC4
|
||||
typedef IfcSchema::IfcRelDecomposes decomposition_element;
|
||||
#else
|
||||
typedef IfcSchema::IfcRelAggregates decomposition_element;
|
||||
#endif
|
||||
|
||||
for (; storey == 0;) {
|
||||
// Iterate over the decomposing element to find the parent IfcBuildingStorey
|
||||
decomposition_element::list::ptr decomposes = obdef->Decomposes();
|
||||
if (!decomposes->size()) {
|
||||
if (obdef->is(IfcSchema::Type::IfcElement)) {
|
||||
IfcSchema::IfcRelContainedInSpatialStructure::list::ptr containment = ((IfcSchema::IfcElement*)obdef)->ContainedInStructure();
|
||||
if (!containment->size()) {
|
||||
break;
|
||||
}
|
||||
for (IfcSchema::IfcRelContainedInSpatialStructure::list::it it = containment->begin(); it != containment->end(); ++it) {
|
||||
IfcSchema::IfcRelContainedInSpatialStructure* container = *it;
|
||||
if (container->RelatingStructure() != obdef) {
|
||||
obdef = container->RelatingStructure();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (decomposition_element::list::it it = decomposes->begin(); it != decomposes->end(); ++it) {
|
||||
decomposition_element* decompose = *it;
|
||||
if (decompose->RelatingObject() != obdef) {
|
||||
obdef = decompose->RelatingObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obdef->is(IfcSchema::Type::IfcBuildingStorey)) {
|
||||
storey = static_cast<IfcSchema::IfcBuildingStorey*>(obdef);
|
||||
if (storey->hasElevation()) {
|
||||
const IfcGeom::ElementSettings& settings = o->geometry().settings();
|
||||
storey_elevation = storey->Elevation() * settings.unit_magnitude();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// With a global section height, building storeys are not a requirement.
|
||||
if (!storey && !section_height) return;
|
||||
|
||||
path_object& p = start_path(storey, nameElement(o));
|
||||
|
||||
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = o->geometry().begin(); it != o->geometry().end(); ++ it) {
|
||||
gp_GTrsf gtrsf = it->Placement();
|
||||
|
||||
const gp_Trsf& o_trsf = o->transformation().data();
|
||||
gtrsf.PreMultiply(o_trsf);
|
||||
|
||||
const TopoDS_Shape& s = it->Shape();
|
||||
const TopoDS_Shape moved_shape = IfcGeom::Kernel::apply_transformation(s, gtrsf);
|
||||
|
||||
const double inf = std::numeric_limits<double>::infinity();
|
||||
double zmin = inf;
|
||||
double zmax = -inf;
|
||||
{TopExp_Explorer exp(moved_shape, TopAbs_VERTEX);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Vertex& vertex = TopoDS::Vertex(exp.Current());
|
||||
gp_Pnt pnt = BRep_Tool::Pnt(vertex);
|
||||
if (pnt.Z() < zmin) { zmin = pnt.Z(); }
|
||||
if (pnt.Z() > zmax) { zmax = pnt.Z(); }
|
||||
}}
|
||||
|
||||
// Empty geometry, no vertices encountered
|
||||
if (zmin == inf) continue;
|
||||
|
||||
// Determine slicing plane z coordinate, priority:
|
||||
// 1) explicitly set global section height
|
||||
// 2) containing building storey elevation + 1m
|
||||
// 3) zmin (from geometry bounding box) + 1m
|
||||
double cut_z;
|
||||
if (section_height) {
|
||||
cut_z = section_height.get();
|
||||
} else if (storey_elevation && !(zmin > *storey_elevation || zmax < *storey_elevation)) {
|
||||
cut_z = storey_elevation.get() + 1.;
|
||||
} else {
|
||||
cut_z = zmin + 1.;
|
||||
}
|
||||
|
||||
// No intersection with bounding box, fail early
|
||||
if (zmin > cut_z || zmax < cut_z) continue;
|
||||
|
||||
// Evaluate cross section geometry
|
||||
TopoDS_Shape result = BRepAlgoAPI_Section(moved_shape, gp_Pln(gp_Pnt(0, 0, cut_z), gp::DZ()));
|
||||
|
||||
Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape();
|
||||
Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape();
|
||||
{TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
edges->Append(exp.Current());
|
||||
}}
|
||||
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(edges, 1e-5, false, wires);
|
||||
|
||||
gp_Pnt prev;
|
||||
|
||||
for (int i = 1; i <= wires->Length(); ++i) {
|
||||
const TopoDS_Wire& wire = TopoDS::Wire(wires->Value(i));
|
||||
write(p, wire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SvgSerializer::setBoundingRectangle(double width, double height) {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->rescale = true;
|
||||
}
|
||||
|
||||
void SvgSerializer::finalize() {
|
||||
|
||||
if (rescale) {
|
||||
// Scale the resulting image to a bounding rectangle specified by command line arguments
|
||||
const double dx = xmax - xmin;
|
||||
const double dy = ymax - ymin;
|
||||
|
||||
double sc = 1.;
|
||||
|
||||
if (dx / width > dy / height) {
|
||||
sc = width / dx;
|
||||
} else {
|
||||
sc = height / dy;
|
||||
}
|
||||
|
||||
const double cx = xmin * sc;
|
||||
const double cy = ymin * sc;
|
||||
|
||||
{std::vector< boost::shared_ptr<util::string_buffer::float_item> >::const_iterator it;
|
||||
for (it = xcoords.begin(); it != xcoords.end(); ++it) {
|
||||
double& v = (*it)->value();
|
||||
v = v * sc - cx;
|
||||
}
|
||||
for (it = ycoords.begin(); it != ycoords.end(); ++it) {
|
||||
double& v = (*it)->value();
|
||||
v = v * sc - cy;
|
||||
}
|
||||
for (it = radii.begin(); it != radii.end(); ++it) {
|
||||
(*it)->value() *= sc;
|
||||
}}
|
||||
}
|
||||
|
||||
std::multimap<IfcSchema::IfcBuildingStorey*, path_object>::const_iterator it;
|
||||
|
||||
IfcSchema::IfcBuildingStorey* previous = 0;
|
||||
bool first = true;
|
||||
for (it = paths.begin(); it != paths.end(); ++it) {
|
||||
if (it->first != previous || first) {
|
||||
if (!first) {
|
||||
svg_file << " </g>\n";
|
||||
}
|
||||
std::ostringstream oss;
|
||||
svg_file << " <g " << nameElement(it->first) << ">\n";
|
||||
}
|
||||
svg_file << " <g " << it->second.first << ">\n";
|
||||
std::vector<util::string_buffer>::const_iterator jt;
|
||||
for (jt = it->second.second.begin(); jt != it->second.second.end(); ++jt) {
|
||||
svg_file << jt->str();
|
||||
}
|
||||
svg_file << " </g>\n";
|
||||
previous = it->first;
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
svg_file << " </g>\n";
|
||||
}
|
||||
svg_file << "</svg>" << std::endl;
|
||||
}
|
||||
|
||||
void SvgSerializer::writeHeader() {
|
||||
svg_file << "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
|
||||
}
|
||||
|
||||
std::string SvgSerializer::nameElement(const IfcGeom::Element<real_t>* elem)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
const std::string type = "product";
|
||||
const std::string name = object_id(elem);
|
||||
oss << "id=\"" << type << "-" << name<< "\"";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string SvgSerializer::nameElement(const IfcSchema::IfcProduct* elem) {
|
||||
if (elem == 0) { return ""; }
|
||||
std::ostringstream oss;
|
||||
const std::string type = elem->is(IfcSchema::Type::IfcBuildingStorey) ? "storey" : "product";
|
||||
const std::string name = (settings().get(SerializerSettings::USE_ELEMENT_GUIDS)
|
||||
? elem->GlobalId() : (settings().get(SerializerSettings::USE_ELEMENT_NAMES)
|
||||
? elem->Name() : IfcParse::IfcGlobalId(elem->GlobalId()).formatted()));
|
||||
|
||||
oss << "id=\"" << type << "-" << name << "\"";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void SvgSerializer::setFile(IfcParse::IfcFile* f) {
|
||||
file = f;
|
||||
IfcSchema::IfcBuildingStorey::list::ptr storeys = f->entitiesByType<IfcSchema::IfcBuildingStorey>();
|
||||
if (!storeys || storeys->size() == 0) {
|
||||
|
||||
IfcGeom::Kernel kernel;
|
||||
|
||||
IfcSchema::IfcProject::list::ptr projects = f->entitiesByType<IfcSchema::IfcProject>();
|
||||
if (projects->size() == 1) {
|
||||
IfcSchema::IfcProject* project = *projects->begin();
|
||||
std::pair<std::string, double> length_unit = kernel.initializeUnits(project->UnitsInContext());
|
||||
} else {
|
||||
Logger::Error("No single project encountered, output might be invalid or missing");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<IfcSchema::Type::Enum> to_derive_from;
|
||||
to_derive_from.push_back(IfcSchema::Type::IfcBuilding);
|
||||
to_derive_from.push_back(IfcSchema::Type::IfcSite);
|
||||
std::vector<IfcSchema::Type::Enum>::const_iterator it;
|
||||
for (it = to_derive_from.begin(); it != to_derive_from.end(); ++it) {
|
||||
IfcEntityList::ptr untyped = f->entitiesByType(*it);
|
||||
if (untyped) {
|
||||
IfcSchema::IfcProduct::list::ptr insts = untyped->as<IfcSchema::IfcProduct>();
|
||||
IfcSchema::IfcProduct::list::it jt;
|
||||
for (jt = insts->begin(); jt != insts->end(); ++jt) {
|
||||
IfcSchema::IfcProduct* product = *jt;
|
||||
if (product->hasObjectPlacement()) {
|
||||
gp_Trsf trsf;
|
||||
if (kernel.convert(product->ObjectPlacement(), trsf)) {
|
||||
setSectionHeight(trsf.TranslationPart().Z() + 1.);
|
||||
Logger::Warning("No building storeys encountered, used for reference:", product->entity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::Error("No building storeys encountered, output might be invalid or missing");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* Copyright 2015 IfcOpenShell and ROOT B.V. *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef SVGSERIALIZER_H
|
||||
#define SVGSERIALIZER_H
|
||||
|
||||
#include "../ifcconvert/GeometrySerializer.h"
|
||||
#include "../ifcconvert/util.h"
|
||||
|
||||
#include "../ifcparse/utils.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
class SvgSerializer : public GeometrySerializer {
|
||||
public:
|
||||
typedef std::pair<std::string, std::vector<util::string_buffer> > path_object;
|
||||
protected:
|
||||
std::ofstream svg_file;
|
||||
double xmin, ymin, xmax, ymax, width, height;
|
||||
boost::optional<double> section_height;
|
||||
bool rescale;
|
||||
std::multimap<IfcSchema::IfcBuildingStorey*, path_object> paths;
|
||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > xcoords;
|
||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > ycoords;
|
||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > radii;
|
||||
IfcParse::IfcFile* file;
|
||||
IfcSchema::IfcBuildingStorey* storey_;
|
||||
public:
|
||||
SvgSerializer(const std::string& out_filename, const SerializerSettings& settings)
|
||||
: GeometrySerializer(settings)
|
||||
, svg_file(IfcUtil::path::from_utf8(out_filename).c_str())
|
||||
, xmin(+std::numeric_limits<double>::infinity())
|
||||
, ymin(+std::numeric_limits<double>::infinity())
|
||||
, xmax(-std::numeric_limits<double>::infinity())
|
||||
, ymax(-std::numeric_limits<double>::infinity())
|
||||
, rescale(false)
|
||||
, file(0)
|
||||
, storey_(0)
|
||||
{}
|
||||
void addXCoordinate(const boost::shared_ptr<util::string_buffer::float_item>& fi) { xcoords.push_back(fi); }
|
||||
void addYCoordinate(const boost::shared_ptr<util::string_buffer::float_item>& fi) { ycoords.push_back(fi); }
|
||||
void addSizeComponent(const boost::shared_ptr<util::string_buffer::float_item>& fi) { radii.push_back(fi); }
|
||||
void growBoundingBox(double x, double y) { if (x < xmin) xmin = x; if (x > xmax) xmax = x; if (y < ymin) ymin = y; if (y > ymax) ymax = y; }
|
||||
void writeHeader();
|
||||
bool ready();
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* /*o*/) {}
|
||||
void write(const IfcGeom::BRepElement<real_t>* o);
|
||||
void write(path_object& p, const TopoDS_Wire& wire);
|
||||
path_object& start_path(IfcSchema::IfcBuildingStorey* storey, const std::string& id);
|
||||
bool isTesselated() const { return false; }
|
||||
void finalize();
|
||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
||||
void setFile(IfcParse::IfcFile* f);
|
||||
void setBoundingRectangle(double width, double height);
|
||||
void setSectionHeight(double h, IfcSchema::IfcBuildingStorey* storey = 0) { section_height = h; storey_ = storey; }
|
||||
std::string nameElement(const IfcGeom::Element<real_t>* elem);
|
||||
std::string nameElement(const IfcSchema::IfcProduct* elem);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -17,12 +17,27 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include "../ifcgeom/IfcGeomRenderStyles.h"
|
||||
|
||||
#include "WavefrontObjSerializer.h"
|
||||
|
||||
#include "../ifcgeom/IfcGeomRenderStyles.h"
|
||||
|
||||
#include "../ifcparse/utils.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iomanip>
|
||||
|
||||
WaveFrontOBJSerializer::WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings)
|
||||
: GeometrySerializer(settings)
|
||||
, mtl_filename(mtl_filename)
|
||||
, obj_stream(IfcUtil::path::from_utf8(obj_filename).c_str())
|
||||
, mtl_stream(IfcUtil::path::from_utf8(mtl_filename).c_str())
|
||||
, vcount_total(1)
|
||||
{
|
||||
obj_stream << std::setprecision(settings.precision);
|
||||
mtl_stream << std::setprecision(settings.precision);
|
||||
}
|
||||
|
||||
bool WaveFrontOBJSerializer::ready() {
|
||||
return obj_stream.is_open() && mtl_stream.is_open();
|
||||
}
|
||||
@@ -40,11 +55,16 @@ void WaveFrontOBJSerializer::writeHeader() {
|
||||
mtl_basename = mtl_basename.substr(slash+1);
|
||||
}
|
||||
obj_stream << "mtllib " << mtl_basename << "\n";
|
||||
mtl_stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n";
|
||||
mtl_stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n";
|
||||
}
|
||||
|
||||
void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style) {
|
||||
mtl_stream << "newmtl " << style.name() << "\n";
|
||||
void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style)
|
||||
{
|
||||
std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES)
|
||||
? style.original_name() : style.name());
|
||||
IfcUtil::sanitate_material_name(material_name);
|
||||
mtl_stream << "newmtl " << material_name << "\n";
|
||||
|
||||
if (style.hasDiffuse()) {
|
||||
const double* diffuse = style.diffuse();
|
||||
mtl_stream << "Kd " << diffuse[0] << " " << diffuse[1] << " " << diffuse[2] << "\n";
|
||||
@@ -59,46 +79,52 @@ void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style) {
|
||||
if (style.hasTransparency()) {
|
||||
const double transparency = 1.0 - style.transparency();
|
||||
if (transparency < 1) {
|
||||
mtl_stream << "Tr " << transparency << "\n";
|
||||
mtl_stream << "d " << transparency << "\n";
|
||||
mtl_stream << "D " << transparency << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<double>* o) {
|
||||
|
||||
std::string tmp = o->name().empty() ? o->guid() : o->name();
|
||||
|
||||
std::replace( tmp.begin(), tmp.end(), ' ', '_');
|
||||
const std::string name = tmp;
|
||||
obj_stream << "g " << name << "\n";
|
||||
void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<real_t>* o)
|
||||
{
|
||||
obj_stream << "g " << object_id(o) << "\n";
|
||||
obj_stream << "s 1" << "\n";
|
||||
|
||||
const IfcGeom::Representation::Triangulation<double>& mesh = o->geometry();
|
||||
|
||||
const int vcount = mesh.verts().size() / 3;
|
||||
for ( std::vector<double>::const_iterator it = mesh.verts().begin(); it != mesh.verts().end(); ) {
|
||||
const double x = *(it++);
|
||||
const double y = *(it++);
|
||||
const double z = *(it++);
|
||||
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
|
||||
|
||||
const int vcount = (int)mesh.verts().size() / 3;
|
||||
for ( std::vector<real_t>::const_iterator it = mesh.verts().begin(); it != mesh.verts().end(); ) {
|
||||
const real_t x = *(it++) + (real_t)settings().offset[0];
|
||||
const real_t y = *(it++) + (real_t)settings().offset[1];
|
||||
const real_t z = *(it++) + (real_t)settings().offset[2];
|
||||
obj_stream << "v " << x << " " << y << " " << z << "\n";
|
||||
}
|
||||
for ( std::vector<double>::const_iterator it = mesh.normals().begin(); it != mesh.normals().end(); ) {
|
||||
const double x = *(it++);
|
||||
const double y = *(it++);
|
||||
const double z = *(it++);
|
||||
|
||||
for ( std::vector<real_t>::const_iterator it = mesh.normals().begin(); it != mesh.normals().end(); ) {
|
||||
const real_t x = *(it++);
|
||||
const real_t y = *(it++);
|
||||
const real_t z = *(it++);
|
||||
obj_stream << "vn " << x << " " << y << " " << z << "\n";
|
||||
}
|
||||
|
||||
for (std::vector<real_t>::const_iterator it = mesh.uvs().begin(); it != mesh.uvs().end();) {
|
||||
const real_t u = *it++;
|
||||
const real_t v = *it++;
|
||||
obj_stream << "vt " << u << " " << v << "\n";
|
||||
}
|
||||
|
||||
int previous_material_id = -2;
|
||||
std::vector<int>::const_iterator material_it = mesh.material_ids().begin();
|
||||
|
||||
const bool has_uvs = !mesh.uvs().empty();
|
||||
const bool has_normals = !mesh.normals().empty();
|
||||
for ( std::vector<int>::const_iterator it = mesh.faces().begin(); it != mesh.faces().end(); ) {
|
||||
|
||||
const int material_id = *(material_it++);
|
||||
if (material_id != previous_material_id) {
|
||||
const IfcGeom::Material& material = mesh.materials()[material_id];
|
||||
const std::string material_name = material.name();
|
||||
std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES)
|
||||
? material.original_name() : material.name());
|
||||
IfcUtil::sanitate_material_name(material_name);
|
||||
obj_stream << "usemtl " << material_name << "\n";
|
||||
if (materials.find(material_name) == materials.end()) {
|
||||
writeMaterial(material);
|
||||
@@ -110,7 +136,52 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement<double>*
|
||||
const int v1 = *(it++)+vcount_total;
|
||||
const int v2 = *(it++)+vcount_total;
|
||||
const int v3 = *(it++)+vcount_total;
|
||||
obj_stream << "f " << v1 << "//" << v1 << " " << v2 << "//" << v2 << " " << v3 << "//" << v3 << "\n";
|
||||
|
||||
if (has_normals && has_uvs) {
|
||||
obj_stream << "f " << v1 << "/" << v1 << "/" << v1 << " "
|
||||
<< v2 << "/" << v2 << "/" << v2 << " "
|
||||
<< v3 << "/" << v3 << "/" << v3 << "\n";
|
||||
} else if (has_normals) {
|
||||
obj_stream << "f " << v1 << "//" << v1 << " "
|
||||
<< v2 << "//" << v2 << " "
|
||||
<< v3 << "//" << v3 << "\n";
|
||||
} else {
|
||||
obj_stream << "f " << v1 << " " << v2 << " " << v3 << "\n";
|
||||
}
|
||||
|
||||
}
|
||||
vcount_total += vcount;
|
||||
|
||||
std::set<int> faces_set (mesh.faces().begin(), mesh.faces().end());
|
||||
const std::vector<int>& edges = mesh.edges();
|
||||
|
||||
for ( std::vector<int>::const_iterator it = edges.begin(); it != edges.end(); ) {
|
||||
const int i1 = *(it++);
|
||||
const int i2 = *(it++);
|
||||
|
||||
if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int material_id = *(material_it++);
|
||||
|
||||
if (material_id != previous_material_id) {
|
||||
const IfcGeom::Material& material = mesh.materials()[material_id];
|
||||
std::string material_name = (settings().get(SerializerSettings::USE_MATERIAL_NAMES)
|
||||
? material.original_name() : material.name());
|
||||
IfcUtil::sanitate_material_name(material_name);
|
||||
obj_stream << "usemtl " << material_name << "\n";
|
||||
if (materials.find(material_name) == materials.end()) {
|
||||
writeMaterial(material);
|
||||
materials.insert(material_name);
|
||||
}
|
||||
previous_material_id = material_id;
|
||||
}
|
||||
|
||||
const int v1 = i1 + vcount_total;
|
||||
const int v2 = i2 + vcount_total;
|
||||
|
||||
obj_stream << "l " << v1 << " " << v2 << "\n";
|
||||
}
|
||||
|
||||
vcount_total += vcount;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "../ifcconvert/GeometrySerializer.h"
|
||||
|
||||
// http://people.sc.fsu.edu/~jburkardt/txt/obj_format.txt
|
||||
class WaveFrontOBJSerializer : public GeometrySerializer {
|
||||
private:
|
||||
const std::string mtl_filename;
|
||||
@@ -34,23 +35,17 @@ private:
|
||||
unsigned int vcount_total;
|
||||
std::set<std::string> materials;
|
||||
public:
|
||||
WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename)
|
||||
: GeometrySerializer()
|
||||
, obj_stream(obj_filename.c_str())
|
||||
, mtl_filename(mtl_filename)
|
||||
, mtl_stream(mtl_filename.c_str())
|
||||
, vcount_total(1)
|
||||
{}
|
||||
WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const SerializerSettings& settings);
|
||||
virtual ~WaveFrontOBJSerializer() {}
|
||||
bool ready();
|
||||
void writeHeader();
|
||||
void writeMaterial(const IfcGeom::Material& style);
|
||||
void write(const IfcGeom::TriangulationElement<double>* o);
|
||||
void write(const IfcGeom::BRepElement<double>* o) {}
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
|
||||
void finalize() {}
|
||||
bool isTesselated() const { return true; }
|
||||
void setUnitNameAndMagnitude(const std::string& name, float magnitude) {}
|
||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
||||
void setFile(IfcParse::IfcFile*) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,21 +1,69 @@
|
||||
#include <map>
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "XmlSerializer.h"
|
||||
|
||||
#include "../ifcparse/IfcSIPrefix.h"
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
#include "../ifcparse/utils.h"
|
||||
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
using namespace IfcSchema;
|
||||
|
||||
static std::map<std::string, std::string> argument_name_map;
|
||||
namespace {
|
||||
|
||||
std::map<std::string, std::string> argument_name_map;
|
||||
|
||||
// Format an IFC attribute and maybe returns as string. Only literal scalar
|
||||
// values are converted. Things like entity instances and lists are omitted.
|
||||
boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type) {
|
||||
boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) {
|
||||
boost::optional<std::string> value;
|
||||
|
||||
// Hard-code lat-lon as it represents an array
|
||||
// of integers best emitted as a single decimal
|
||||
if (argument_name == "IfcSite.RefLatitude" ||
|
||||
argument_name == "IfcSite.RefLongitude")
|
||||
{
|
||||
std::vector<int> angle = *argument;
|
||||
double deg;
|
||||
if (angle.size() >= 3) {
|
||||
deg = angle[0] + angle[1] / 60. + angle[2] / 3600.;
|
||||
int prec = 8;
|
||||
if (angle.size() == 4) {
|
||||
deg += angle[3] / (1000000. * 3600.);
|
||||
prec = 14;
|
||||
}
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(prec) << deg;
|
||||
value = stream.str();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
switch(argument_type) {
|
||||
case IfcUtil::Argument_BOOL: {
|
||||
const bool b = *argument;
|
||||
@@ -37,11 +85,11 @@ boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil:
|
||||
stream << v;
|
||||
value = stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_ENTITY: {
|
||||
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
||||
IfcUtil::IfcBaseClass* e = *argument;
|
||||
if (Type::IsSimple(e->type())) {
|
||||
IfcUtil::IfcBaseType* f = (IfcUtil::IfcBaseType*) e;
|
||||
value = format_attribute(f->getArgument(0), f->getArgumentType(0));
|
||||
value = format_attribute(f->getArgument(0), f->getArgumentType(0), argument_name);
|
||||
} else if (e->is(IfcSchema::Type::IfcSIUnit) || e->is(IfcSchema::Type::IfcConversionBasedUnit)) {
|
||||
// Some string concatenation to have a unit name as a XML attribute.
|
||||
|
||||
@@ -50,29 +98,49 @@ boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil:
|
||||
if (e->is(IfcSchema::Type::IfcSIUnit)) {
|
||||
IfcSchema::IfcSIUnit* unit = (IfcSchema::IfcSIUnit*) e;
|
||||
unit_name = IfcSchema::IfcSIUnitName::ToString(unit->Name());
|
||||
if (unit->Prefix()) {
|
||||
unit_name = IfcSchema::IfcSIPrefix::ToString(*unit->Prefix()) + unit_name;
|
||||
if (unit->hasPrefix()) {
|
||||
unit_name = IfcSchema::IfcSIPrefix::ToString(unit->Prefix()) + unit_name;
|
||||
}
|
||||
} else {
|
||||
IfcSchema::IfcConversionBasedUnit* unit = (IfcSchema::IfcConversionBasedUnit*) e;
|
||||
unit_name = unit->Name();
|
||||
}
|
||||
|
||||
for (std::string::iterator c = unit_name.begin(); c != unit_name.end(); ++c) *c = tolower(*c);
|
||||
|
||||
value = unit_name;
|
||||
} else if (e->is(IfcSchema::Type::IfcLocalPlacement)) {
|
||||
IfcSchema::IfcLocalPlacement* placement = e->as<IfcSchema::IfcLocalPlacement>();
|
||||
gp_Trsf trsf;
|
||||
IfcGeom::Kernel kernel;
|
||||
|
||||
if (kernel.convert(placement, trsf)) {
|
||||
std::stringstream stream;
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
for (int j = 1; j < 4; ++j) {
|
||||
const double trsf_value = trsf.Value(j, i);
|
||||
stream << trsf_value << " ";
|
||||
}
|
||||
stream << ((i == 4) ? "1" : "0 ");
|
||||
}
|
||||
value = stream.str();
|
||||
}
|
||||
}
|
||||
break; }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
|
||||
// over the entity attributes and writes them as xml attributes of the node.
|
||||
ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) {
|
||||
ptree child;
|
||||
// Appends to a node with possibly existing attributes
|
||||
ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& child, ptree& tree, bool as_link = false) {
|
||||
const unsigned n = instance->getArgumentCount();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
try {
|
||||
instance->getArgument(i);
|
||||
} catch (const std::exception&) {
|
||||
Logger::Error("Expected " + boost::lexical_cast<std::string>(n) + " attributes for:", instance->entity);
|
||||
break;
|
||||
}
|
||||
const Argument* argument = instance->getArgument(i);
|
||||
if (argument->isNull()) continue;
|
||||
|
||||
@@ -84,10 +152,13 @@ ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, boo
|
||||
}
|
||||
const IfcUtil::ArgumentType argument_type = instance->getArgumentType(i);
|
||||
|
||||
const std::string qualified_name = IfcSchema::Type::ToString(instance->type()) + "." + argument_name;
|
||||
boost::optional<std::string> value;
|
||||
try {
|
||||
value = format_attribute(argument, argument_type);
|
||||
} catch (...) {}
|
||||
value = format_attribute(argument, argument_type, qualified_name);
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (as_link) {
|
||||
@@ -104,11 +175,26 @@ ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, boo
|
||||
return tree.add_child(Type::ToString(instance->type()), child);
|
||||
}
|
||||
|
||||
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
|
||||
// over the entity attributes and writes them as xml attributes of the node.
|
||||
ptree& format_entity_instance(IfcUtil::IfcBaseEntity* instance, ptree& tree, bool as_link = false) {
|
||||
ptree child;
|
||||
return format_entity_instance(instance, child, tree, as_link);
|
||||
}
|
||||
|
||||
std::string qualify_unrooted_instance(IfcUtil::IfcBaseClass* inst) {
|
||||
return IfcSchema::Type::ToString(inst->type()) + "_" + boost::lexical_cast<std::string>(inst->entity->id());
|
||||
}
|
||||
|
||||
// A function to be called recursively. Template specialization is used
|
||||
// to descend into decomposition, containment and property relationships.
|
||||
template <typename A>
|
||||
void descend(A* instance, ptree& tree) {
|
||||
format_entity_instance(instance, tree);
|
||||
ptree& descend(A* instance, ptree& tree) {
|
||||
if (instance->is(IfcSchema::Type::IfcObjectDefinition)) {
|
||||
return descend(instance->template as<IfcSchema::IfcObjectDefinition>(), tree);
|
||||
} else {
|
||||
return format_entity_instance(instance, tree);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns related entity instances using IFC's objectified relationship
|
||||
@@ -119,76 +205,110 @@ typename V::list::ptr get_related(T* t, F f, G g) {
|
||||
typename V::list::ptr acc(new typename V::list);
|
||||
for (typename U::list::it it = li->begin(); it != li->end(); ++it) {
|
||||
U* u = *it;
|
||||
acc->push((*u.*g)());
|
||||
acc->push((*u.*g)()->template as<V>());
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Member functions for IFC attributes have identical names for getters and setters. Hence a full member function signature is needed to identify them.
|
||||
typedef IfcTemplatedEntityList<IfcProduct>::ptr (IfcRelContainedInSpatialStructure::*get_related_elements) (void) const;
|
||||
typedef IfcTemplatedEntityList<IfcObjectDefinition>::ptr (IfcRelDecomposes::*get_related_objects) (void) const;
|
||||
typedef IfcPropertySetDefinition* (IfcRelDefinesByProperties::*get_related_properties) (void) const;
|
||||
|
||||
// Descends into the tree by recursing into IfcRelContainedInSpatialStructure,
|
||||
// IfcRelDecomposes and IfcRelDefinesByProperties relations.
|
||||
// IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations.
|
||||
template <>
|
||||
void descend(IfcProduct* product, ptree& tree) {
|
||||
ptree& descend(IfcObjectDefinition* product, ptree& tree) {
|
||||
ptree& child = format_entity_instance(product, tree);
|
||||
|
||||
if (product->is(Type::IfcSpatialStructureElement)) {
|
||||
IfcSpatialStructureElement* structure = (IfcSpatialStructureElement*) product;
|
||||
|
||||
IfcProduct::list::ptr elements = get_related
|
||||
<IfcSpatialStructureElement, IfcRelContainedInSpatialStructure, IfcProduct>
|
||||
(structure, &IfcSpatialStructureElement::ContainsElements, static_cast<get_related_elements>(&IfcRelContainedInSpatialStructure::RelatedElements));
|
||||
IfcObjectDefinition::list::ptr elements = get_related
|
||||
<IfcSpatialStructureElement, IfcRelContainedInSpatialStructure, IfcObjectDefinition>
|
||||
(structure, &IfcSpatialStructureElement::ContainsElements, &IfcRelContainedInSpatialStructure::RelatedElements);
|
||||
|
||||
for (IfcProduct::list::it it = elements->begin(); it != elements->end(); ++it) {
|
||||
for (IfcObjectDefinition::list::it it = elements->begin(); it != elements->end(); ++it) {
|
||||
descend(*it, child);
|
||||
}
|
||||
}
|
||||
|
||||
if (product->is(Type::IfcElement)) {
|
||||
IfcElement* element = static_cast<IfcElement*>(product);
|
||||
IfcOpeningElement::list::ptr openings = get_related<IfcElement, IfcRelVoidsElement, IfcOpeningElement>(
|
||||
element, &IfcElement::HasOpenings, &IfcRelVoidsElement::RelatedOpeningElement);
|
||||
|
||||
for (IfcOpeningElement::list::it it = openings->begin(); it != openings->end(); ++it) {
|
||||
descend(*it, child);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef USE_IFC4
|
||||
IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcProduct, IfcRelDecomposes, IfcObjectDefinition>
|
||||
(product, &IfcProduct::IsDecomposedBy, static_cast<get_related_objects>(&IfcRelDecomposes::RelatedObjects));
|
||||
<IfcObjectDefinition, IfcRelDecomposes, IfcObjectDefinition>
|
||||
(product, &IfcObjectDefinition::IsDecomposedBy, &IfcRelDecomposes::RelatedObjects);
|
||||
#else
|
||||
IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcObjectDefinition, IfcRelAggregates, IfcObjectDefinition>
|
||||
(product, &IfcProduct::IsDecomposedBy, &IfcRelAggregates::RelatedObjects);
|
||||
#endif
|
||||
|
||||
for (IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) {
|
||||
IfcObjectDefinition* ob = *it;
|
||||
if (ob->is(Type::IfcSpatialStructureElement)) {
|
||||
descend((IfcProduct*)ob, child);
|
||||
} else {
|
||||
descend(ob, child);
|
||||
descend(ob, child);
|
||||
}
|
||||
|
||||
if (product->is(IfcSchema::Type::IfcObject)) {
|
||||
IfcSchema::IfcObject* object = product->as<IfcSchema::IfcObject>();
|
||||
|
||||
IfcPropertySetDefinition::list::ptr property_sets = get_related
|
||||
<IfcObject, IfcRelDefinesByProperties, IfcPropertySetDefinition>
|
||||
(object, &IfcObject::IsDefinedBy, &IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
||||
|
||||
for (IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) {
|
||||
IfcPropertySetDefinition* pset = *it;
|
||||
if (pset->is(Type::IfcPropertySet)) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
if (pset->is(Type::IfcElementQuantity)) {
|
||||
format_entity_instance(pset, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
IfcTypeObject::list::ptr types = get_related
|
||||
<IfcObject, IfcRelDefinesByType, IfcTypeObject>
|
||||
(object, &IfcObject::IsTypedBy, &IfcRelDefinesByType::RelatingType);
|
||||
#else
|
||||
IfcTypeObject::list::ptr types = get_related
|
||||
<IfcObject, IfcRelDefinesByType, IfcTypeObject>
|
||||
(object, &IfcObject::IsDefinedBy, &IfcRelDefinesByType::RelatingType);
|
||||
#endif
|
||||
|
||||
for (IfcTypeObject::list::it it = types->begin(); it != types->end(); ++it) {
|
||||
IfcTypeObject* type = *it;
|
||||
format_entity_instance(type, child, true);
|
||||
}
|
||||
}
|
||||
|
||||
IfcPropertySetDefinition::list::ptr property_sets = get_related
|
||||
<IfcProduct, IfcRelDefinesByProperties, IfcPropertySetDefinition>
|
||||
(product, &IfcProduct::IsDefinedBy, static_cast<get_related_properties>(&IfcRelDefinesByProperties::RelatingPropertyDefinition));
|
||||
|
||||
for (IfcPropertySetDefinition::list::it it = property_sets->begin(); it != property_sets->end(); ++it) {
|
||||
IfcPropertySetDefinition* pset = *it;
|
||||
if (pset->is(Type::IfcPropertySet)) {
|
||||
format_entity_instance(pset, child, true);
|
||||
if (product->is(Type::IfcProduct)) {
|
||||
std::map<std::string, IfcPresentationLayerAssignment*> layers = IfcGeom::Kernel::get_layers(product->as<IfcProduct>());
|
||||
for (std::map<std::string, IfcPresentationLayerAssignment*>::const_iterator it = layers.begin(); it != layers.end(); ++it) {
|
||||
// IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID.
|
||||
// Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true
|
||||
// for the format_entity_instance() call.
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + it->first);
|
||||
format_entity_instance(it->second, node, child, true);
|
||||
}
|
||||
|
||||
IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
for (IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) {
|
||||
if ((*it)->as<IfcRelAssociatesMaterial>()) {
|
||||
IfcMaterialSelect* mat = (*it)->as<IfcRelAssociatesMaterial>()->RelatingMaterial();
|
||||
ptree node;
|
||||
node.put("<xmlattr>.xlink:href", "#" + qualify_unrooted_instance(mat));
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, child, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Descends into the tree by recursing into IfcRelDecomposes relations.
|
||||
template <>
|
||||
void descend(IfcProject* project, ptree& tree) {
|
||||
ptree& child = format_entity_instance(project, tree);
|
||||
|
||||
IfcObjectDefinition::list::ptr structures = get_related
|
||||
<IfcProject, IfcRelDecomposes, IfcObjectDefinition>
|
||||
(project, &IfcProject::IsDecomposedBy, static_cast<get_related_objects>(&IfcRelDecomposes::RelatedObjects));
|
||||
|
||||
for (IfcObjectDefinition::list::it it = structures->begin(); it != structures->end(); ++it) {
|
||||
IfcObjectDefinition* ob = *it;
|
||||
if (ob->is(Type::IfcSpatialStructureElement)) {
|
||||
descend((IfcProduct*)ob, child);
|
||||
} else {
|
||||
descend(ob, child);
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
// Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out.
|
||||
@@ -204,6 +324,20 @@ void format_properties(IfcProperty::list::ptr properties, ptree& node) {
|
||||
}
|
||||
}
|
||||
|
||||
// Format IfcElementQuantity instances and insert into the DOM.
|
||||
void format_quantities(IfcPhysicalQuantity::list::ptr quantities, ptree& node) {
|
||||
for (IfcPhysicalQuantity::list::it it = quantities->begin(); it != quantities->end(); ++it) {
|
||||
IfcPhysicalQuantity* p = *it;
|
||||
ptree& node2 = format_entity_instance(p, node);
|
||||
if (p->is(Type::IfcPhysicalComplexQuantity)) {
|
||||
IfcPhysicalComplexQuantity* complex = (IfcPhysicalComplexQuantity*)p;
|
||||
format_quantities(complex->HasQuantities(), node2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // ~unnamed namespace
|
||||
|
||||
void XmlSerializer::finalize() {
|
||||
argument_name_map.insert(std::make_pair("GlobalId", "id"));
|
||||
|
||||
@@ -214,7 +348,7 @@ void XmlSerializer::finalize() {
|
||||
}
|
||||
IfcProject* project = *projects->begin();
|
||||
|
||||
ptree root, header, decomposition, properties;
|
||||
ptree root, header, units, decomposition, properties, quantities, types, layers, materials;
|
||||
|
||||
// Write the SPF header as XML nodes.
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_description().description()) {
|
||||
@@ -229,13 +363,55 @@ void XmlSerializer::finalize() {
|
||||
BOOST_FOREACH(const std::string& s, file->header().file_schema().schema_identifiers()) {
|
||||
header.add_child("file_schema.schema_identifiers", ptree(s));
|
||||
}
|
||||
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
|
||||
header.put("file_name.name", file->header().file_name().name());
|
||||
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
|
||||
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
|
||||
header.put("file_name.originating_system", file->header().file_name().originating_system());
|
||||
header.put("file_name.authorization", file->header().file_name().authorization());
|
||||
|
||||
try {
|
||||
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_description implementation_level, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.name", file->header().file_name().name());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name name, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name time_stamp, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name preprocessor_version, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.originating_system", file->header().file_name().originating_system());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name originating_system, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
try {
|
||||
header.put("file_name.authorization", file->header().file_name().authorization());
|
||||
}
|
||||
catch (const IfcParse::IfcException& ex) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get ifc file header file_name authorization, error: '" << ex.what() << "'";
|
||||
Logger::Message(Logger::LOG_ERROR, ss.str());
|
||||
}
|
||||
|
||||
// Descend into the decomposition structure of the IFC file.
|
||||
descend(project, decomposition);
|
||||
|
||||
@@ -246,9 +422,103 @@ void XmlSerializer::finalize() {
|
||||
ptree& node = format_entity_instance(pset, properties);
|
||||
format_properties(pset->HasProperties(), node);
|
||||
}
|
||||
|
||||
// Write all quantities and values as XML nodes.
|
||||
IfcElementQuantity::list::ptr qtosets = file->entitiesByType<IfcElementQuantity>();
|
||||
for (IfcElementQuantity::list::it it = qtosets->begin(); it != qtosets->end(); ++it) {
|
||||
IfcElementQuantity* qto = *it;
|
||||
ptree& node = format_entity_instance(qto, quantities);
|
||||
format_quantities(qto->Quantities(), node);
|
||||
}
|
||||
|
||||
|
||||
// Write all type objects as XML nodes.
|
||||
IfcTypeObject::list::ptr type_objects = file->entitiesByType<IfcTypeObject>();
|
||||
for (IfcTypeObject::list::it it = type_objects->begin(); it != type_objects->end(); ++it) {
|
||||
IfcTypeObject* type_object = *it;
|
||||
ptree& node = descend(type_object, types);
|
||||
// ptree& node = format_entity_instance(type_object, types);
|
||||
|
||||
if (type_object->hasHasPropertySets()) {
|
||||
IfcPropertySetDefinition::list::ptr property_sets = type_object->HasPropertySets();
|
||||
for (IfcPropertySetDefinition::list::it jt = property_sets->begin(); jt != property_sets->end(); ++jt) {
|
||||
IfcPropertySetDefinition* pset = *jt;
|
||||
if (pset->is(Type::IfcPropertySet)) {
|
||||
format_entity_instance(pset, node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all assigned units as XML nodes.
|
||||
IfcEntityList::ptr unit_assignments = project->UnitsInContext()->Units();
|
||||
for (IfcEntityList::it it = unit_assignments->begin(); it != unit_assignments->end(); ++it) {
|
||||
if ((*it)->is(IfcSchema::Type::IfcNamedUnit)) {
|
||||
IfcSchema::IfcNamedUnit* named_unit = (*it)->as<IfcSchema::IfcNamedUnit>();
|
||||
ptree& node = format_entity_instance(named_unit, units);
|
||||
node.put("<xmlattr>.SI_equivalent", IfcParse::get_SI_equivalent(named_unit));
|
||||
} else if ((*it)->is(IfcSchema::Type::IfcMonetaryUnit)) {
|
||||
format_entity_instance((*it)->as<IfcSchema::IfcMonetaryUnit>(), units);
|
||||
}
|
||||
}
|
||||
|
||||
// Layer assignments. IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier)
|
||||
// so use names as the IDs and only insert those with unique names. In case of possible duplicate names/IDs
|
||||
// the first IfcPresentationLayerAssignment occurrence takes precedence.
|
||||
std::set<std::string> layer_names;
|
||||
IfcPresentationLayerAssignment::list::ptr layer_assignments = file->entitiesByType<IfcPresentationLayerAssignment>();
|
||||
for (IfcPresentationLayerAssignment::list::it it = layer_assignments->begin(); it != layer_assignments->end(); ++it) {
|
||||
const std::string& name = (*it)->Name();
|
||||
if (layer_names.find(name) == layer_names.end()) {
|
||||
layer_names.insert(name);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", name);
|
||||
format_entity_instance(*it, node, layers);
|
||||
}
|
||||
}
|
||||
|
||||
IfcRelAssociatesMaterial::list::ptr materal_associations = file->entitiesByType<IfcRelAssociatesMaterial>();
|
||||
std::set<IfcMaterialSelect*> emitted_materials;
|
||||
for (IfcRelAssociatesMaterial::list::it it = materal_associations->begin(); it != materal_associations->end(); ++it) {
|
||||
IfcMaterialSelect* mat = (**it).RelatingMaterial();
|
||||
if (emitted_materials.find(mat) == emitted_materials.end()) {
|
||||
emitted_materials.insert(mat);
|
||||
ptree node;
|
||||
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
|
||||
if (mat->as<IfcMaterialLayerSetUsage>() || mat->as<IfcMaterialLayerSet>()) {
|
||||
IfcMaterialLayerSet* layerset = mat->as<IfcMaterialLayerSet>();
|
||||
if (!layerset) {
|
||||
layerset = mat->as<IfcMaterialLayerSetUsage>()->ForLayerSet();
|
||||
}
|
||||
if (layerset->hasLayerSetName()) {
|
||||
node.put("<xmlattr>.LayerSetName", layerset->LayerSetName());
|
||||
}
|
||||
IfcMaterialLayer::list::ptr ls = layerset->MaterialLayers();
|
||||
for (IfcMaterialLayer::list::it jt = ls->begin(); jt != ls->end(); ++jt) {
|
||||
ptree subnode;
|
||||
if ((*jt)->hasMaterial()) {
|
||||
subnode.put("<xmlattr>.Name", (*jt)->Material()->Name());
|
||||
}
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
} else if (mat->as<IfcMaterialList>()) {
|
||||
IfcMaterial::list::ptr mats = mat->as<IfcMaterialList>()->Materials();
|
||||
for (IfcMaterial::list::it jt = mats->begin(); jt != mats->end(); ++jt) {
|
||||
ptree subnode;
|
||||
format_entity_instance(*jt, subnode, node);
|
||||
}
|
||||
}
|
||||
format_entity_instance((IfcUtil::IfcBaseEntity*) mat, node, materials);
|
||||
}
|
||||
}
|
||||
|
||||
root.add_child("ifc.header", header);
|
||||
root.add_child("ifc.units", units);
|
||||
root.add_child("ifc.properties", properties);
|
||||
root.add_child("ifc.quantities", quantities);
|
||||
root.add_child("ifc.types", types);
|
||||
root.add_child("ifc.layers", layers);
|
||||
root.add_child("ifc.materials", materials);
|
||||
root.add_child("ifc.decomposition", decomposition);
|
||||
|
||||
root.put("ifc.<xmlattr>.xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
@@ -258,5 +528,7 @@ void XmlSerializer::finalize() {
|
||||
#else
|
||||
boost::property_tree::xml_writer_settings<char> settings('\t', 1);
|
||||
#endif
|
||||
boost::property_tree::write_xml(xml_filename, root, std::locale(), settings);
|
||||
}
|
||||
|
||||
std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str());
|
||||
boost::property_tree::write_xml(f, root, settings);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
* IfcOpenShell is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the Lesser GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3.0 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* IfcOpenShell is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* Lesser GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the Lesser GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
#include "../ifcconvert/util.h"
|
||||
|
||||
using namespace util;
|
||||
|
||||
boost::shared_ptr<string_buffer::string_item> string_buffer::add(const std::string& s) {
|
||||
boost::shared_ptr<string_item> i = boost::shared_ptr<string_item>(new string_item(s));
|
||||
items.push_back(i);
|
||||
return i;
|
||||
}
|
||||
boost::shared_ptr<string_buffer::float_item> string_buffer::add(const double& d) {
|
||||
boost::shared_ptr<float_item> i = boost::shared_ptr<float_item>(new float_item(d));
|
||||
items.push_back(i);
|
||||
return i;
|
||||
}
|
||||
std::string string_buffer::str() const {
|
||||
std::stringstream ss;
|
||||
for (std::vector< boost::shared_ptr<item> >::const_iterator it = items.begin(); it != items.end(); ++it) {
|
||||
ss << (**it).str();
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFCCONVERT_UTIL_H
|
||||
#define IFCCONVERT_UTIL_H
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace util {
|
||||
class string_buffer {
|
||||
public:
|
||||
class item {
|
||||
public:
|
||||
virtual std::string str() const = 0;
|
||||
virtual ~item() {};
|
||||
};
|
||||
class string_item : public item {
|
||||
std::string s;
|
||||
public:
|
||||
string_item(const std::string& s) : s(s) {}
|
||||
void assign(const std::string& s) { this->s = s; }
|
||||
const std::string& value() const { return s; }
|
||||
std::string& value() { return s; }
|
||||
std::string str() const { return s; }
|
||||
virtual ~string_item() {};
|
||||
};
|
||||
class float_item : public item {
|
||||
double d;
|
||||
public:
|
||||
float_item(const double& d) : d(d) {}
|
||||
void assign(const double& d) { this->d = d; }
|
||||
const double& value() const { return d; }
|
||||
double& value() { return d; }
|
||||
std::string str() const { std::stringstream ss; ss << d; return ss.str(); }
|
||||
virtual ~float_item() {};
|
||||
};
|
||||
private:
|
||||
std::vector< boost::shared_ptr<item> > items;
|
||||
void clear();
|
||||
void assign(const std::vector< boost::shared_ptr<item> >& other);
|
||||
public:
|
||||
boost::shared_ptr<string_item> add(const std::string& s);
|
||||
boost::shared_ptr<float_item> add(const double& d);
|
||||
std::string str() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,8 @@ the IFC schema and will most likely fail on any other Express schema.
|
||||
|
||||
The code can be invoked in the following way and results in two header files
|
||||
and a single implementation file named according to the schema name in the
|
||||
Express file. A python 3 interpreter with the pyparsing library is required.
|
||||
Express file. A python 3 interpreter with the pyparsing [1] library is required.
|
||||
|
||||
$ python bootstrap.py express.bnf > express_parser.py && python express_parser.py IFC2X3_TC1.exp
|
||||
|
||||
[1] http://pyparsing.wikispaces.com/Download+and+Installation
|
||||
|
||||
@@ -19,8 +19,14 @@
|
||||
|
||||
import sys
|
||||
import string
|
||||
import operator
|
||||
import itertools
|
||||
|
||||
from pyparsing import *
|
||||
|
||||
try: from functools import reduce
|
||||
except: pass
|
||||
|
||||
class Expression:
|
||||
def __init__(self, contents):
|
||||
self.contents = contents[0]
|
||||
@@ -56,12 +62,12 @@ class Keyword:
|
||||
class Terminal:
|
||||
def __init__(self, contents):
|
||||
self.contents = contents[0]
|
||||
def __repr__(self):
|
||||
s = self.contents
|
||||
is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
|
||||
self.is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
|
||||
all(c in alphanums+"_" for c in s[1:-1])
|
||||
ty = "CaselessKeyword" if is_keyword else "CaselessLiteral"
|
||||
return "%s(%s)" % (ty, s)
|
||||
def __repr__(self):
|
||||
ty = "CaselessKeyword" if self.is_keyword else "CaselessLiteral"
|
||||
return "%s(%s)" % (ty, self.contents)
|
||||
|
||||
|
||||
LPAREN = Suppress("(")
|
||||
@@ -94,16 +100,16 @@ grammar.ignore(HASH + restOfLine)
|
||||
|
||||
express = grammar.parseFile(sys.argv[1])
|
||||
|
||||
def find_keywords(expr, li = None):
|
||||
def find_bytype(expr, ty, li = None):
|
||||
if li is None: li = []
|
||||
if isinstance(expr, Term):
|
||||
expr = expr.contents
|
||||
if isinstance(expr, Keyword):
|
||||
li.append(repr(expr))
|
||||
return li
|
||||
if isinstance(expr, ty):
|
||||
li.append(expr)
|
||||
return set(li)
|
||||
elif isinstance(expr, Expression):
|
||||
for term in expr:
|
||||
find_keywords(term, li)
|
||||
find_bytype(term, ty, li)
|
||||
return set(li)
|
||||
|
||||
actions = {
|
||||
@@ -122,6 +128,8 @@ actions = {
|
||||
'inverse_attr' : "lambda t: InverseAttribute(t)",
|
||||
'bound_spec' : "lambda t: BoundSpecification(t)",
|
||||
'explicit_attr' : "lambda t: ExplicitAttribute(t)",
|
||||
'width_spec' : "lambda t: WidthSpec(t)",
|
||||
'string_type' : "lambda t: StringType(t)",
|
||||
}
|
||||
|
||||
to_emit = set(id for id, expr in express)
|
||||
@@ -129,18 +137,22 @@ emitted = set()
|
||||
to_combine = set(["simple_id"])
|
||||
to_ignore = set(["where_clause", "supertype_constraint", "unique_clause"])
|
||||
statements = []
|
||||
|
||||
terminals = reduce(lambda x,y: x | y, (find_bytype(e, Terminal) for id, e in express))
|
||||
keywords = list(filter(operator.attrgetter('is_keyword'), terminals))
|
||||
negated_keywords = map(lambda s: "~%s" % s, keywords)
|
||||
|
||||
while True:
|
||||
emitted_in_loop = set()
|
||||
for id, expr in express:
|
||||
kws = find_keywords(expr)
|
||||
kws = map(repr, find_bytype(expr, Keyword))
|
||||
found = [k in emitted for k in kws]
|
||||
if id in to_emit and all(found):
|
||||
emitted_in_loop.add(id)
|
||||
emitted.add(id)
|
||||
stmt = "(%s)" % expr
|
||||
if id in to_combine:
|
||||
stmt = "originalTextFor(Combine%s)" % stmt
|
||||
stmt = " + ".join(itertools.chain(negated_keywords, ("originalTextFor(Combine%s)" % stmt,)))
|
||||
if id in actions:
|
||||
stmt = "%s.setParseAction(%s)" % (stmt, actions[id])
|
||||
statements.append("%s = %s" % (id, stmt))
|
||||
@@ -173,6 +185,7 @@ import implementation
|
||||
import latebound_header
|
||||
import latebound_implementation
|
||||
|
||||
syntax.ignore("--" + restOfLine)
|
||||
syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))"))
|
||||
ast = syntax.parseFile(sys.argv[1])
|
||||
schema = schema.Schema(ast)
|
||||
@@ -183,4 +196,6 @@ enum_header.EnumHeader(mapping).emit()
|
||||
implementation.Implementation(mapping).emit()
|
||||
latebound_header.LateBoundHeader(mapping).emit()
|
||||
latebound_implementation.LateBoundImplementation(mapping).emit()
|
||||
|
||||
sys.stdout.write(schema.name)
|
||||
"""%('\n'.join(statements)))
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
class Base(object):
|
||||
"""
|
||||
A base class for all code generation classes. Currently only working around
|
||||
some python 2/3 incompatibilities in terms of unicode file handling.
|
||||
"""
|
||||
def emit(self):
|
||||
import platform
|
||||
if tuple(map(int, platform.python_version_tuple())) < (2, 8):
|
||||
from io import open as unicode_open
|
||||
unicode_type = unicode
|
||||
else:
|
||||
unicode_open = open
|
||||
unicode_type = lambda x, *args, **kwargs: x
|
||||
f = unicode_open(self.file_name, 'w', encoding='utf-8')
|
||||
f.write(unicode_type(repr(self), encoding='utf-8', errors='ignore'))
|
||||
f.close()
|
||||
@@ -27,18 +27,25 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import re,csv
|
||||
import re
|
||||
import os
|
||||
import csv
|
||||
|
||||
from schema import OrderedCaseInsensitiveDict
|
||||
|
||||
try: from html.entities import entitydefs
|
||||
except: from htmlentitydefs import entitydefs
|
||||
|
||||
name_to_oid = {}
|
||||
make_absolute = lambda fn: os.path.join(os.path.dirname(os.path.realpath(__file__)), fn)
|
||||
|
||||
name_to_oid = OrderedCaseInsensitiveDict()
|
||||
oid_to_desc = {}
|
||||
oid_to_name = {}
|
||||
oid_to_pid = {}
|
||||
regices = list(zip([re.compile(s,re.M) for s in [r'<[\w\n=" \-/\.;_\t:%#,\?\(\)]+>',r'(\n[\t ]*){2,}',r'^[\t ]+']],['','\n\n',' ']))
|
||||
|
||||
definition_files = ['DocEntity.csv', 'DocEnumeration.csv', 'DocDefined.csv', 'DocSelect.csv']
|
||||
definition_files = map(make_absolute, definition_files)
|
||||
for fn in definition_files:
|
||||
with open(fn) as f:
|
||||
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
@@ -46,15 +53,15 @@ for fn in definition_files:
|
||||
oid_to_name[oid] = name
|
||||
oid_to_desc[oid] = desc
|
||||
|
||||
with open('DocEntityAttributes.csv') as f:
|
||||
with open(make_absolute('DocEntityAttributes.csv')) as f:
|
||||
for pid, x, oid in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
oid_to_pid[oid] = pid
|
||||
|
||||
with open('DocAttribute.csv') as f:
|
||||
with open(make_absolute('DocAttribute.csv')) as f:
|
||||
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
pid = oid_to_pid[oid]
|
||||
pname = oid_to_name[pid]
|
||||
name_to_oid[(pname, name)] = oid
|
||||
name_to_oid[".".join((pname, name))] = oid
|
||||
oid_to_desc[oid] = desc
|
||||
|
||||
def description(item):
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
###############################################################################
|
||||
|
||||
import templates
|
||||
import codegen
|
||||
|
||||
class EnumHeader:
|
||||
class EnumHeader(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
enumerable_types = sorted(set([name for name, type in mapping.schema.types.items()] + [name for name, type in mapping.schema.entities.items()]))
|
||||
|
||||
@@ -30,9 +31,9 @@ class EnumHeader:
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%senum.h'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
def emit(self):
|
||||
f = open('%senum.h'%self.schema_name, 'w', encoding='utf-8')
|
||||
f.write(str(self))
|
||||
f.close()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Taken from http://sourceforge.net/p/exp-engine/expresso/ci/master/tree/docs/iso-10303-11--2004.bnf
|
||||
|
||||
ABS = "abs" .
|
||||
ABSTRACT = "abstract" .
|
||||
ACOS = "acos" .
|
||||
@@ -202,7 +200,7 @@ constructed_types = enumeration_type | select_type .
|
||||
declaration = entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl .
|
||||
derived_attr = attribute_decl ":" parameter_type ":=" expression ";" .
|
||||
derive_clause = DERIVE derived_attr { derived_attr } .
|
||||
domain_rule = rule_label_id ":" expression .
|
||||
domain_rule = [ rule_label_id ":" ] expression .
|
||||
element = expression [ ":" repetition ] .
|
||||
entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] .
|
||||
entity_constructor = entity_ref "(" [ expression { "," expression } ] ")" .
|
||||
@@ -334,7 +332,7 @@ type_label_id = simple_id .
|
||||
unary_op = "+" | "-" | NOT .
|
||||
underlying_type = constructed_types | concrete_types .
|
||||
unique_clause = UNIQUE unique_rule ";" { unique_rule ";" } .
|
||||
unique_rule = rule_label_id ":" referenced_attribute { "," referenced_attribute } .
|
||||
unique_rule = [ rule_label_id ":" ] referenced_attribute { "," referenced_attribute } .
|
||||
until_control = UNTIL logical_expression .
|
||||
use_clause = USE FROM schema_ref [ "(" named_type_or_rename { "," named_type_or_rename } ")" ] ";" .
|
||||
variable_id = simple_id .
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import operator
|
||||
|
||||
import codegen
|
||||
import templates
|
||||
import documentation
|
||||
|
||||
class Header:
|
||||
class Header(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
declarations = []
|
||||
|
||||
@@ -40,15 +43,18 @@ class Header:
|
||||
emitted_simpletypes = set()
|
||||
while len(emitted_simpletypes) < len(mapping.schema.simpletypes):
|
||||
for name, type in mapping.schema.simpletypes.items():
|
||||
if name in emitted_simpletypes: continue
|
||||
if name.lower() in emitted_simpletypes: continue
|
||||
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
|
||||
attr_type = mapping.make_argument_type(type)
|
||||
superclass = mapping.simple_type_parent(name)
|
||||
if superclass is None:
|
||||
superclass = "IfcUtil::IfcBaseType"
|
||||
elif superclass not in emitted_simpletypes:
|
||||
elif superclass.lower() not in emitted_simpletypes:
|
||||
continue
|
||||
emitted_simpletypes.add(name)
|
||||
else:
|
||||
# Case normalize
|
||||
superclass = [k for k in mapping.schema.simpletypes.keys() if k.lower() == superclass.lower()][0]
|
||||
emitted_simpletypes.add(name.lower())
|
||||
write(templates.simpletype, name=name, type=type_str, attr_type=attr_type, superclass=superclass)
|
||||
|
||||
class_definitions = []
|
||||
@@ -59,15 +65,18 @@ class Header:
|
||||
emitted_entities = set()
|
||||
while len(emitted_entities) < len(mapping.schema.entities):
|
||||
for name, type in mapping.schema.entities.items():
|
||||
if name in emitted_entities: continue
|
||||
if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities:
|
||||
if name.lower() in emitted_entities: continue
|
||||
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
|
||||
attr_lines = []
|
||||
def write_method(attr):
|
||||
attr_lines.extend(["/// %s"%d for d in documentation.description((name, attr.name))])
|
||||
type_str = mapping.get_parameter_type(attr, allow_optional=True, allow_entities=True)
|
||||
if attr.optional:
|
||||
attr_lines.append(templates.optional_attribute_description % (attr.name, name))
|
||||
attr_lines.append("bool has%s() const;"%(attr.name))
|
||||
attr_lines.extend(["/// %s"%d for d in documentation.description(".".join((name, attr.name)))])
|
||||
type_str = mapping.get_parameter_type(attr, allow_optional=False, allow_entities=False)
|
||||
if mapping.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN":
|
||||
attr_lines.append("%s %s() const;"%(type_str, attr.name))
|
||||
attr_lines.append("void %s(%s v);"%(attr.name, type_str))
|
||||
attr_lines.append("void set%s(%s v);"%(attr.name, type_str))
|
||||
|
||||
[write_method(attr) for attr in type.attributes]
|
||||
|
||||
@@ -84,7 +93,11 @@ class Header:
|
||||
inverse = "\n".join(["%s%s"%(' '*4, a) for a in inv_lines])
|
||||
if len(inverse): inverse += '\n'
|
||||
|
||||
supertypes = type.supertypes if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
|
||||
def case_norm(n):
|
||||
n = n.lower()
|
||||
return [k for k in mapping.schema.entities.keys() if k.lower() == n][0]
|
||||
|
||||
supertypes = map(case_norm, type.supertypes) if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
|
||||
superclass = ": %s "%(", ".join(["public %s"%c for c in supertypes]))
|
||||
|
||||
argument_count = mapping.argument_count(type)
|
||||
@@ -92,17 +105,23 @@ class Header:
|
||||
argument_start = argument_count - len(type.attributes)
|
||||
|
||||
argument_name_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return "%s"; '%(i+argument_start, attr.name) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else ""
|
||||
argument_name_function_body_tail = (" return %s::getArgumentName(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' throw IfcParse::IfcException("argument out of range"); '
|
||||
argument_name_function_body_tail = (" return %s::getArgumentName(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
|
||||
|
||||
argument_name_function_body = argument_name_function_body_switch_stmt + argument_name_function_body_tail
|
||||
|
||||
argument_type_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return %s; '%(i+argument_start, mapping.make_argument_type(attr)) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else ""
|
||||
argument_type_function_body_tail = (" return %s::getArgumentType(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' throw IfcParse::IfcException("argument out of range"); '
|
||||
|
||||
derived = mapping.derived_in_supertype(type)
|
||||
attribute_names = list(map(operator.attrgetter('name'), mapping.arguments(type)))
|
||||
derived_in_supertype = set(derived) & set(attribute_names)
|
||||
derived_in_supertype_indices = sorted(attribute_names.index(nm) for nm in derived_in_supertype)
|
||||
attribute_type_cases = ['case %d: return IfcUtil::Argument_DERIVED; ' % idx for idx in derived_in_supertype_indices]
|
||||
attribute_type_cases += ['case %d: return %s; '%(i+argument_start, mapping.make_argument_type(attr)) for i, attr in enumerate(type.attributes)]
|
||||
argument_type_function_body_switch_stmt = " switch (i) {%s}"%("".join(attribute_type_cases)) if len(type.attributes) else ""
|
||||
argument_type_function_body_tail = (" return %s::getArgumentType(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
|
||||
|
||||
argument_type_function_body = argument_type_function_body_switch_stmt + argument_type_function_body_tail
|
||||
|
||||
argument_entity_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return %s; '%(i+argument_start, mapping.make_argument_entity(attr)) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else ""
|
||||
argument_entity_function_body_tail = (" return %s::getArgumentEntity(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' throw IfcParse::IfcException("argument out of range"); '
|
||||
argument_entity_function_body_tail = (" return %s::getArgumentEntity(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
|
||||
|
||||
argument_entity_function_body = argument_entity_function_body_switch_stmt + argument_entity_function_body_tail
|
||||
|
||||
@@ -120,10 +139,9 @@ class Header:
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%s.h'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
def emit(self):
|
||||
f = open('%s.h'%self.schema_name, 'w', encoding='utf-8')
|
||||
f.write(str(self))
|
||||
f.close()
|
||||
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import codegen
|
||||
import templates
|
||||
|
||||
class Implementation:
|
||||
from schema import OrderedCaseInsensitiveDict
|
||||
|
||||
class Implementation(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
enumeration_functions = []
|
||||
entity_implementations = []
|
||||
@@ -59,23 +62,25 @@ class Implementation:
|
||||
write_attr = lambda str, **kwargs: attributes.append(str%kwargs)
|
||||
for arg in constructor_arguments:
|
||||
if not arg['is_inherited'] and not arg['is_derived']:
|
||||
|
||||
if arg['is_optional']:
|
||||
write_attr(
|
||||
templates.const_function,
|
||||
class_name = name,
|
||||
name = 'has%s'%arg['name'],
|
||||
arguments = '',
|
||||
return_type = 'bool',
|
||||
body = templates.optional_attr_stmt % {'index':arg['index']-1}
|
||||
)
|
||||
|
||||
def find_template(arg):
|
||||
simple = mapping.schema.is_simpletype(arg['list_instance_type'])
|
||||
select = arg['list_instance_type'] == "IfcUtil::IfcBaseClass"
|
||||
express = arg['list_instance_type'] in mapping.express_to_cpp_typemapping
|
||||
if arg['is_optional']:
|
||||
if arg['is_enum']: return templates.get_attr_stmt_optional_enum
|
||||
elif arg['is_nested']: return templates.get_attr_stmt_optional_nested_array
|
||||
elif arg['is_array'] and not (select or simple or express): return templates.get_attr_stmt_optional_array
|
||||
elif arg['non_optional_type'].endswith('*'): return templates.get_attr_stmt_optional_entity
|
||||
else: return templates.get_attr_stmt_generic
|
||||
else:
|
||||
if arg['is_enum']: return templates.get_attr_stmt_enum
|
||||
elif arg['is_nested']: return templates.get_attr_stmt_nested_array
|
||||
elif arg['is_array'] and not (select or simple or express): return templates.get_attr_stmt_array
|
||||
elif arg['non_optional_type'].endswith('*'): return templates.get_attr_stmt_entity
|
||||
else: return templates.get_attr_stmt_generic
|
||||
express = mapping.flatten_type_string(arg['list_instance_type']) in mapping.express_to_cpp_typemapping
|
||||
if arg['is_enum']: return templates.get_attr_stmt_enum
|
||||
elif arg['is_nested'] and arg['is_templated_list']: return templates.get_attr_stmt_nested_array
|
||||
elif arg['is_templated_list'] and not (select or simple or express): return templates.get_attr_stmt_array
|
||||
elif arg['non_optional_type'].endswith('*'): return templates.get_attr_stmt_entity
|
||||
else: return templates.get_attr_stmt
|
||||
|
||||
tmpl = find_template(arg)
|
||||
write_attr(
|
||||
@@ -83,7 +88,7 @@ class Implementation:
|
||||
class_name = name,
|
||||
name = arg['name'],
|
||||
arguments = '',
|
||||
return_type = arg['full_type'],
|
||||
return_type = arg['non_optional_type'],
|
||||
body = tmpl % {'index': arg['index']-1,
|
||||
'type' : arg['non_optional_type'].split('::')[0],
|
||||
'list_instance_type' : arg['list_instance_type']}
|
||||
@@ -93,21 +98,16 @@ class Implementation:
|
||||
simple = mapping.schema.is_simpletype(arg['list_instance_type'])
|
||||
select = arg['list_instance_type'] == "IfcUtil::IfcBaseClass"
|
||||
express = arg['list_instance_type'] in mapping.express_to_cpp_typemapping
|
||||
if arg['is_optional']:
|
||||
if arg['is_enum']: return templates.set_attr_stmt_optional_enum
|
||||
elif arg['is_array'] and not (select or simple or express): return templates.set_attr_stmt_optional_array
|
||||
else: return templates.set_attr_stmt_optional_generic
|
||||
else:
|
||||
if arg['is_enum']: return templates.set_attr_stmt_enum
|
||||
elif arg['is_array'] and not (select or simple or express): return templates.set_attr_stmt_array
|
||||
else: return templates.set_attr_stmt_generic
|
||||
if arg['is_enum']: return templates.set_attr_stmt_enum
|
||||
elif arg['is_templated_list'] and not (select or simple or express): return templates.set_attr_stmt_array
|
||||
else: return templates.set_attr_stmt
|
||||
|
||||
tmpl = find_template(arg)
|
||||
write_attr(
|
||||
templates.function,
|
||||
class_name = name,
|
||||
name = arg['name'],
|
||||
arguments = '%s v'%arg['full_type'],
|
||||
name = 'set%s'%arg['name'],
|
||||
arguments = '%s v'%arg['non_optional_type'],
|
||||
return_type = 'void',
|
||||
body = tmpl % {'index': arg['index']-1,
|
||||
'type' : arg['non_optional_type'].split('::')[0]}
|
||||
@@ -116,8 +116,9 @@ class Implementation:
|
||||
if arg['is_derived']:
|
||||
constructor_implementations.append(templates.constructor_stmt_derived % {'index' : arg['index']-1})
|
||||
else:
|
||||
is_optional_non_naked_ptr = arg['is_optional'] and not arg['non_optional_type'].endswith('*')
|
||||
arg_name = "v%(index)d_%(name)s"%arg
|
||||
deref_name = ("*%s"%arg_name) if arg['is_optional'] else arg_name
|
||||
deref_name = ("*%s"%arg_name) if is_optional_non_naked_ptr else arg_name
|
||||
|
||||
tmpl = templates.constructor_stmt_array if arg['is_templated_list'] \
|
||||
else templates.constructor_stmt_enum if arg['is_enum'] \
|
||||
@@ -125,7 +126,7 @@ class Implementation:
|
||||
impl = tmpl % {'name' : deref_name,
|
||||
'index' : arg['index']-1,
|
||||
'type' : arg['non_optional_type'].split('::')[0]}
|
||||
if arg['is_optional']:
|
||||
if is_optional_non_naked_ptr:
|
||||
impl = templates.constructor_stmt_optional%{'name' : arg_name,
|
||||
'index' : arg['index']-1,
|
||||
'stmt' : impl}
|
||||
@@ -133,7 +134,7 @@ class Implementation:
|
||||
|
||||
def get_attribute_index(entity, attr_name):
|
||||
related_entity = mapping.schema.entities[entity]
|
||||
return [a['name'] for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name)
|
||||
return [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name.lower())
|
||||
|
||||
inverse = [templates.const_function % {
|
||||
'class_name' : name,
|
||||
@@ -143,7 +144,7 @@ class Implementation:
|
||||
'body' : templates.get_inverse % {'type': i.entity, 'index':get_attribute_index(i.entity, i.attribute)}
|
||||
} for i in (type.inverse.elements if type.inverse else [])]
|
||||
|
||||
superclass = "%s((IfcAbstractEntity*)0)" % type.supertypes[0] if len(type.supertypes) == 1 else 'IfcUtil::IfcBaseEntity()'
|
||||
superclass = "%s((IfcEntityInstanceData*)0)" % type.supertypes[0] if len(type.supertypes) == 1 else 'IfcUtil::IfcBaseEntity()'
|
||||
|
||||
write(
|
||||
templates.entity_implementation,
|
||||
@@ -156,7 +157,7 @@ class Implementation:
|
||||
superclass = superclass
|
||||
)
|
||||
|
||||
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(mapping.schema.types.keys()))
|
||||
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(map(str, mapping.schema.types.keys())))
|
||||
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.simpletypes.items()]
|
||||
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.entities.items()]
|
||||
|
||||
@@ -168,12 +169,15 @@ class Implementation:
|
||||
'name' : name,
|
||||
'padding' : ' ' * (max_len - len(name))
|
||||
} for name in enumerable_types]
|
||||
|
||||
enumeration_index_by_str = OrderedCaseInsensitiveDict((j,i) for i,j in enumerate(enumerable_types))
|
||||
def get_parent_id(s):
|
||||
e = mapping.schema.entities.get(s)
|
||||
if e and e.supertypes:
|
||||
return enumeration_index_by_str[e.supertypes[0]]
|
||||
else: return -1
|
||||
|
||||
parent_type_statements = [templates.parent_type_stmt % {
|
||||
'name' : name,
|
||||
'parent' : type.supertypes[0],
|
||||
'padding' : ' ' * (max_len - len(name))
|
||||
} for name, type in mapping.schema.entities.items() if type.supertypes and len(type.supertypes) == 1]
|
||||
parent_type_statements = ",".join(map(str, map(get_parent_id, enumerable_types)))
|
||||
|
||||
max_id = len(enumerable_types)
|
||||
|
||||
@@ -191,22 +195,29 @@ class Implementation:
|
||||
constructor = templates.constructor_single_initlist if superclass \
|
||||
else templates.constructor
|
||||
|
||||
simpletype_impl_cast = templates.simpletype_impl_cast_templated if mapping.is_templated_list(type) \
|
||||
else templates.simpletype_impl_cast
|
||||
|
||||
simpletype_impl_constructor = templates.simpletype_impl_constructor_templated if mapping.is_templated_list(type) \
|
||||
else templates.simpletype_impl_constructor
|
||||
|
||||
def compose(params):
|
||||
class_name, attr_type, superclass, superclass_init, name, tmpl, return_type, args, body = params
|
||||
underlying_type = mapping.list_instance_type(type)
|
||||
arguments = ",".join(args)
|
||||
body = body % locals()
|
||||
return tmpl % locals()
|
||||
|
||||
simple_type_impl.append(templates.simpletype_impl_comment % {'name': class_name})
|
||||
simple_type_impl.extend(map(compose, map(lambda x: (class_name, attr_type, superclass, "(IfcAbstractEntity*)0")+x, (
|
||||
('getArgumentType', templates.const_function, 'IfcUtil::ArgumentType', ('unsigned int i',), templates.simpletype_impl_argument_type ),
|
||||
('getArgument', templates.const_function, 'Argument*', ('unsigned int i',), templates.simpletype_impl_argument ),
|
||||
('is', templates.const_function, 'bool', ('Type::Enum v',), simpletype_impl_is ),
|
||||
('type', templates.const_function, 'Type::Enum', (), templates.simpletype_impl_type ),
|
||||
('Class', templates.function, 'Type::Enum', (), templates.simpletype_impl_class ),
|
||||
('', constructor, '', ('IfcAbstractEntity* e',), templates.simpletype_impl_explicit_constructor),
|
||||
('', constructor, '', ("%s v" % type_str,), templates.simpletype_impl_constructor ),
|
||||
('', templates.cast_function, type_str, (), templates.simpletype_impl_cast )
|
||||
simple_type_impl.extend(map(compose, map(lambda x: (class_name, attr_type, superclass, "(IfcEntityInstanceData*)0")+x, (
|
||||
('getArgumentType', templates.const_function, 'IfcUtil::ArgumentType', ('unsigned int i',), templates.simpletype_impl_argument_type ),
|
||||
('getArgument', templates.const_function, 'Argument*', ('unsigned int i',), templates.simpletype_impl_argument ),
|
||||
('is', templates.const_function, 'bool', ('Type::Enum v',), simpletype_impl_is ),
|
||||
('type', templates.const_function, 'Type::Enum', (), templates.simpletype_impl_type ),
|
||||
('Class', templates.function, 'Type::Enum', (), templates.simpletype_impl_class ),
|
||||
('', constructor, '', ('IfcEntityInstanceData* e',), templates.simpletype_impl_explicit_constructor),
|
||||
('', constructor, '', ("%s v" % type_str,), simpletype_impl_constructor ),
|
||||
('', templates.cast_function, type_str, (), simpletype_impl_cast )
|
||||
))))
|
||||
simple_type_impl.append('')
|
||||
|
||||
@@ -219,16 +230,15 @@ class Implementation:
|
||||
'type_name_strings' : type_name_strings,
|
||||
'string_map_statements' : catnl(string_map_statements),
|
||||
'simple_type_statement' : simple_type_statements,
|
||||
'parent_type_statements' : catnl(parent_type_statements),
|
||||
'parent_type_statements' : parent_type_statements,
|
||||
'entity_implementations' : catnl(entity_implementations),
|
||||
'simple_type_impl' : catnl(simple_type_impl)
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%s.cpp'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
def emit(self):
|
||||
f = open('%s.cpp'%self.schema_name, 'w', encoding='utf-8')
|
||||
f.write(str(self))
|
||||
f.close()
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import codegen
|
||||
import templates
|
||||
|
||||
class LateBoundHeader:
|
||||
class LateBoundHeader(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
self.str = templates.lb_header % {
|
||||
'schema_name_upper' : mapping.schema.name.upper(),
|
||||
@@ -27,9 +28,9 @@ class LateBoundHeader:
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%s-latebound.h'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
def emit(self):
|
||||
f = open('%s-latebound.h'%self.schema_name, 'w', encoding='utf-8')
|
||||
f.write(str(self))
|
||||
f.close()
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import codegen
|
||||
import templates
|
||||
|
||||
class LateBoundImplementation:
|
||||
class LateBoundImplementation(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
@@ -40,11 +41,10 @@ class LateBoundImplementation:
|
||||
})
|
||||
|
||||
emitted_entities = set()
|
||||
entities_to_emit = mapping.schema.entities.keys()
|
||||
while len(emitted_entities) < len(mapping.schema.entities):
|
||||
for name, type in mapping.schema.entities.items():
|
||||
if name in emitted_entities: continue
|
||||
if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities:
|
||||
if name.lower() in emitted_entities: continue
|
||||
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
|
||||
constructor_arguments = mapping.get_assignable_arguments(type, include_derived = True)
|
||||
entity_descriptor_attributes = []
|
||||
for arg in constructor_arguments:
|
||||
@@ -60,8 +60,9 @@ class LateBoundImplementation:
|
||||
})
|
||||
|
||||
emitted_entities.add(name)
|
||||
|
||||
parent_statement = '0' if len(type.supertypes) != 1 else templates.entity_descriptor_parent % {
|
||||
'type' : type.supertypes[0]
|
||||
'type' : [k for k in mapping.schema.entities.keys() if k.lower() == type.supertypes[0].lower()][0]
|
||||
}
|
||||
entity_descriptors.append(templates.entity_descriptor % {
|
||||
'type' : name,
|
||||
@@ -91,13 +92,13 @@ class LateBoundImplementation:
|
||||
if type.inverse:
|
||||
for attr in type.inverse.elements:
|
||||
related_entity = mapping.schema.entities[attr.entity]
|
||||
related_attrs = [a['name'] for a in mapping.get_assignable_arguments(related_entity, include_derived=True)]
|
||||
related_attrs = [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)]
|
||||
|
||||
inverse_implementations.append(templates.inverse_implementation % {
|
||||
'type' : name,
|
||||
'name' : attr.name,
|
||||
'related_type' : attr.entity,
|
||||
'index' : related_attrs.index(attr.attribute)
|
||||
'index' : related_attrs.index(attr.attribute.lower())
|
||||
})
|
||||
|
||||
self.str = templates.lb_implementation % {
|
||||
@@ -110,10 +111,9 @@ class LateBoundImplementation:
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%s-latebound.cpp'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
def emit(self):
|
||||
f = open('%s-latebound.cpp'%self.schema_name, 'w', encoding='utf-8')
|
||||
f.write(str(self))
|
||||
f.close()
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import nodes
|
||||
import templates
|
||||
|
||||
@@ -28,8 +31,15 @@ class Mapping:
|
||||
'integer' : 'int',
|
||||
'real' : 'double',
|
||||
'number' : 'double',
|
||||
'string' : 'std::string'
|
||||
'string' : 'std::string',
|
||||
'binary' : 'boost::dynamic_bitset<>'
|
||||
}
|
||||
|
||||
supported_argument_types = set([
|
||||
'INT', 'BOOL', 'DOUBLE', 'STRING', 'BINARY', 'ENUMERATION', 'ENTITY_INSTANCE',
|
||||
'AGGREGATE_OF_INT', 'AGGREGATE_OF_DOUBLE', 'AGGREGATE_OF_STRING', 'AGGREGATE_OF_BINARY', 'AGGREGATE_OF_ENTITY_INSTANCE',
|
||||
'AGGREGATE_OF_AGGREGATE_OF_INT', 'AGGREGATE_OF_AGGREGATE_OF_DOUBLE', 'AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE',
|
||||
])
|
||||
|
||||
def __init__(self, schema):
|
||||
self.schema = schema
|
||||
@@ -44,17 +54,17 @@ class Mapping:
|
||||
def simple_type_parent(self, type):
|
||||
parent = self.schema.types[type].type.type
|
||||
if isinstance(parent, nodes.AggregationType): parent = None
|
||||
return None if parent in self.express_to_cpp_typemapping else parent
|
||||
return None if str(parent) in self.express_to_cpp_typemapping else parent
|
||||
|
||||
def make_type_string(self, type):
|
||||
if isinstance(type, str):
|
||||
return self.express_to_cpp_typemapping.get(type, type)
|
||||
if isinstance(type, (str, nodes.BinaryType, nodes.StringType)):
|
||||
return self.express_to_cpp_typemapping.get(str(type), type)
|
||||
else:
|
||||
is_list = self.schema.is_entity(type.type)
|
||||
is_nested_list = isinstance(type.type, nodes.AggregationType)
|
||||
tmpl = templates.list_list_type if is_nested_list else templates.list_type if is_list else templates.array_type
|
||||
return tmpl % {
|
||||
'instance_type' : self.make_type_string(type.type),
|
||||
'instance_type' : self.make_type_string(self.flatten_type_string(type.type)),
|
||||
'lower' : type.bounds.lower,
|
||||
'upper' : type.bounds.upper,
|
||||
}
|
||||
@@ -70,56 +80,61 @@ class Mapping:
|
||||
def make_argument_entity(self, attr):
|
||||
type = attr.type if hasattr(attr, 'type') else attr
|
||||
while isinstance(type, nodes.AggregationType): type = type.type
|
||||
if type in self.express_to_cpp_typemapping or isinstance(type, nodes.BinaryType): return "Type::UNDEFINED"
|
||||
if str(type) in self.express_to_cpp_typemapping: return "Type::UNDEFINED"
|
||||
else: return "Type::%s" % type
|
||||
|
||||
def make_argument_type(self, attr):
|
||||
def _make_argument_type(type):
|
||||
if type in self.express_to_cpp_typemapping:
|
||||
return self.express_to_cpp_typemapping.get(type, type).split('::')[-1].upper()
|
||||
elif self.schema.is_entity(type):
|
||||
return "ENTITY"
|
||||
elif self.schema.is_type(type):
|
||||
return _make_argument_type(self.schema.types[type].type.type)
|
||||
if self.schema.is_entity(type) or isinstance(type, nodes.SelectType):
|
||||
return "ENTITY_INSTANCE"
|
||||
elif isinstance(type, nodes.BinaryType):
|
||||
return "UNKNOWN"
|
||||
return "BINARY"
|
||||
elif isinstance(type, nodes.StringType):
|
||||
return "STRING"
|
||||
elif isinstance(type, nodes.EnumerationType):
|
||||
return "ENUMERATION"
|
||||
elif isinstance(type, nodes.SelectType):
|
||||
return "ENTITY"
|
||||
elif isinstance(type, nodes.AggregationType):
|
||||
ty = _make_argument_type(type.type)
|
||||
if ty == "UNKNOWN": return "UNKNOWN"
|
||||
return "%s_LIST"%ty if ty.startswith("ENTITY") else ("VECTOR_%s"%ty)
|
||||
else: raise ValueError
|
||||
supported = {'INT', 'BOOL', 'DOUBLE', 'STRING', 'VECTOR_INT', 'VECTOR_DOUBLE', 'VECTOR_STRING', 'ENTITY', 'ENTITY_LIST', 'ENTITY_LIST_LIST', 'ENUMERATION'}
|
||||
return "AGGREGATE_OF_" + ty
|
||||
elif str(type) in self.express_to_cpp_typemapping:
|
||||
return self.express_to_cpp_typemapping.get(str(type), type).split('::')[-1].upper()
|
||||
elif self.schema.is_type(type):
|
||||
return _make_argument_type(self.schema.types[type].type.type)
|
||||
else:
|
||||
raise ValueError("Unable to map type %r for attribute %r" % (type, attr))
|
||||
ty = _make_argument_type(attr.type if hasattr(attr, 'type') else attr)
|
||||
if ty not in supported: ty = 'UNKNOWN'
|
||||
if ty not in self.supported_argument_types:
|
||||
print("Attribute %r mapped as 'unknown'" % (attr), file=sys.stderr)
|
||||
ty = 'UNKNOWN'
|
||||
return "IfcUtil::Argument_%s" % ty
|
||||
|
||||
def get_type_dep(self, type):
|
||||
if isinstance(type, str):
|
||||
return self.express_to_cpp_typemapping.get(type, type)
|
||||
return self.express_to_cpp_typemapping.get(str(type), type)
|
||||
else:
|
||||
return self.get_type_dep(type.type)
|
||||
|
||||
def get_parameter_type(self, attr, allow_optional, allow_entities, allow_pointer = True):
|
||||
|
||||
attr_type = self.flatten_type(attr.type)
|
||||
type_str = self.express_to_cpp_typemapping.get(str(attr_type), attr_type)
|
||||
|
||||
is_ptr = False
|
||||
|
||||
if self.schema.is_enumeration(attr_type):
|
||||
type_str = '%s::%s'%(attr_type, attr_type)
|
||||
elif isinstance(type_str, nodes.AggregationType):
|
||||
is_nested_list = isinstance(attr_type.type, nodes.AggregationType)
|
||||
ty = self.get_parameter_type(attr_type.type if is_nested_list else attr_type, False, allow_entities, False)
|
||||
if True and self.schema.is_select(attr_type.type):
|
||||
if self.schema.is_select(attr_type.type):
|
||||
type_str = templates.untyped_list
|
||||
elif self.schema.is_simpletype(ty) or ty in self.express_to_cpp_typemapping.values():
|
||||
type_str = templates.array_type % {
|
||||
elif self.schema.is_simpletype(ty) or str(ty) in self.express_to_cpp_typemapping.values():
|
||||
tmpl = templates.nested_array_type if is_nested_list else templates.array_type
|
||||
bounds = (attr_type.bounds.lower, attr_type.bounds.upper) if attr_type.bounds else (-1, -1)
|
||||
type_str = tmpl % {
|
||||
'instance_type' : ty,
|
||||
'lower' : attr_type.bounds.lower,
|
||||
'upper' : attr_type.bounds.upper
|
||||
'lower' : bounds[0],
|
||||
'upper' : bounds[1]
|
||||
}
|
||||
else:
|
||||
tmpl = templates.list_list_type if is_nested_list else templates.list_type
|
||||
@@ -128,9 +143,11 @@ class Mapping:
|
||||
}
|
||||
elif allow_pointer and (self.schema.is_entity(type_str) or self.schema.is_select(type_str)):
|
||||
type_str += '*'
|
||||
is_ptr = True
|
||||
elif not allow_pointer and self.schema.is_select(type_str):
|
||||
type_str = "IfcUtil::IfcBaseClass*"
|
||||
if allow_optional and attr.optional:# and not is_ptr:
|
||||
is_ptr = True
|
||||
if allow_optional and attr.optional and not is_ptr:
|
||||
type_str = "boost::optional< %s >"%type_str
|
||||
return type_str
|
||||
|
||||
@@ -147,23 +164,28 @@ class Mapping:
|
||||
return c + ([str(s) for s in t.derive.elements] if t.derive else [])
|
||||
|
||||
def list_instance_type(self, attr):
|
||||
f = lambda v : 'IfcUtil::IfcBaseClass' if self.schema.is_select(v) else v
|
||||
if self.is_array(attr.type):
|
||||
if not isinstance(attr.type, str) and self.is_array(attr.type.type):
|
||||
if isinstance(attr.type.type, str):
|
||||
return f(attr.type.type)
|
||||
else: return f(attr.type.type.type)
|
||||
attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr
|
||||
if isinstance(attr_type, str): return None
|
||||
f = lambda v : 'IfcUtil::IfcBaseClass' if self.schema.is_select(v) else str(v)
|
||||
if self.is_array(attr_type):
|
||||
if not isinstance(attr_type, str) and self.is_array(attr_type.type):
|
||||
if isinstance(attr_type.type, str):
|
||||
return f(attr_type.type)
|
||||
else: return f(attr_type.type.type)
|
||||
else:
|
||||
if isinstance(attr.type, str):
|
||||
return f(attr.type)
|
||||
else: return f(attr.type.type)
|
||||
if isinstance(attr_type, str):
|
||||
return f(attr_type)
|
||||
else: return f(attr_type.type)
|
||||
return None
|
||||
|
||||
def is_templated_list(self, attr):
|
||||
attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr
|
||||
if isinstance(attr, str): return False
|
||||
ty = self.list_instance_type(attr)
|
||||
arr = self.is_array(attr.type)
|
||||
if ty is None: return False
|
||||
arr = self.is_array(attr_type)
|
||||
simple = self.schema.is_simpletype(ty)
|
||||
express = ty in self.express_to_cpp_typemapping
|
||||
express = self.flatten_type_string(ty) in self.express_to_cpp_typemapping
|
||||
select = ty == 'IfcUtil::IfcBaseClass'
|
||||
return arr and not simple and not express and not select
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ import string
|
||||
import collections
|
||||
|
||||
class Node:
|
||||
def __init__(self, tokens):
|
||||
self.tokens = tokens
|
||||
def __init__(self, tokens = None):
|
||||
self.tokens = tokens or []
|
||||
self.init()
|
||||
def tokens_of_type(self, cls):
|
||||
return [t for t in self.tokens if isinstance(t, cls)]
|
||||
@@ -128,7 +128,7 @@ class AttributeList(Node):
|
||||
class InverseAttribute(Node):
|
||||
name = property(lambda self: self.tokens[0])
|
||||
type = property(lambda self: self.tokens[2])
|
||||
bounds = property(lambda self: None if len(self.tokens) == 6 else self.tokens[3])
|
||||
bounds = property(lambda self: None if len(self.tokens) != 9 else self.tokens[3])
|
||||
entity = property(lambda self: self.tokens[-4])
|
||||
attribute = property(lambda self: self.tokens[-2])
|
||||
def init(self):
|
||||
@@ -149,7 +149,7 @@ class BinaryType(Node):
|
||||
def init(self):
|
||||
pass
|
||||
def __repr__(self):
|
||||
return "BINARY"
|
||||
return "binary"
|
||||
|
||||
|
||||
class BoundSpecification(Node):
|
||||
@@ -170,6 +170,23 @@ class ExplicitAttribute(Node):
|
||||
def init(self):
|
||||
# NB: This assumes a single name per attribute
|
||||
# definition, which is not necessarily the case.
|
||||
if self.tokens[0] == "self":
|
||||
i = list(self.tokens).index(":")
|
||||
self.tokens = self.tokens[i-1:]
|
||||
assert self.tokens[1] == ':'
|
||||
def __repr__(self):
|
||||
return "%s : %s%s" % (self.name, self.type, " ?" if self.optional else "")
|
||||
|
||||
|
||||
class WidthSpec(Node):
|
||||
def init(self):
|
||||
if self.tokens[-1] == "fixed":
|
||||
self.tokens[-1:] = []
|
||||
assert (self.tokens[0], self.tokens[-1]) == ("(", ")")
|
||||
self.width = int("".join(self.tokens[1:-1]))
|
||||
|
||||
class StringType(Node):
|
||||
def init(self):
|
||||
pass
|
||||
def __repr__(self):
|
||||
return "string"
|
||||
|
||||
@@ -18,29 +18,56 @@
|
||||
###############################################################################
|
||||
|
||||
import nodes
|
||||
import platform
|
||||
import collections
|
||||
|
||||
if tuple(map(int, platform.python_version_tuple())) < (2, 7):
|
||||
import ordereddict
|
||||
collections.OrderedDict = ordereddict.OrderedDict
|
||||
|
||||
# According to ISO 10303-11 7.1.2: Letters: "... The case of
|
||||
# letters is significant only within explicit string literals."
|
||||
class OrderedCaseInsensitiveDict(collections.OrderedDict):
|
||||
class KeyObject(str):
|
||||
def __eq__(self, other):
|
||||
return self.lower() == other.lower()
|
||||
def __hash__(self):
|
||||
return hash(self.lower())
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
collections.OrderedDict.__init__(self)
|
||||
for key, value in collections.OrderedDict(*args, **kwargs).items():
|
||||
self[OrderedCaseInsensitiveDict.KeyObject(key)] = value
|
||||
def __setitem__(self, key, value):
|
||||
return collections.OrderedDict.__setitem__(self, OrderedCaseInsensitiveDict.KeyObject(key), value)
|
||||
def __getitem__(self, key):
|
||||
return collections.OrderedDict.__getitem__(self, OrderedCaseInsensitiveDict.KeyObject(key))
|
||||
def get(self, key, *args, **kwargs):
|
||||
return collections.OrderedDict.get(self, OrderedCaseInsensitiveDict.KeyObject(key), *args, **kwargs)
|
||||
def __contains__(self, key):
|
||||
return collections.OrderedDict.__contains__(self, OrderedCaseInsensitiveDict.KeyObject(key))
|
||||
|
||||
class Schema:
|
||||
def is_enumeration(self, v):
|
||||
return v in self.enumerations
|
||||
return str(v) in self.enumerations
|
||||
def is_select(self, v):
|
||||
return v in self.selects
|
||||
return str(v) in self.selects
|
||||
def is_simpletype(self, v):
|
||||
return v in self.simpletypes
|
||||
return str(v) in self.simpletypes
|
||||
def is_type(self, v):
|
||||
return v in self.types
|
||||
return str(v) in self.types
|
||||
def is_entity(self, v):
|
||||
return v in self.entities
|
||||
return str(v) in self.entities
|
||||
def __init__(self, parsetree):
|
||||
self.name = parsetree[1]
|
||||
|
||||
sort = lambda d: collections.OrderedDict(sorted(d.items()))
|
||||
sort = lambda d: OrderedCaseInsensitiveDict(sorted(d))
|
||||
|
||||
self.types = sort({t.name:t for t in parsetree if isinstance(t, nodes.TypeDeclaration)})
|
||||
self.entities = sort({t.name:t for t in parsetree if isinstance(t, nodes.EntityDeclaration)})
|
||||
self.types = sort([(t.name,t) for t in parsetree if isinstance(t, nodes.TypeDeclaration)])
|
||||
self.entities = sort([(t.name,t) for t in parsetree if isinstance(t, nodes.EntityDeclaration)])
|
||||
|
||||
of_type = lambda *types: sort({a: b.type.type for a,b in self.types.items() if any(isinstance(b.type.type, ty) for ty in types)})
|
||||
of_type = lambda *types: sort([(a, b.type.type) for a,b in self.types.items() if any(isinstance(b.type.type, ty) for ty in types)])
|
||||
|
||||
self.enumerations = of_type(nodes.EnumerationType)
|
||||
self.selects = of_type(nodes.SelectType)
|
||||
self.simpletypes = of_type(str, nodes.AggregationType)
|
||||
self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType, nodes.StringType)
|
||||
|
||||
@@ -23,12 +23,16 @@ header = """
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
|
||||
#include "../ifcparse/IfcEntityList.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/Argument.h"
|
||||
|
||||
#include "../ifcparse/%(schema_name)senum.h"
|
||||
|
||||
#define IfcSchema %(schema_name)s
|
||||
@@ -43,8 +47,8 @@ const char* const Identifier = "%(schema_name_upper)s";
|
||||
%(declarations)s
|
||||
|
||||
%(class_definitions)s
|
||||
void InitStringMap();
|
||||
IfcUtil::IfcBaseClass* SchemaEntity(IfcAbstractEntity* e = 0);
|
||||
IFC_PARSE_API void InitStringMap();
|
||||
IFC_PARSE_API IfcUtil::IfcBaseClass* SchemaEntity(IfcEntityInstanceData* e = 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -54,6 +58,11 @@ enum_header = """
|
||||
#ifndef %(schema_name_upper)sENUM_H
|
||||
#define %(schema_name_upper)sENUM_H
|
||||
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#define IfcSchema %(schema_name)s
|
||||
|
||||
namespace %(schema_name)s {
|
||||
@@ -62,10 +71,10 @@ namespace Type {
|
||||
typedef enum {
|
||||
%(types)s, UNDEFINED
|
||||
} Enum;
|
||||
Enum Parent(Enum v);
|
||||
Enum FromString(const std::string& s);
|
||||
std::string ToString(Enum v);
|
||||
bool IsSimple(Enum v);
|
||||
IFC_PARSE_API boost::optional<Enum> Parent(Enum v);
|
||||
IFC_PARSE_API Enum FromString(const std::string& s);
|
||||
IFC_PARSE_API const std::string& ToString(Enum v);
|
||||
IFC_PARSE_API bool IsSimple(Enum v);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,23 +88,23 @@ lb_header = """
|
||||
|
||||
#define IfcSchema %(schema_name)s
|
||||
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcEntityDescriptor.h"
|
||||
#include "../ifcparse/IfcWritableEntity.h"
|
||||
|
||||
namespace %(schema_name)s {
|
||||
namespace Type {
|
||||
int GetAttributeCount(Enum t);
|
||||
int GetAttributeIndex(Enum t, const std::string& a);
|
||||
IfcUtil::ArgumentType GetAttributeType(Enum t, unsigned char a);
|
||||
Enum GetAttributeEntity(Enum t, unsigned char a);
|
||||
const std::string& GetAttributeName(Enum t, unsigned char a);
|
||||
bool GetAttributeOptional(Enum t, unsigned char a);
|
||||
bool GetAttributeDerived(Enum t, unsigned char a);
|
||||
std::pair<const char*, int> GetEnumerationIndex(Enum t, const std::string& a);
|
||||
std::pair<Enum, unsigned> GetInverseAttribute(Enum t, const std::string& a);
|
||||
std::set<std::string> GetInverseAttributeNames(Enum t);
|
||||
void PopulateDerivedFields(IfcWrite::IfcWritableEntity* e);
|
||||
IFC_PARSE_API int GetAttributeCount(Enum t);
|
||||
IFC_PARSE_API int GetAttributeIndex(Enum t, const std::string& a);
|
||||
IFC_PARSE_API IfcUtil::ArgumentType GetAttributeType(Enum t, unsigned char a);
|
||||
IFC_PARSE_API Enum GetAttributeEntity(Enum t, unsigned char a);
|
||||
IFC_PARSE_API const std::string& GetAttributeName(Enum t, unsigned char a);
|
||||
IFC_PARSE_API bool GetAttributeOptional(Enum t, unsigned char a);
|
||||
IFC_PARSE_API bool GetAttributeDerived(Enum t, unsigned char a);
|
||||
IFC_PARSE_API std::pair<const char*, int> GetEnumerationIndex(Enum t, const std::string& a);
|
||||
IFC_PARSE_API std::pair<Enum, unsigned> GetInverseAttribute(Enum t, const std::string& a);
|
||||
IFC_PARSE_API std::set<std::string> GetInverseAttributeNames(Enum t);
|
||||
IFC_PARSE_API void PopulateDerivedFields(IfcEntityInstanceData* e);
|
||||
}}
|
||||
|
||||
#endif
|
||||
@@ -105,22 +114,23 @@ implementation= """
|
||||
#include "../ifcparse/%(schema_name)s.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/IfcWrite.h"
|
||||
#include "../ifcparse/IfcWritableEntity.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
using namespace %(schema_name)s;
|
||||
using namespace IfcParse;
|
||||
using namespace IfcWrite;
|
||||
|
||||
IfcUtil::IfcBaseClass* %(schema_name)s::SchemaEntity(IfcAbstractEntity* e) {
|
||||
IfcUtil::IfcBaseClass* %(schema_name)s::SchemaEntity(IfcEntityInstanceData* e) {
|
||||
switch(e->type()) {
|
||||
%(schema_entity_statements)s
|
||||
default: throw IfcException("Unable to find find keyword in schema"); break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Type::ToString(Enum v) {
|
||||
const std::string& Type::ToString(Enum v) {
|
||||
if (v < 0 || v >= %(max_id)d) throw IfcException("Unable to find find keyword in schema");
|
||||
const char* names[] = { %(type_name_strings)s };
|
||||
static std::string names[] = { %(type_name_strings)s };
|
||||
return names[v];
|
||||
}
|
||||
|
||||
@@ -136,10 +146,14 @@ Type::Enum Type::FromString(const std::string& s) {
|
||||
else return it->second;
|
||||
}
|
||||
|
||||
Type::Enum Type::Parent(Enum v){
|
||||
if (v < 0 || v >= %(max_id)d) return (Enum)-1;
|
||||
%(parent_type_statements)s
|
||||
return (Enum)-1;
|
||||
static int parent_map[] = {%(parent_type_statements)s};
|
||||
boost::optional<Type::Enum> Type::Parent(Enum v){
|
||||
const int p = parent_map[static_cast<int>(v)];
|
||||
if (p >= 0) {
|
||||
return static_cast<Type::Enum>(p);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
bool Type::IsSimple(Enum v) {
|
||||
@@ -160,8 +174,7 @@ lb_implementation = """
|
||||
#include "../ifcparse/%(schema_name)s-latebound.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/IfcWrite.h"
|
||||
#include "../ifcparse/IfcWritableEntity.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcEntityDescriptor.h"
|
||||
|
||||
using namespace %(schema_name)s;
|
||||
@@ -179,15 +192,23 @@ enumeration_descriptor_map_t enumeration_descriptor_map;
|
||||
inverse_map_t inverse_map;
|
||||
derived_map_t derived_map;
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma optimize( "", off )
|
||||
#endif
|
||||
|
||||
void InitDescriptorMap() {
|
||||
IfcEntityDescriptor* current;
|
||||
%(entity_descriptors)s
|
||||
// Enumerations
|
||||
IfcEnumerationDescriptor* current_enum;
|
||||
std::vector<std::string> values;
|
||||
%(enumeration_descriptors)s
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma optimize( "", on )
|
||||
#endif
|
||||
|
||||
void InitInverseMap() {
|
||||
%(inverse_implementations)s
|
||||
}
|
||||
@@ -252,49 +273,64 @@ std::pair<const char*, int> Type::GetEnumerationIndex(Enum t, const std::string&
|
||||
}
|
||||
|
||||
std::pair<Type::Enum, unsigned> Type::GetInverseAttribute(Enum t, const std::string& a) {
|
||||
if (inverse_map.empty()) ::InitInverseMap();
|
||||
inverse_map_t::const_iterator it;
|
||||
inverse_map_t::mapped_type::const_iterator jt;
|
||||
while (true) {
|
||||
if (inverse_map.empty()) ::InitInverseMap();
|
||||
inverse_map_t::const_iterator it;
|
||||
inverse_map_t::mapped_type::const_iterator jt;
|
||||
for(;;) {
|
||||
it = inverse_map.find(t);
|
||||
if (it != inverse_map.end()) {
|
||||
jt = it->second.find(a);
|
||||
if (jt != it->second.end()) {
|
||||
return jt->second;
|
||||
}
|
||||
}
|
||||
if ((t = Parent(t)) == -1) break;
|
||||
jt = it->second.find(a);
|
||||
if (jt != it->second.end()) {
|
||||
return jt->second;
|
||||
}
|
||||
}
|
||||
boost::optional<Enum> pt = Parent(t);
|
||||
if (pt) {
|
||||
t = *pt;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw IfcException("Attribute not found");
|
||||
}
|
||||
|
||||
std::set<std::string> Type::GetInverseAttributeNames(Enum t) {
|
||||
if (inverse_map.empty()) ::InitInverseMap();
|
||||
inverse_map_t::const_iterator it;
|
||||
inverse_map_t::mapped_type::const_iterator jt;
|
||||
if (inverse_map.empty()) ::InitInverseMap();
|
||||
inverse_map_t::const_iterator it;
|
||||
inverse_map_t::mapped_type::const_iterator jt;
|
||||
|
||||
std::set<std::string> return_value;
|
||||
std::set<std::string> return_value;
|
||||
|
||||
while (true) {
|
||||
for (;;) {
|
||||
it = inverse_map.find(t);
|
||||
if (it != inverse_map.end()) {
|
||||
for (jt = it->second.begin(); jt != it->second.end(); ++jt) {
|
||||
return_value.insert(jt->first);
|
||||
}
|
||||
}
|
||||
if ((t = Parent(t)) == -1) break;
|
||||
for (jt = it->second.begin(); jt != it->second.end(); ++jt) {
|
||||
return_value.insert(jt->first);
|
||||
}
|
||||
}
|
||||
boost::optional<Enum> pt = Parent(t);
|
||||
if (pt) {
|
||||
t = *pt;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return return_value;
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void Type::PopulateDerivedFields(IfcWrite::IfcWritableEntity* e) {
|
||||
void Type::PopulateDerivedFields(IfcEntityInstanceData* e) {
|
||||
if (derived_map.empty()) ::InitDerivedMap();
|
||||
std::map<Type::Enum, std::set<int> >::const_iterator i = derived_map.find(e->type());
|
||||
if (i != derived_map.end()) {
|
||||
for (std::set<int>::const_iterator it = i->second.begin(); it != i->second.end(); ++it) {
|
||||
e->setArgumentDerived(*it);
|
||||
}
|
||||
}
|
||||
if (i != derived_map.end()) {
|
||||
for (std::set<int>::const_iterator it = i->second.begin(); it != i->second.end(); ++it) {
|
||||
IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();
|
||||
attr->set(IfcWrite::IfcWriteArgument::Derived());
|
||||
e->setArgument(*it, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -307,7 +343,7 @@ entity_descriptor_attribute_with_entity = ' current->add("%(name)s",%(optiona
|
||||
|
||||
enumeration_descriptor = """ values.clear(); values.reserve(128);
|
||||
%(enumeration_descriptor_values)s
|
||||
current_enum = enumeration_descriptor_map[Type::%(type)s] = new IfcEnumerationDescriptor(Type::%(type)s, values);"""
|
||||
enumeration_descriptor_map[Type::%(type)s] = new IfcEnumerationDescriptor(Type::%(type)s, values);"""
|
||||
|
||||
enumeration_descriptor_value = ' values.push_back("%(name)s");'
|
||||
|
||||
@@ -315,29 +351,31 @@ derived_field_statement = ' {std::set<int> idxs; %(statements)sderived_map[Ty
|
||||
derived_field_statement_attrs = 'idxs.insert(%d); '
|
||||
|
||||
simpletype = """%(documentation)s
|
||||
class %(name)s : public %(superclass)s {
|
||||
class IFC_PARSE_API %(name)s : public %(superclass)s {
|
||||
public:
|
||||
virtual IfcUtil::ArgumentType getArgumentType(unsigned int i) const;
|
||||
virtual Argument* getArgument(unsigned int i) const;
|
||||
bool is(Type::Enum v) const;
|
||||
Type::Enum type() const;
|
||||
static Type::Enum Class();
|
||||
explicit %(name)s (IfcAbstractEntity* e);
|
||||
explicit %(name)s (IfcEntityInstanceData* e);
|
||||
%(name)s (%(type)s v);
|
||||
operator %(type)s() const;
|
||||
};
|
||||
"""
|
||||
|
||||
simpletype_impl_comment = "// Function implementations for %(name)s"
|
||||
simpletype_impl_argument_type = "if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcException(\"argument out of range\"); }"
|
||||
simpletype_impl_argument_type = "if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcAttributeOutOfRangeException(\"Argument index out of range\"); }"
|
||||
simpletype_impl_argument = "return entity->getArgument(i);"
|
||||
simpletype_impl_is_with_supertype = "return v == Type::%(class_name)s || %(superclass)s::is(v);"
|
||||
simpletype_impl_is_without_supertype = "return v == %(class_name)s::Class();"
|
||||
simpletype_impl_type = "return Type::%(class_name)s;"
|
||||
simpletype_impl_class = "return Type::%(class_name)s;"
|
||||
simpletype_impl_explicit_constructor = "entity = e;"
|
||||
simpletype_impl_constructor = "IfcWritableEntity* e = new IfcWritableEntity(Type::%(class_name)s); e->setArgument(0, v); entity = e;"
|
||||
simpletype_impl_cast = "return *entity->getArgument(0);"
|
||||
simpletype_impl_constructor = "entity = new IfcEntityInstanceData(Class()); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v" +"); entity->setArgument(0, attr);}"
|
||||
simpletype_impl_constructor_templated = "entity = new IfcEntityInstanceData(Class()); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v->generalize()); entity->setArgument(0, attr);}"
|
||||
simpletype_impl_cast = "return *entity->getArgument(0);"
|
||||
simpletype_impl_cast_templated = "IfcEntityList::ptr es = *entity->getArgument(0); return es->as<%(underlying_type)s>();"
|
||||
|
||||
select = """%(documentation)s
|
||||
typedef IfcUtil::IfcBaseClass %(name)s;
|
||||
@@ -346,13 +384,13 @@ typedef IfcUtil::IfcBaseClass %(name)s;
|
||||
enumeration = """namespace %(name)s {
|
||||
%(documentation)s
|
||||
typedef enum {%(values)s} %(name)s;
|
||||
const char* ToString(%(name)s v);
|
||||
%(name)s FromString(const std::string& s);
|
||||
IFC_PARSE_API const char* ToString(%(name)s v);
|
||||
IFC_PARSE_API %(name)s FromString(const std::string& s);
|
||||
}
|
||||
"""
|
||||
|
||||
entity = """%(documentation)s
|
||||
class %(name)s %(superclass)s{
|
||||
class IFC_PARSE_API %(name)s %(superclass)s{
|
||||
public:
|
||||
%(attributes)s virtual unsigned int getArgumentCount() const { return %(argument_count)d; }
|
||||
virtual IfcUtil::ArgumentType getArgumentType(unsigned int i) const {%(argument_type_function_body)s}
|
||||
@@ -362,7 +400,7 @@ public:
|
||||
%(inverse)s bool is(Type::Enum v) const;
|
||||
Type::Enum type() const;
|
||||
static Type::Enum Class();
|
||||
%(name)s (IfcAbstractEntity* e);
|
||||
%(name)s (IfcEntityInstanceData* e);
|
||||
%(name)s (%(constructor_arguments)s);
|
||||
typedef IfcTemplatedEntityList< %(name)s > list;
|
||||
};
|
||||
@@ -385,8 +423,8 @@ entity_implementation = """// Function implementations for %(name)s
|
||||
%(attributes)s%(inverse)sbool %(name)s::is(Type::Enum v) const { return v == Type::%(name)s%(parent_type_test)s; }
|
||||
Type::Enum %(name)s::type() const { return Type::%(name)s; }
|
||||
Type::Enum %(name)s::Class() { return Type::%(name)s; }
|
||||
%(name)s::%(name)s(IfcAbstractEntity* e) : %(superclass)s { if (!e) return; if (!e->is(Type::%(name)s)) throw IfcException("Unable to find find keyword in schema"); entity = e; }
|
||||
%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass)s { IfcWritableEntity* e = new IfcWritableEntity(Class());%(constructor_implementation)s entity = e; EntityBuffer::Add(this); }
|
||||
%(name)s::%(name)s(IfcEntityInstanceData* e) : %(superclass)s { if (!e) return; if (e->type() != Type::%(name)s) throw IfcException("Unable to find find keyword in schema"); entity = e; }
|
||||
%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass)s {entity = new IfcEntityInstanceData(Class()); %(constructor_implementation)s }
|
||||
"""
|
||||
|
||||
optional_attribute_description = "/// Whether the optional attribute %s is defined for this %s"
|
||||
@@ -398,6 +436,7 @@ constructor_single_initlist = "%(class_name)s::%(class_name)s(%(arguments)s) : %
|
||||
cast_function = "%(class_name)s::operator %(return_type)s() const { %(body)s }"
|
||||
|
||||
array_type = "std::vector< %(instance_type)s > /*[%(lower)s:%(upper)s]*/"
|
||||
nested_array_type = "std::vector< std::vector< %(instance_type)s > >"
|
||||
list_type = "IfcTemplatedEntityList< %(instance_type)s >::ptr"
|
||||
list_list_type = "IfcTemplatedEntityListList< %(instance_type)s >::ptr"
|
||||
untyped_list = "IfcEntityList::ptr"
|
||||
@@ -406,40 +445,31 @@ inverse_attr = "IfcTemplatedEntityList< %(entity)s >::ptr %(name)s() const; // I
|
||||
enum_from_string_stmt = ' if (s == "%(value)s") return ::%(schema_name)s::%(name)s::%(short_name)s_%(value)s;'
|
||||
|
||||
schema_entity_stmt = ' case Type::%(name)s: return new %(name)s(e); break;'
|
||||
schema_simple_stmt = ' case Type::%(name)s: return new IfcUtil::IfcEntitySelect(e); break;'
|
||||
string_map_statement = ' string_map["%(uppercase_name)s"%(padding)s] = Type::%(name)s;'
|
||||
parent_type_stmt = ' if(v==%(name)s%(padding)s) { return %(parent)s; }'
|
||||
|
||||
parent_type_test = " || %s::is(v)"
|
||||
|
||||
get_attr_stmt_generic = "return *entity->getArgument(%(index)d);"
|
||||
get_attr_stmt_enum = "return %(type)s::FromString(*entity->getArgument(%(index)d));"
|
||||
get_attr_stmt_entity = "return (%(type)s)((IfcUtil::IfcBaseClass*)(*entity->getArgument(%(index)d)));"
|
||||
get_attr_stmt_array = "IfcEntityList::ptr es = *entity->getArgument(%(index)d); return es->as<%(list_instance_type)s>();"
|
||||
get_attr_stmt_nested_array = "IfcEntityListList::ptr es = *entity->getArgument(%(index)d); return es->as<%(list_instance_type)s>();"
|
||||
get_attr_stmt_optional = "Argument* arg = entity->getArgument(%%(index)d); if (arg->isNull()) { return boost::none; } else { %s }"
|
||||
get_attr_stmt_optional_generic = get_attr_stmt_optional % "return *arg;"
|
||||
get_attr_stmt_optional_enum = get_attr_stmt_optional % "return %(type)s::FromString(*arg);"
|
||||
get_attr_stmt_optional_entity = get_attr_stmt_optional % "return (%(type)s)((IfcUtil::IfcBaseClass*)(*arg));"
|
||||
get_attr_stmt_optional_array = get_attr_stmt_optional % "IfcEntityList::ptr es = *arg; return es->as<%(list_instance_type)s>();"
|
||||
get_attr_stmt_optional_nested_array = get_attr_stmt_optional % "IfcEntityListList::ptr es = *arg; return es->as<%(list_instance_type)s>();"
|
||||
optional_attr_stmt = "return !entity->getArgument(%(index)d)->isNull();"
|
||||
|
||||
set_attr_stmt = "IfcWritableEntity* w = entity->isWritable(); if (!w) { entity = w = new IfcWritableEntity(entity); } %s;"
|
||||
set_attr_stmt_optional = "IfcWritableEntity* w = entity->isWritable(); if (!w) { entity = w = new IfcWritableEntity(entity); } if (v) { %s; } else { w->setArgument(%%(index)d); }"
|
||||
set_attr_stmt_generic = set_attr_stmt % "w->setArgument(%(index)d,v)"
|
||||
set_attr_stmt_enum = set_attr_stmt % "w->setArgument(%(index)d,v,%(type)s::ToString(v));"
|
||||
set_attr_stmt_array = set_attr_stmt % "w->setArgument(%(index)d,v->generalize());"
|
||||
set_attr_stmt_optional_generic = set_attr_stmt_optional % "w->setArgument(%(index)d,*v)"
|
||||
set_attr_stmt_optional_enum = set_attr_stmt_optional % "w->setArgument(%(index)d,*v,%(type)s::ToString(*v));"
|
||||
set_attr_stmt_optional_array = set_attr_stmt_optional % "w->setArgument(%(index)d,(*v)->generalize());"
|
||||
get_attr_stmt = "return *entity->getArgument(%(index)d);"
|
||||
get_attr_stmt_enum = "return %(type)s::FromString(*entity->getArgument(%(index)d));"
|
||||
get_attr_stmt_entity = "return (%(type)s)((IfcUtil::IfcBaseClass*)(*entity->getArgument(%(index)d)));"
|
||||
get_attr_stmt_array = "IfcEntityList::ptr es = *entity->getArgument(%(index)d); return es->as<%(list_instance_type)s>();"
|
||||
get_attr_stmt_nested_array = "IfcEntityListList::ptr es = *entity->getArgument(%(index)d); return es->as<%(list_instance_type)s>();"
|
||||
|
||||
get_inverse = "return entity->getInverse(Type::%(type)s, %(index)d)->as<%(type)s>();"
|
||||
|
||||
constructor_stmt = " e->setArgument(%(index)d,(%(name)s));"
|
||||
constructor_stmt_enum = " e->setArgument(%(index)d,%(name)s,%(type)s::ToString(%(name)s));"
|
||||
constructor_stmt_array = " e->setArgument(%(index)d,(%(name)s)->generalize());"
|
||||
constructor_stmt_optional = " if (%(name)s) {%(stmt)s } else { e->setArgument(%(index)d); }"
|
||||
constructor_stmt_derived = " e->setArgumentDerived(%(index)d);"
|
||||
set_attr_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v" +");entity->setArgument(%(index)d,attr);}"
|
||||
set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,%(type)s::ToString(v)));entity->setArgument(%(index)d,attr);}"
|
||||
set_attr_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v->generalize()" +");entity->setArgument(%(index)d,attr);}"
|
||||
|
||||
constructor_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)" +");entity->setArgument(%(index)d,attr);}"
|
||||
constructor_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((IfcWrite::IfcWriteArgument::EnumerationReference(%(name)s,%(type)s::ToString(%(name)s)))" +");entity->setArgument(%(index)d,attr);}"
|
||||
constructor_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)->generalize()" +");entity->setArgument(%(index)d,attr);}"
|
||||
constructor_stmt_derived = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::Derived()" +");entity->setArgument(%(index)d,attr);}"
|
||||
|
||||
constructor_stmt_optional = " if (%(name)s) {%(stmt)s } else { IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(boost::blank()); entity->setArgument(%(index)d, attr); }"
|
||||
|
||||
inverse_implementation = " inverse_map[Type::%(type)s].insert(std::make_pair(\"%(name)s\", std::make_pair(Type::%(related_type)s, %(index)d)));"
|
||||
|
||||
|
||||
+223
-50
@@ -20,8 +20,14 @@
|
||||
#ifndef IFCGEOM_H
|
||||
#define IFCGEOM_H
|
||||
|
||||
#define ALMOST_ZERO (1e-9)
|
||||
#define ALMOST_THE_SAME(a,b) (fabs(a-b) < ALMOST_ZERO)
|
||||
#include <cmath>
|
||||
|
||||
static const double ALMOST_ZERO = 1.e-9;
|
||||
|
||||
template <typename T>
|
||||
inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMOST_ZERO) {
|
||||
return fabs(a-b) < tolerance;
|
||||
}
|
||||
|
||||
#include <gp_Pnt.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
@@ -37,52 +43,131 @@
|
||||
#include <Geom_Curve.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
#include <TColgp_SequenceOfPnt.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <BOPAlgo_Operation.hxx>
|
||||
|
||||
#include "../ifcparse/IfcParse.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
|
||||
#include "../ifcgeom/IfcGeomElement.h"
|
||||
#include "../ifcgeom/IfcGeomRepresentation.h"
|
||||
#include "../ifcgeom/IfcRepresentationShapeItem.h"
|
||||
#include "../ifcgeom/IfcGeomShapeType.h"
|
||||
#include "ifc_geom_api.h"
|
||||
|
||||
// Define this in case you want to conserve memory usage at all cost. This has been
|
||||
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
|
||||
// #define NO_CACHE
|
||||
|
||||
#ifdef NO_CACHE
|
||||
|
||||
#define IN_CACHE(T,E,t,e)
|
||||
#define CACHE(T,E,e)
|
||||
|
||||
#else
|
||||
|
||||
#define IN_CACHE(T,E,t,e) std::map<int,t>::const_iterator it = cache.T.find(E->entity->id());\
|
||||
if ( it != cache.T.end() ) { e = it->second; return true; }
|
||||
#define CACHE(T,E,e) cache.T[E->entity->id()] = e;
|
||||
|
||||
namespace IfcGeom {
|
||||
#endif
|
||||
|
||||
class Cache {
|
||||
namespace IfcGeom {
|
||||
class IFC_GEOM_API geometry_exception : public std::exception {
|
||||
protected:
|
||||
std::string message;
|
||||
public:
|
||||
geometry_exception(const std::string& m)
|
||||
: message(m) {}
|
||||
virtual ~geometry_exception() throw () {}
|
||||
virtual const char* what() const throw() {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class IFC_GEOM_API too_many_faces_exception : public geometry_exception {
|
||||
public:
|
||||
too_many_faces_exception()
|
||||
: geometry_exception("Too many faces for operation") {}
|
||||
};
|
||||
|
||||
class IFC_GEOM_API Cache {
|
||||
public:
|
||||
#include "IfcRegisterCreateCache.h"
|
||||
std::map<int, SurfaceStyle> Style;
|
||||
std::map<int, TopoDS_Shape> Shape;
|
||||
};
|
||||
|
||||
class Kernel {
|
||||
class IFC_GEOM_API Kernel {
|
||||
private:
|
||||
|
||||
double deflection_tolerance;
|
||||
double wire_creation_tolerance;
|
||||
double point_equality_tolerance;
|
||||
double max_faces_to_sew;
|
||||
double ifc_length_unit;
|
||||
double ifc_planeangle_unit;
|
||||
double modelling_precision;
|
||||
double dimensionality;
|
||||
|
||||
#ifndef NO_CACHE
|
||||
Cache cache;
|
||||
#endif
|
||||
|
||||
std::map<int, SurfaceStyle> style_cache;
|
||||
|
||||
const SurfaceStyle* internalize_surface_style(const std::pair<IfcSchema::IfcSurfaceStyle*, IfcSchema::IfcSurfaceStyleShading*>& shading_style);
|
||||
|
||||
// For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf)
|
||||
IfcSchema::Type::Enum placement_rel_to;
|
||||
|
||||
public:
|
||||
Kernel()
|
||||
: deflection_tolerance(0.001)
|
||||
, wire_creation_tolerance(0.0001)
|
||||
, point_equality_tolerance(0.00001)
|
||||
, max_faces_to_sew(-1.0)
|
||||
, ifc_length_unit(1.0)
|
||||
, ifc_planeangle_unit(-1.0)
|
||||
, modelling_precision(0.00001)
|
||||
, dimensionality(1.)
|
||||
, placement_rel_to(IfcSchema::Type::UNDEFINED)
|
||||
{}
|
||||
|
||||
Kernel(const Kernel& other) {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Kernel& operator=(const Kernel& other) {
|
||||
setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE));
|
||||
setValue(GV_WIRE_CREATION_TOLERANCE, other.getValue(GV_WIRE_CREATION_TOLERANCE));
|
||||
setValue(GV_POINT_EQUALITY_TOLERANCE, other.getValue(GV_POINT_EQUALITY_TOLERANCE));
|
||||
setValue(GV_MAX_FACES_TO_SEW, other.getValue(GV_MAX_FACES_TO_SEW));
|
||||
setValue(GV_LENGTH_UNIT, other.getValue(GV_LENGTH_UNIT));
|
||||
setValue(GV_PLANEANGLE_UNIT, other.getValue(GV_PLANEANGLE_UNIT));
|
||||
setValue(GV_PRECISION, other.getValue(GV_PRECISION));
|
||||
setValue(GV_DIMENSIONALITY, other.getValue(GV_DIMENSIONALITY));
|
||||
setValue(GV_DEFLECTION_TOLERANCE, other.getValue(GV_DEFLECTION_TOLERANCE));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Tolerances and settings for various geometrical operations:
|
||||
enum GeomValue {
|
||||
// Specifies the deflection of the mesher
|
||||
// Default: 0.001m / 1mm
|
||||
GV_DEFLECTION_TOLERANCE,
|
||||
// Specifies the tolerance of the wire builder, most notably for trimmed curves
|
||||
// Defailt: 0.0001m / 0.1mm
|
||||
// Default: 0.0001m / 0.1mm
|
||||
GV_WIRE_CREATION_TOLERANCE,
|
||||
// Specifies the minimal area of a face to be included in an IfcConnectedFaceset
|
||||
// Default: 0.000001m 0.01cm2
|
||||
// Read-only
|
||||
GV_MINIMAL_FACE_AREA,
|
||||
// Specifies the treshold distance under which cartesian points are deemed equal
|
||||
// Specifies the threshold distance under which cartesian points are deemed equal
|
||||
// Default: 0.00001m / 0.01mm
|
||||
GV_POINT_EQUALITY_TOLERANCE,
|
||||
// Specifies maximum number of faces for a shell to be sewed. Sewing shells
|
||||
// that consist of many faces is really detrimental for the performance.
|
||||
// Default: 1000
|
||||
GV_MAX_FACES_TO_SEW,
|
||||
// By default singular faces have no explicitly defined orientation, to
|
||||
// force faces to be defined CounterClockWise, set this value greater than zero.
|
||||
GV_FORCE_CCW_FACE_ORIENTATION,
|
||||
// The length unit used the creation of TopoDS_Shapes, primarily affects the
|
||||
// interpretation of IfcCartesianPoints and IfcVector magnitudes
|
||||
// DefaultL 1.0
|
||||
@@ -94,13 +179,15 @@ public:
|
||||
// The precision used in boolean operations, setting this value too low results
|
||||
// in artefacts and potentially modelling failures
|
||||
// Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available)
|
||||
GV_PRECISION
|
||||
GV_PRECISION,
|
||||
// Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
|
||||
GV_DIMENSIONALITY
|
||||
};
|
||||
|
||||
bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face);
|
||||
bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire);
|
||||
bool convert_shapes(const IfcUtil::IfcBaseClass* L, IfcRepresentationShapeItems& result);
|
||||
bool is_shape_collection(const IfcUtil::IfcBaseClass* L);
|
||||
IfcGeom::ShapeType shape_type(const IfcUtil::IfcBaseClass* L);
|
||||
bool convert_shape(const IfcUtil::IfcBaseClass* L, TopoDS_Shape& result);
|
||||
bool flatten_shape_list(const IfcGeom::IfcRepresentationShapeItems& shapes, TopoDS_Shape& result, bool fuse);
|
||||
bool convert_wire(const IfcUtil::IfcBaseClass* L, TopoDS_Wire& result);
|
||||
@@ -108,8 +195,44 @@ public:
|
||||
bool convert_face(const IfcUtil::IfcBaseClass* L, TopoDS_Shape& result);
|
||||
bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes);
|
||||
bool convert_openings_fast(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes);
|
||||
void assert_closed_wire(TopoDS_Wire& wire);
|
||||
|
||||
bool convert_layerset(const IfcSchema::IfcProduct*, std::vector<Handle_Geom_Surface>&, std::vector<const SurfaceStyle*>&, std::vector<double>&);
|
||||
bool apply_layerset(const IfcRepresentationShapeItems&, const std::vector<Handle_Geom_Surface>&, const std::vector<const SurfaceStyle*>&, IfcRepresentationShapeItems&);
|
||||
bool apply_folded_layerset(const IfcRepresentationShapeItems&, const std::vector< std::vector<Handle_Geom_Surface> >&, const std::vector<const SurfaceStyle*>&, IfcRepresentationShapeItems&);
|
||||
bool fold_layers(const IfcSchema::IfcWall*, const IfcRepresentationShapeItems&, const std::vector<Handle_Geom_Surface>&, const std::vector<double>&, std::vector< std::vector<Handle_Geom_Surface> >&);
|
||||
|
||||
bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&);
|
||||
bool split_solid_by_shell(const TopoDS_Shape&, const TopoDS_Shape& s, TopoDS_Shape&, TopoDS_Shape&);
|
||||
|
||||
#if OCC_VERSION_HEX < 0x60900
|
||||
bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&);
|
||||
bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&);
|
||||
#else
|
||||
bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);
|
||||
bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);
|
||||
#endif
|
||||
|
||||
bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height);
|
||||
|
||||
const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&);
|
||||
const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&);
|
||||
const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&);
|
||||
bool intersect(const Handle_Geom_Curve&, const Handle_Geom_Surface&, gp_Pnt&);
|
||||
bool intersect(const Handle_Geom_Curve&, const TopoDS_Face&, gp_Pnt&);
|
||||
bool intersect(const Handle_Geom_Curve&, const TopoDS_Shape&, std::vector<gp_Pnt>&);
|
||||
bool intersect(const Handle_Geom_Surface&, const TopoDS_Shape&, std::vector< std::pair<Handle_Geom_Surface, Handle_Geom_Curve> >&);
|
||||
bool closest(const gp_Pnt&, const std::vector<gp_Pnt>&, gp_Pnt&);
|
||||
bool project(const Handle_Geom_Curve&, const gp_Pnt&, gp_Pnt& p, double& u, double& d);
|
||||
bool project(const Handle_Geom_Surface&, const TopoDS_Shape&, double& u1, double& v1, double& u2, double& v2, double widen=0.1);
|
||||
static int count(const TopoDS_Shape&, TopAbs_ShapeEnum);
|
||||
|
||||
bool find_wall_end_points(const IfcSchema::IfcWall*, gp_Pnt& start, gp_Pnt& end);
|
||||
|
||||
IfcSchema::IfcSurfaceStyleShading* get_surface_style(IfcSchema::IfcRepresentationItem* item);
|
||||
const IfcSchema::IfcRepresentationItem* find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item);
|
||||
bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& solid);
|
||||
bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid);
|
||||
bool is_compound(const TopoDS_Shape& shape);
|
||||
bool is_convex(const TopoDS_Wire& wire);
|
||||
TopoDS_Shape halfspace_from_plane(const gp_Pln& pln,const gp_Pnt& cent);
|
||||
@@ -117,68 +240,118 @@ public:
|
||||
gp_Pnt point_above_plane(const gp_Pln& pln, bool agree=true);
|
||||
const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid);
|
||||
bool profile_helper(int numVerts, double* verts, int numFillets, int* filletIndices, double* filletRadii, gp_Trsf2d trsf, TopoDS_Shape& face);
|
||||
double shape_volume(const TopoDS_Shape& s);
|
||||
double face_area(const TopoDS_Face& f);
|
||||
void apply_tolerance(TopoDS_Shape& s, double t);
|
||||
void setValue(GeomValue var, double value);
|
||||
double getValue(GeomValue var);
|
||||
double getValue(GeomValue var) const;
|
||||
bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape);
|
||||
void remove_redundant_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.);
|
||||
void remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.);
|
||||
void remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.);
|
||||
bool wire_to_sequence_of_point(const TopoDS_Wire&, TColgp_SequenceOfPnt&);
|
||||
void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed);
|
||||
bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&);
|
||||
bool flatten_wire(TopoDS_Wire&);
|
||||
bool triangulate_wire(const TopoDS_Wire&, TopTools_ListOfShape&);
|
||||
bool wire_intersections(const TopoDS_Wire & wire, TopTools_ListOfShape & wires);
|
||||
void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest);
|
||||
|
||||
static double shape_volume(const TopoDS_Shape& s);
|
||||
static double face_area(const TopoDS_Face& f);
|
||||
|
||||
static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&);
|
||||
static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&);
|
||||
|
||||
bool is_identity_transform(IfcUtil::IfcBaseClass*);
|
||||
|
||||
IfcSchema::IfcRelVoidsElement::list::ptr find_openings(IfcSchema::IfcProduct* product);
|
||||
|
||||
IfcSchema::IfcRepresentation* find_representation(const IfcSchema::IfcProduct*, const std::string&);
|
||||
|
||||
std::pair<std::string, double> initializeUnits(IfcSchema::IfcUnitAssignment*);
|
||||
|
||||
IfcSchema::IfcObjectDefinition* get_decomposing_entity(IfcSchema::IfcProduct*);
|
||||
static IfcSchema::IfcObjectDefinition* get_decomposing_entity(IfcSchema::IfcProduct*, bool include_openings=true);
|
||||
|
||||
static std::map<std::string, IfcSchema::IfcPresentationLayerAssignment*> get_layers(IfcSchema::IfcProduct* prod);
|
||||
|
||||
template <typename P>
|
||||
IfcGeom::BRepElement<P>* create_brep_for_representation_and_product(const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*);
|
||||
IfcGeom::BRepElement<P>* create_brep_for_representation_and_product(
|
||||
const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*);
|
||||
|
||||
const SurfaceStyle* get_style(const IfcSchema::IfcRepresentationItem* representation_item);
|
||||
template <typename P>
|
||||
IfcGeom::BRepElement<P>* create_brep_for_processed_representation(
|
||||
const IteratorSettings&, IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*, IfcGeom::BRepElement<P>*);
|
||||
|
||||
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> get_surface_style(const IfcSchema::IfcRepresentationItem* representation_item) {
|
||||
IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem();
|
||||
for (IfcSchema::IfcStyledItem::list::it jt = styled_items->begin(); jt != styled_items->end(); ++jt) {
|
||||
const IfcSchema::IfcMaterial* get_single_material_association(const IfcSchema::IfcProduct*);
|
||||
IfcSchema::IfcRepresentation* representation_mapped_to(const IfcSchema::IfcRepresentation* representation);
|
||||
IfcSchema::IfcProduct::list::ptr products_represented_by(const IfcSchema::IfcRepresentation*);
|
||||
const SurfaceStyle* get_style(const IfcSchema::IfcRepresentationItem*);
|
||||
const SurfaceStyle* get_style(const IfcSchema::IfcMaterial*);
|
||||
|
||||
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> _get_surface_style(const IfcSchema::IfcStyledItem* si) {
|
||||
#ifdef USE_IFC4
|
||||
IfcUtil::IfcAbstractSelect::list::ptr style_assignments = (*jt)->Styles();
|
||||
for (IfcUtil::IfcAbstractSelect::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
if (!(*kt)->is(IfcSchema::Type::IfcPresentationStyleAssignment)) {
|
||||
continue;
|
||||
}
|
||||
IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt;
|
||||
IfcEntityList::ptr style_assignments = si->Styles();
|
||||
for (IfcEntityList::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
if (!(*kt)->is(IfcSchema::Type::IfcPresentationStyleAssignment)) {
|
||||
continue;
|
||||
}
|
||||
IfcSchema::IfcPresentationStyleAssignment* style_assignment = (IfcSchema::IfcPresentationStyleAssignment*) *kt;
|
||||
#else
|
||||
IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = (*jt)->Styles();
|
||||
for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt;
|
||||
IfcSchema::IfcPresentationStyleAssignment::list::ptr style_assignments = si->Styles();
|
||||
for (IfcSchema::IfcPresentationStyleAssignment::list::it kt = style_assignments->begin(); kt != style_assignments->end(); ++kt) {
|
||||
IfcSchema::IfcPresentationStyleAssignment* style_assignment = *kt;
|
||||
#endif
|
||||
IfcEntityList::ptr styles = style_assignment->Styles();
|
||||
for (IfcEntityList::it lt = styles->begin(); lt != styles->end(); ++lt) {
|
||||
IfcUtil::IfcBaseClass* style = *lt;
|
||||
if (style->is(IfcSchema::Type::IfcSurfaceStyle)) {
|
||||
IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style;
|
||||
if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) {
|
||||
IfcEntityList::ptr styles_elements = surface_style->Styles();
|
||||
for (IfcEntityList::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) {
|
||||
if ((*mt)->is(T::Class())) {
|
||||
return std::make_pair(surface_style, (T*) *mt);
|
||||
}
|
||||
IfcEntityList::ptr styles = style_assignment->Styles();
|
||||
for (IfcEntityList::it lt = styles->begin(); lt != styles->end(); ++lt) {
|
||||
IfcUtil::IfcBaseClass* style = *lt;
|
||||
if (style->is(IfcSchema::Type::IfcSurfaceStyle)) {
|
||||
IfcSchema::IfcSurfaceStyle* surface_style = (IfcSchema::IfcSurfaceStyle*) style;
|
||||
if (surface_style->Side() != IfcSchema::IfcSurfaceSide::IfcSurfaceSide_NEGATIVE) {
|
||||
IfcEntityList::ptr styles_elements = surface_style->Styles();
|
||||
for (IfcEntityList::it mt = styles_elements->begin(); mt != styles_elements->end(); ++mt) {
|
||||
if ((*mt)->is(T::Class())) {
|
||||
return std::make_pair(surface_style, (T*) *mt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StyledByItem is a SET [0:1] OF IfcStyledItem, so we
|
||||
// break after encountering the first IfcStyledItem
|
||||
break;
|
||||
}
|
||||
|
||||
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
|
||||
}
|
||||
|
||||
template <typename T> std::pair<IfcSchema::IfcSurfaceStyle*, T*> get_surface_style(const IfcSchema::IfcRepresentationItem* representation_item) {
|
||||
// For certain representation items, most notably boolean operands,
|
||||
// a style definition might reside on one of its operands.
|
||||
representation_item = find_item_carrying_style(representation_item);
|
||||
|
||||
if (representation_item->as<IfcSchema::IfcStyledItem>()) {
|
||||
return _get_surface_style<T>(representation_item->as<IfcSchema::IfcStyledItem>());
|
||||
}
|
||||
IfcSchema::IfcStyledItem::list::ptr styled_items = representation_item->StyledByItem();
|
||||
if (styled_items->size()) {
|
||||
// StyledByItem is a SET [0:1] OF IfcStyledItem, so we return after the first IfcStyledItem:
|
||||
return _get_surface_style<T>(*styled_items->begin());
|
||||
}
|
||||
return std::make_pair<IfcSchema::IfcSurfaceStyle*, T*>(0,0);
|
||||
}
|
||||
|
||||
void purge_cache() {
|
||||
// Rather hack-ish, but a stopgap solution to keep memory under control
|
||||
// for large files. SurfaceStyles need to be kept at all costs, as they
|
||||
// are read later on when serializing Collada files.
|
||||
#ifndef NO_CACHE
|
||||
cache = Cache();
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_conversion_placement_rel_to(IfcSchema::Type::Enum type);
|
||||
|
||||
#include "IfcRegisterGeomHeader.h"
|
||||
|
||||
};
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* tesselate(TopoDS_Shape& shape, double deflection, IfcEntityList::ptr es);
|
||||
IFC_GEOM_API IfcSchema::IfcProductDefinitionShape* tesselate(const TopoDS_Shape& shape, double deflection);
|
||||
IFC_GEOM_API IfcSchema::IfcProductDefinitionShape* serialise(const TopoDS_Shape& shape, bool advanced);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
|
||||
#include <TopLoc_Location.hxx>
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#endif
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircle* l, Handle(Geom_Curve)& curve) {
|
||||
@@ -135,4 +139,58 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLine* l, Handle(Geom_Curve)& c
|
||||
// See note at IfcGeomWires.cpp:237
|
||||
curve = new Geom_Line(pnt,vec);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineCurveWithKnots* l, Handle(Geom_Curve)& curve) {
|
||||
|
||||
const bool is_rational = l->is(IfcSchema::Type::IfcRationalBSplineCurveWithKnots);
|
||||
|
||||
const IfcSchema::IfcCartesianPoint::list::ptr cps = l->ControlPointsList();
|
||||
const std::vector<int> mults = l->KnotMultiplicities();
|
||||
const std::vector<double> knots = l->Knots();
|
||||
|
||||
TColgp_Array1OfPnt Poles(0, cps->size() - 1);
|
||||
TColStd_Array1OfReal Weights(0, cps->size() - 1);
|
||||
TColStd_Array1OfReal Knots(0, (int)knots.size() - 1);
|
||||
TColStd_Array1OfInteger Mults(0, (int)mults.size() - 1);
|
||||
Standard_Integer Degree = l->Degree();
|
||||
Standard_Boolean Periodic = l->ClosedCurve();
|
||||
|
||||
int i;
|
||||
|
||||
if (is_rational) {
|
||||
IfcSchema::IfcRationalBSplineCurveWithKnots* rl = (IfcSchema::IfcRationalBSplineCurveWithKnots*)l;
|
||||
std::vector<double> weights = rl->WeightsData();
|
||||
|
||||
i = 0;
|
||||
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it, ++i) {
|
||||
Weights(i) = *it;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (IfcSchema::IfcCartesianPoint::list::it it = cps->begin(); it != cps->end(); ++it, ++i) {
|
||||
gp_Pnt pnt;
|
||||
if (!convert(*it, pnt)) return false;
|
||||
Poles(i) = pnt;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (std::vector<int>::const_iterator it = mults.begin(); it != mults.end(); ++it, ++i) {
|
||||
Mults(i) = *it;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (std::vector<double>::const_iterator it = knots.begin(); it != knots.end(); ++it, ++i) {
|
||||
Knots(i) = *it;
|
||||
}
|
||||
|
||||
if (is_rational) {
|
||||
curve = new Geom_BSplineCurve(Poles, Weights, Knots, Mults, Degree, Periodic);
|
||||
} else {
|
||||
curve = new Geom_BSplineCurve(Poles, Knots, Mults, Degree, Periodic);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -20,9 +20,14 @@
|
||||
#ifndef IFCGEOMELEMENT_H
|
||||
#define IFCGEOMELEMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../ifcparse/IfcGlobalId.h"
|
||||
|
||||
#include "../ifcgeom/IfcGeomRepresentation.h"
|
||||
#include "../ifcgeom/IfcGeomIteratorSettings.h"
|
||||
|
||||
#include "ifc_geom_api.h"
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
@@ -40,7 +45,7 @@ namespace IfcGeom {
|
||||
for(int i = 1; i < 5; ++i) {
|
||||
for (int j = 1; j < 4; ++j) {
|
||||
const double trsf_value = trsf.Value(j,i);
|
||||
const double matrix_value = i == 4 && settings.convert_back_units()
|
||||
const double matrix_value = i == 4 && settings.get(IteratorSettings::CONVERT_BACK_UNITS)
|
||||
? trsf_value / settings.unit_magnitude()
|
||||
: trsf_value;
|
||||
_data.push_back(static_cast<P>(matrix_value));
|
||||
@@ -53,15 +58,25 @@ namespace IfcGeom {
|
||||
template <typename P>
|
||||
class Transformation {
|
||||
private:
|
||||
gp_Trsf trsf;
|
||||
Matrix<P> _matrix;
|
||||
ElementSettings settings_;
|
||||
gp_Trsf trsf_;
|
||||
Matrix<P> matrix_;
|
||||
public:
|
||||
Transformation(const ElementSettings& settings, const gp_Trsf& trsf)
|
||||
: trsf(trsf)
|
||||
, _matrix(settings, trsf)
|
||||
: settings_(settings)
|
||||
, trsf_(trsf)
|
||||
, matrix_(settings, trsf)
|
||||
{}
|
||||
const gp_Trsf& data() const { return trsf; }
|
||||
const Matrix<P>& matrix() const { return _matrix; }
|
||||
const gp_Trsf& data() const { return trsf_; }
|
||||
const Matrix<P>& matrix() const { return matrix_; }
|
||||
|
||||
Transformation inverted() const {
|
||||
return Transformation(settings_, trsf_.Inverted());
|
||||
}
|
||||
|
||||
Transformation multiplied(const Transformation& other) const {
|
||||
return Transformation(settings_, trsf_.Multiplied(other.data()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
@@ -72,33 +87,92 @@ namespace IfcGeom {
|
||||
std::string _name;
|
||||
std::string _type;
|
||||
std::string _guid;
|
||||
std::string _context;
|
||||
std::string _unique_id;
|
||||
Transformation<P> _transformation;
|
||||
IfcSchema::IfcProduct* product_;
|
||||
std::vector<const IfcGeom::Element<P>*> _parents;
|
||||
public:
|
||||
|
||||
friend bool operator == (const Element<P> & element1, const Element<P> & element2)
|
||||
{
|
||||
return element1.id() == element2.id();
|
||||
}
|
||||
|
||||
// Use the id to compare, or the elevation is the elements are IfcBuildingStoreys and the elevation is set
|
||||
friend bool operator < (const Element<P> & element1, const Element<P> & element2)
|
||||
{
|
||||
if (element1.type() == "IfcBuildingStorey" && element2.type() == "IfcBuildingStorey")
|
||||
{
|
||||
IfcSchema::IfcBuildingStorey* storey1 = NULL;
|
||||
IfcSchema::IfcBuildingStorey* storey2 = NULL;
|
||||
|
||||
storey1 = (IfcSchema::IfcBuildingStorey*)element1.product();
|
||||
storey2 = (IfcSchema::IfcBuildingStorey*)element2.product();
|
||||
|
||||
if (storey1 != NULL && storey2 != NULL && storey1->hasElevation() && storey2->hasElevation())
|
||||
{
|
||||
return storey1->Elevation() < storey2->Elevation();
|
||||
}
|
||||
}
|
||||
return element1.id() < element2.id();
|
||||
}
|
||||
|
||||
int id() const { return _id; }
|
||||
int parent_id() const { return _parent_id; }
|
||||
const std::string& name() const { return _name; }
|
||||
const std::string& type() const { return _type; }
|
||||
const std::string& guid() const { return _guid; }
|
||||
const std::string& context() const { return _context; }
|
||||
const std::string& unique_id() const { return _unique_id; }
|
||||
const Transformation<P>& transformation() const { return _transformation; }
|
||||
Element(const ElementSettings& settings, int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid, const gp_Trsf& trsf)
|
||||
: _id(id), _parent_id(parent_id), _name(name), _type(type), _guid(guid), _transformation(settings, trsf)
|
||||
{}
|
||||
IfcSchema::IfcProduct* product() const { return product_; }
|
||||
const std::vector<const IfcGeom::Element<P>*> parents() const { return _parents; }
|
||||
void SetParents(std::vector<const IfcGeom::Element<P>*> newparents) { _parents = newparents; }
|
||||
|
||||
Element(const ElementSettings& settings, int id, int parent_id, const std::string& name, const std::string& type,
|
||||
const std::string& guid, const std::string& context, const gp_Trsf& trsf, IfcSchema::IfcProduct *product)
|
||||
: _id(id), _parent_id(parent_id), _name(name), _type(type), _guid(guid), _context(context), _transformation(settings, trsf)
|
||||
, product_(product)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
if (type == "IfcProject") {
|
||||
oss << "project";
|
||||
} else {
|
||||
try {
|
||||
oss << "product-" << IfcParse::IfcGlobalId(guid).formatted();
|
||||
} catch (const std::exception& e) {
|
||||
oss << "product";
|
||||
Logger::Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_context.empty()) {
|
||||
std::string ctx = _context;
|
||||
boost::to_lower(ctx);
|
||||
boost::replace_all(ctx, " ", "-");
|
||||
oss << "-" << ctx;
|
||||
}
|
||||
|
||||
_unique_id = oss.str();
|
||||
}
|
||||
virtual ~Element() {}
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
class BRepElement : public Element<P> {
|
||||
private:
|
||||
Representation::BRep* _geometry;
|
||||
boost::shared_ptr<Representation::BRep> _geometry;
|
||||
public:
|
||||
const boost::shared_ptr<Representation::BRep>& geometry_pointer() const { return _geometry; }
|
||||
const Representation::BRep& geometry() const { return *_geometry; }
|
||||
BRepElement(int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid, const gp_Trsf& trsf, Representation::BRep* geometry)
|
||||
: Element<P>(geometry->settings(),id,parent_id,name,type,guid,trsf)
|
||||
BRepElement(int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid,
|
||||
const std::string& context, const gp_Trsf& trsf, const boost::shared_ptr<Representation::BRep>& geometry,
|
||||
IfcSchema::IfcProduct* product)
|
||||
: Element<P>(geometry->settings(),id,parent_id,name,type,guid,context,trsf, product)
|
||||
, _geometry(geometry)
|
||||
{}
|
||||
virtual ~BRepElement() {
|
||||
delete _geometry;
|
||||
}
|
||||
private:
|
||||
BRepElement(const BRepElement& other);
|
||||
BRepElement& operator=(const BRepElement& other);
|
||||
@@ -107,16 +181,18 @@ namespace IfcGeom {
|
||||
template <typename P>
|
||||
class TriangulationElement : public Element<P> {
|
||||
private:
|
||||
Representation::Triangulation<P>* _geometry;
|
||||
boost::shared_ptr< Representation::Triangulation<P> > _geometry;
|
||||
public:
|
||||
const Representation::Triangulation<P>& geometry() const { return *_geometry; }
|
||||
const boost::shared_ptr< Representation::Triangulation<P> >& geometry_pointer() const { return _geometry; }
|
||||
TriangulationElement(const BRepElement<P>& shape_model)
|
||||
: Element<P>(shape_model)
|
||||
, _geometry(new Representation::Triangulation<P>(shape_model.geometry()))
|
||||
, _geometry(boost::shared_ptr<Representation::Triangulation<P> >(new Representation::Triangulation<P>(shape_model.geometry())))
|
||||
{}
|
||||
TriangulationElement(const Element<P>& element, const boost::shared_ptr<Representation::Triangulation<P> >& geometry)
|
||||
: Element<P>(element)
|
||||
, _geometry(geometry)
|
||||
{}
|
||||
virtual ~TriangulationElement() {
|
||||
delete _geometry;
|
||||
}
|
||||
private:
|
||||
TriangulationElement(const TriangulationElement& other);
|
||||
TriangulationElement& operator=(const TriangulationElement& other);
|
||||
@@ -141,4 +217,4 @@ namespace IfcGeom {
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+484
-219
@@ -47,6 +47,7 @@
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <Geom_TrimmedCurve.hxx>
|
||||
@@ -66,13 +67,16 @@
|
||||
#include <BRepBuilderAPI_MakeShell.hxx>
|
||||
#include <BRepBuilderAPI_MakeSolid.hxx>
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS_Iterator.hxx>
|
||||
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
|
||||
#include <ShapeFix_Edge.hxx>
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
@@ -83,159 +87,360 @@
|
||||
#include <Standard_Failure.hxx>
|
||||
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepCheck_Face.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include <TopTools_DataMapOfShapeInteger.hxx>
|
||||
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
#include <TColgp_Array2OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <BRepCheck_Face.hxx>
|
||||
#endif
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) {
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds();
|
||||
IfcSchema::IfcFaceBound::list::it it = bounds->begin();
|
||||
IfcSchema::IfcLoop* loop = (*it)->Bound();
|
||||
TopoDS_Wire outer_wire;
|
||||
if ( ! convert_wire(loop,outer_wire) ) return false;
|
||||
BRepBuilderAPI_MakeFace mf (outer_wire);
|
||||
BRepBuilderAPI_FaceError er = mf.Error();
|
||||
if ( er == BRepBuilderAPI_NotPlanar ) {
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(outer_wire, 0.01, TopAbs_WIRE);
|
||||
mf.~BRepBuilderAPI_MakeFace();
|
||||
new (&mf) BRepBuilderAPI_MakeFace(outer_wire);
|
||||
er = mf.Error();
|
||||
}
|
||||
if ( er != BRepBuilderAPI_FaceDone ) return false;
|
||||
if ( bounds->size() == 1 ) {
|
||||
face = mf.Face();
|
||||
} else {
|
||||
for( ++it; it != bounds->end(); ++ it) {
|
||||
IfcSchema::IfcLoop* loop = (*it)->Bound();
|
||||
TopoDS_Wire wire;
|
||||
if ( ! convert_wire(loop,wire) ) return false;
|
||||
mf.Add(wire);
|
||||
}
|
||||
if ( mf.IsDone() ) {
|
||||
ShapeFix_Shape sfs(mf.Face());
|
||||
sfs.Perform();
|
||||
TopoDS_Shape sfs_shape = sfs.Shape();
|
||||
bool is_face = sfs_shape.ShapeType() == TopAbs_FACE;
|
||||
if ( is_face ) {
|
||||
face = TopoDS::Face(sfs_shape);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail on this early as it can cause issues later on
|
||||
if (bounds->size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( getValue(GV_FORCE_CCW_FACE_ORIENTATION)>0 ) {
|
||||
// Check the orientation of the face by comparing the
|
||||
// normal of the topological surface to the Newell's Method's
|
||||
// normal. Newell's Method is used for the normal calculation
|
||||
// as a simple edge cross product can give opposite results
|
||||
// for a concave face boundary.
|
||||
// Reference: Graphics Gems III p. 231
|
||||
BRepGProp_Face prop(TopoDS::Face(face));
|
||||
gp_Vec normal_direction;
|
||||
gp_Pnt center;
|
||||
double u1,u2,v1,v2;
|
||||
prop.Bounds(u1,u2,v1,v2);
|
||||
prop.Normal((u1+u2)/2.0,(v1+v2)/2.0,center,normal_direction);
|
||||
gp_Dir face_normal1 = gp_Dir(normal_direction.XYZ());
|
||||
Handle(Geom_Surface) face_surface;
|
||||
const bool is_face_surface = l->is(IfcSchema::Type::IfcFaceSurface);
|
||||
|
||||
double x = 0, y = 0, z = 0;
|
||||
gp_Pnt current, previous, first;
|
||||
int n = 0;
|
||||
// Iterate over the vertices of the outer wire (discarding
|
||||
// any potential holes)
|
||||
for ( TopExp_Explorer exp(outer_wire,TopAbs_VERTEX);; exp.Next()) {
|
||||
unsigned has_more = exp.More();
|
||||
if ( has_more ) {
|
||||
const TopoDS_Vertex& v = TopoDS::Vertex(exp.Current());
|
||||
current = BRep_Tool::Pnt(v);
|
||||
} else {
|
||||
current = first;
|
||||
}
|
||||
if ( n ) {
|
||||
const double& xn = previous.X();
|
||||
const double& yn = previous.Y();
|
||||
const double& zn = previous.Z();
|
||||
const double& xn1 = current.X();
|
||||
const double& yn1 = current.Y();
|
||||
const double& zn1 = current.Z();
|
||||
x += (yn-yn1)*(zn+zn1);
|
||||
y += (xn+xn1)*(zn-zn1);
|
||||
z += (xn-xn1)*(yn+yn1);
|
||||
} else {
|
||||
first = current;
|
||||
}
|
||||
if ( !has_more ) {
|
||||
break;
|
||||
}
|
||||
previous = current;
|
||||
++n;
|
||||
}
|
||||
if (is_face_surface) {
|
||||
IfcSchema::IfcFaceSurface* fs = (IfcSchema::IfcFaceSurface*) l;
|
||||
fs->FaceSurface();
|
||||
// FIXME: Surfaces are interpreted as a TopoDS_Shape
|
||||
TopoDS_Shape surface_shape;
|
||||
if (!convert_shape(fs->FaceSurface(), surface_shape)) return false;
|
||||
|
||||
// If Newell's normal does not point in the same direction
|
||||
// as the topological face normal the face orientation is
|
||||
// reversed
|
||||
gp_Vec face_normal2(x,y,z);
|
||||
// FIXME: Assert this obtaines the only face
|
||||
TopExp_Explorer exp(surface_shape, TopAbs_FACE);
|
||||
if (!exp.More()) return false;
|
||||
|
||||
if (face_normal2.Magnitude() > ALMOST_ZERO) {
|
||||
if ( face_normal1.Dot(face_normal2) < 0 ) {
|
||||
TopAbs_Orientation o = face.Orientation();
|
||||
face.Orientation(o == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD);
|
||||
}
|
||||
}
|
||||
TopoDS_Face surface = TopoDS::Face(exp.Current());
|
||||
face_surface = BRep_Tool::Surface(surface);
|
||||
}
|
||||
|
||||
// It might be a good idea to globally discard faces
|
||||
// smaller than a certain treshold value. But for now
|
||||
// only when processing IfcConnectedFacesets the small
|
||||
// faces are skipped.
|
||||
// return face_area(face) > 0.0001;
|
||||
return true;
|
||||
const int num_bounds = bounds->size();
|
||||
int num_outer_bounds = 0;
|
||||
|
||||
for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) {
|
||||
IfcSchema::IfcFaceBound* bound = *it;
|
||||
if (bound->is(IfcSchema::Type::IfcFaceOuterBound)) num_outer_bounds ++;
|
||||
}
|
||||
|
||||
// The number of outer bounds should be one according to the schema. Also Open Cascade
|
||||
// expects this, but it is not strictly checked. Regardless, if the number is greater,
|
||||
// the face will still be processed as long as there are no holes. A compound of faces
|
||||
// is returned in that case.
|
||||
if (num_bounds > 1 && num_outer_bounds > 1 && num_bounds != num_outer_bounds) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Invalid configuration of boundaries for:", l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
if (num_outer_bounds > 1) {
|
||||
builder.MakeCompound(compound);
|
||||
}
|
||||
|
||||
TopTools_DataMapOfShapeInteger wire_senses;
|
||||
|
||||
// The builder is initialized on the heap because of the various different moments
|
||||
// of initialization depending on the configuration of surfaces and boundaries.
|
||||
BRepBuilderAPI_MakeFace* mf = 0;
|
||||
|
||||
bool success = false;
|
||||
int processed = 0;
|
||||
|
||||
for (int process_interior = 0; process_interior <= 1; ++process_interior) {
|
||||
for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) {
|
||||
IfcSchema::IfcFaceBound* bound = *it;
|
||||
IfcSchema::IfcLoop* loop = bound->Bound();
|
||||
|
||||
bool same_sense = bound->Orientation();
|
||||
const bool is_interior =
|
||||
!bound->is(IfcSchema::Type::IfcFaceOuterBound) &&
|
||||
(num_bounds > 1) &&
|
||||
(num_outer_bounds < num_bounds);
|
||||
|
||||
// The exterior face boundary is processed first
|
||||
if (is_interior == !process_interior) continue;
|
||||
|
||||
TopoDS_Wire wire;
|
||||
if (!convert_wire(loop, wire)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary loop", loop->entity);
|
||||
delete mf;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
The approach below does not result in a significant speed-up
|
||||
if (loop->is(IfcSchema::Type::IfcPolyLoop) && processed == 0 && face_surface.IsNull()) {
|
||||
IfcSchema::IfcPolyLoop* polyloop = (IfcSchema::IfcPolyLoop*) loop;
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points = polyloop->Polygon();
|
||||
|
||||
if (points->size() == 3) {
|
||||
// Help Open Cascade by finding the plane more efficiently
|
||||
IfcSchema::IfcCartesianPoint::list::it point_iterator = points->begin();
|
||||
gp_Pnt a, b, c;
|
||||
convert(*point_iterator++, a);
|
||||
convert(*point_iterator++, b);
|
||||
convert(*point_iterator++, c);
|
||||
const gp_XYZ ab = (b.XYZ() - a.XYZ());
|
||||
const gp_XYZ ac = (c.XYZ() - a.XYZ());
|
||||
const gp_Vec cross = ab.Crossed(ac);
|
||||
if (cross.SquareMagnitude() > ALMOST_ZERO) {
|
||||
const gp_Dir n = cross;
|
||||
face_surface = new Geom_Plane(a, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!same_sense) {
|
||||
wire.Reverse();
|
||||
}
|
||||
|
||||
wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED);
|
||||
|
||||
bool flattened_wire = false;
|
||||
|
||||
if (!mf) {
|
||||
process_wire:
|
||||
|
||||
if (face_surface.IsNull()) {
|
||||
gp_Pln pln;
|
||||
if (count(wire, TopAbs_EDGE) > 128 && approximate_plane_through_wire(wire, pln)) {
|
||||
// tfk: optimization find the underlying surface ourselves since it's going
|
||||
// to be planar in IFC if no explicit surface is given. Should we always do this?
|
||||
mf = new BRepBuilderAPI_MakeFace(pln, wire, true);
|
||||
} else {
|
||||
mf = new BRepBuilderAPI_MakeFace(wire);
|
||||
}
|
||||
} else {
|
||||
/// @todo check necessity of false here
|
||||
mf = new BRepBuilderAPI_MakeFace(face_surface, wire, false);
|
||||
}
|
||||
|
||||
/* BRepBuilderAPI_FaceError er = mf->Error();
|
||||
if (er == BRepBuilderAPI_NotPlanar) {
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE);
|
||||
delete mf;
|
||||
mf = new BRepBuilderAPI_MakeFace(wire);
|
||||
} */
|
||||
|
||||
if (mf->IsDone()) {
|
||||
TopoDS_Face outer_face_bound = mf->Face();
|
||||
|
||||
// In case of (non-planar) face surface, p-curves need to be computed.
|
||||
// For planar faces, Open Cascade generates p-curves on the fly.
|
||||
if (!face_surface.IsNull() && face_surface->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
TopExp_Explorer exp(outer_face_bound, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(exp.Current());
|
||||
ShapeFix_Edge fix_edge;
|
||||
fix_edge.FixAddPCurve(edge, outer_face_bound, false, getValue(GV_PRECISION));
|
||||
}
|
||||
}
|
||||
|
||||
if (BRepCheck_Face(outer_face_bound).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) {
|
||||
wire.Reverse();
|
||||
same_sense = !same_sense;
|
||||
delete mf;
|
||||
if (face_surface.IsNull()) {
|
||||
mf = new BRepBuilderAPI_MakeFace(wire);
|
||||
} else {
|
||||
mf = new BRepBuilderAPI_MakeFace(face_surface, wire);
|
||||
}
|
||||
ShapeFix_Face fix(mf->Face());
|
||||
fix.FixOrientation();
|
||||
outer_face_bound = fix.Face();
|
||||
}
|
||||
|
||||
if (num_outer_bounds > 1) {
|
||||
builder.Add(compound, outer_face_bound);
|
||||
delete mf; mf = 0;
|
||||
} else if (num_bounds > 1) {
|
||||
// Reinitialize the builder to the outer face
|
||||
// bound in order to add holes more robustly.
|
||||
delete mf;
|
||||
// TODO: What about the face_surface?
|
||||
mf = new BRepBuilderAPI_MakeFace(outer_face_bound);
|
||||
} else {
|
||||
face = outer_face_bound;
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
const bool non_planar = mf->Error() == BRepBuilderAPI_NotPlanar;
|
||||
delete mf;
|
||||
|
||||
const bool sewing_shells = getValue(GV_MAX_FACES_TO_SEW) > -1;
|
||||
|
||||
if (non_planar && sewing_shells && bounds->size() == 1 && face_surface.IsNull()) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Triangulating face boundary", bound->entity);
|
||||
|
||||
// When creating a solid, flatting the boundary only postpones the issue to
|
||||
// creating a topological manifold out of the individual faces.
|
||||
TopTools_ListOfShape face_list;
|
||||
triangulate_wire(wire, face_list);
|
||||
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compound);
|
||||
|
||||
TopTools_ListIteratorOfListOfShape face_iterator;
|
||||
for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) {
|
||||
builder.Add(compound, face_iterator.Value());
|
||||
}
|
||||
|
||||
face = compound;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!non_planar || flattened_wire || !flatten_wire(wire)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound->entity);
|
||||
return false;
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_ERROR, "Flattening face boundary", bound->entity);
|
||||
flattened_wire = true;
|
||||
goto process_wire;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
mf->Add(wire);
|
||||
|
||||
// Same as above:
|
||||
// In case of (non-planar) face surface, p-curves need to be computed.
|
||||
if (BRep_Tool::Surface(mf->Face())->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(exp.Current());
|
||||
ShapeFix_Edge fix_edge;
|
||||
fix_edge.FixAddPCurve(edge, mf->Face(), false, getValue(GV_PRECISION));
|
||||
}
|
||||
}
|
||||
}
|
||||
processed ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
success = processed == num_bounds;
|
||||
if (success) {
|
||||
if (num_outer_bounds > 1) {
|
||||
face = compound;
|
||||
} else {
|
||||
success = success && mf->IsDone();
|
||||
if (success) {
|
||||
face = mf->Face();
|
||||
}
|
||||
|
||||
ShapeFix_Face sfs(TopoDS::Face(face));
|
||||
TopTools_DataMapOfShapeListOfShape wire_map;
|
||||
sfs.FixOrientation(wire_map);
|
||||
|
||||
TopoDS_Iterator jt(face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||
// tfk: @todo if wire_map contains w, I would assume wire_senses also contains w,
|
||||
// this is not the case in github issue #405.
|
||||
if (wire_map.IsBound(w) && wire_senses.IsBound(w)) {
|
||||
const TopTools_ListOfShape& shapes = wire_map.Find(w);
|
||||
TopTools_ListIteratorOfListOfShape it(shapes);
|
||||
for (; it.More(); it.Next()) {
|
||||
// Apparently the wire got reversed, so register it with opposite orientation in the map
|
||||
wire_senses.Bind(it.Value(), wire_senses.Find(w) == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
face = TopoDS::Face(sfs.Face());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// If the wires are reversed the face needs to be reversed as well in order
|
||||
// to maintain the counter-clock-wise ordering of the bounding wire's vertices.
|
||||
if (num_bounds == 1 || true) {
|
||||
bool all_reversed = true;
|
||||
TopoDS_Iterator jt(face, false);
|
||||
for (; jt.More(); jt.Next()) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(jt.Value());
|
||||
if (!wire_senses.IsBound(w.Oriented(TopAbs_FORWARD)) || (w.Orientation() == wire_senses.Find(w.Oriented(TopAbs_FORWARD)))) {
|
||||
all_reversed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_reversed) {
|
||||
face.Reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete mf;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, TopoDS_Shape& face) {
|
||||
TopoDS_Wire wire;
|
||||
if ( ! convert_wire(l->OuterCurve(),wire) ) return false;
|
||||
if (!convert_wire(l->OuterCurve(), wire)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert_closed_wire(wire);
|
||||
|
||||
TopoDS_Face f;
|
||||
bool success = convert_wire_to_face(wire, f);
|
||||
if (success) face = f;
|
||||
if (success) {
|
||||
face = f;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryProfileDefWithVoids* l, TopoDS_Shape& face) {
|
||||
TopoDS_Wire profile;
|
||||
if ( ! convert_wire(l->OuterCurve(),profile) ) return false;
|
||||
if (!convert_wire(l->OuterCurve(), profile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert_closed_wire(profile);
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(profile);
|
||||
|
||||
IfcSchema::IfcCurve::list::ptr voids = l->InnerCurves();
|
||||
for( IfcSchema::IfcCurve::list::it it = voids->begin(); it != voids->end(); ++ it ) {
|
||||
|
||||
for(IfcSchema::IfcCurve::list::it it = voids->begin(); it != voids->end(); ++it) {
|
||||
TopoDS_Wire hole;
|
||||
if ( convert_wire(*it,hole) ) {
|
||||
if (convert_wire(*it, hole)) {
|
||||
assert_closed_wire(hole);
|
||||
mf.Add(hole);
|
||||
}
|
||||
}
|
||||
|
||||
ShapeFix_Shape sfs(mf.Face());
|
||||
sfs.Perform();
|
||||
face = TopoDS::Face(sfs.Shape());
|
||||
face = sfs.Shape();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleProfileDef* l, TopoDS_Shape& face) {
|
||||
const double x = l->XDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
|
||||
if ( x < ALMOST_ZERO || y < ALMOST_ZERO ) {
|
||||
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity);
|
||||
@@ -243,7 +448,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleProfileDef* l, TopoDS
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[8] = {-x,-y,x,-y,x,y,-x,y};
|
||||
return profile_helper(4,coords,0,0,0,trsf2d,face);
|
||||
}
|
||||
@@ -259,7 +471,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRoundedRectangleProfileDef* l,
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[8] = {-x,-y, x,-y, x,y, -x,y};
|
||||
int fillets[4] = {0,1,2,3};
|
||||
double radii[4] = {r,r,r,r};
|
||||
@@ -271,11 +490,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l,
|
||||
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double d = l->WallThickness() * getValue(GV_LENGTH_UNIT);
|
||||
|
||||
const bool fr1 = l->OuterFilletRadius() ? true : false;
|
||||
const bool fr2 = l->InnerFilletRadius() ? true : false;
|
||||
const bool fr1 = l->hasOuterFilletRadius();
|
||||
const bool fr2 = l->hasInnerFilletRadius();
|
||||
|
||||
const double r1 = fr1 ? *l->OuterFilletRadius() * getValue(GV_LENGTH_UNIT) : 0.;
|
||||
const double r2 = fr2 ? *l->InnerFilletRadius() * getValue(GV_LENGTH_UNIT) : 0.;
|
||||
const double r1 = fr1 ? l->OuterFilletRadius() * getValue(GV_LENGTH_UNIT) : 0.;
|
||||
const double r2 = fr2 ? l->InnerFilletRadius() * getValue(GV_LENGTH_UNIT) : 0.;
|
||||
|
||||
if ( x < ALMOST_ZERO || y < ALMOST_ZERO ) {
|
||||
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity);
|
||||
@@ -286,7 +505,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l,
|
||||
TopoDS_Face f2;
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords1[8] = {-x ,-y, x ,-y, x, y, -x, y };
|
||||
double coords2[8] = {-x+d,-y+d, x-d,-y+d, x-d,y-d, -x+d,y-d};
|
||||
double radii1[4] = {r1,r1,r1,r1};
|
||||
@@ -325,7 +551,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[8] = {-x1,-y, x1,-y, dx+w-x1,y, dx-x1,y};
|
||||
return profile_helper(4,coords,0,0,0,trsf2d,face);
|
||||
}
|
||||
@@ -336,10 +569,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Sh
|
||||
const double d1 = l->WebThickness() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double dy1 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
|
||||
|
||||
bool doFillet1 = l->FilletRadius() ? true : false;
|
||||
bool doFillet1 = l->hasFilletRadius();
|
||||
double f1 = 0.;
|
||||
if ( doFillet1 ) {
|
||||
f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
|
||||
bool doFillet2 = doFillet1;
|
||||
@@ -348,12 +581,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Sh
|
||||
if (l->is(IfcSchema::Type::IfcAsymmetricIShapeProfileDef)) {
|
||||
IfcSchema::IfcAsymmetricIShapeProfileDef* assym = (IfcSchema::IfcAsymmetricIShapeProfileDef*) l;
|
||||
x2 = assym->TopFlangeWidth() / 2. * getValue(GV_LENGTH_UNIT);
|
||||
doFillet2 = assym->TopFlangeFilletRadius() ? true : false;
|
||||
doFillet2 = assym->hasTopFlangeFilletRadius();
|
||||
if (doFillet2) {
|
||||
f2 = *assym->TopFlangeFilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f2 = assym->TopFlangeFilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
if (assym->TopFlangeThickness()) {
|
||||
dy2 = *assym->TopFlangeThickness() * getValue(GV_LENGTH_UNIT);
|
||||
if (assym->hasTopFlangeThickness()) {
|
||||
dy2 = assym->TopFlangeThickness() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +596,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[24] = {-x1,-y, x1,-y, x1,-y+dy1, d1,-y+dy1, d1,y-dy2, x2,y-dy2, x2,y, -x2,y, -x2,y-dy2, -d1,y-dy2, -d1,-y+dy1, -x1,-y+dy1};
|
||||
int fillets[4] = {3,4,9,10};
|
||||
@@ -377,17 +616,17 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcZShapeProfileDef* l, TopoDS_Sh
|
||||
const double dx = l->WebThickness() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double dy = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
|
||||
|
||||
bool doFillet = l->FilletRadius() ? true : false;
|
||||
bool doEdgeFillet = l->EdgeRadius() ? true : false;
|
||||
bool doFillet = l->hasFilletRadius();
|
||||
bool doEdgeFillet = l->hasEdgeRadius();
|
||||
|
||||
double f1 = 0.;
|
||||
double f2 = 0.;
|
||||
|
||||
if ( doFillet ) {
|
||||
f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
if ( doEdgeFillet ) {
|
||||
f2 = *l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
|
||||
if ( x == 0.0f || y == 0.0f || dx == 0.0f || dy == 0.0f ) {
|
||||
@@ -396,7 +635,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcZShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[16] = {-dx,-y, x,-y, x,-y+dy, dx,-y+dy, dx,y, -x,y, -x,y-dy, -dx,y-dy};
|
||||
int fillets[4] = {2,3,6,7};
|
||||
@@ -409,11 +654,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Sh
|
||||
const double x = l->Width() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double d1 = l->WallThickness() * getValue(GV_LENGTH_UNIT);
|
||||
const double d2 = l->Girth() * getValue(GV_LENGTH_UNIT);
|
||||
bool doFillet = l->InternalFilletRadius() ? true : false;
|
||||
bool doFillet = l->hasInternalFilletRadius();
|
||||
double f1 = 0;
|
||||
double f2 = 0;
|
||||
if ( doFillet ) {
|
||||
f1 = *l->InternalFilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f1 = l->InternalFilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f2 = f1 + d1;
|
||||
}
|
||||
|
||||
@@ -423,7 +668,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
IfcGeom::Kernel::convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[24] = {-x,-y,x,-y,x,-y+d2,x-d1,-y+d2,x-d1,-y+d1,-x+d1,-y+d1,-x+d1,y-d1,x-d1,y-d1,x-d1,y-d2,x,y-d2,x,y,-x,y};
|
||||
int fillets[8] = {0,1,4,5,6,7,10,11};
|
||||
@@ -432,22 +683,22 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Shape& face) {
|
||||
const bool hasSlope = l->LegSlope() ? true : false;
|
||||
const bool doEdgeFillet = l->EdgeRadius() ? true : false;
|
||||
const bool doFillet = l->FilletRadius() ? true : false;
|
||||
const bool hasSlope = l->hasLegSlope();
|
||||
const bool doEdgeFillet = l->hasEdgeRadius();
|
||||
const bool doFillet = l->hasFilletRadius();
|
||||
|
||||
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double x = (l->Width() ? *l->Width() : l->Depth()) / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double x = (l->hasWidth() ? l->Width() : l->Depth()) / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double d = l->Thickness() * getValue(GV_LENGTH_UNIT);
|
||||
const double slope = hasSlope ? (*l->LegSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
const double slope = hasSlope ? (l->LegSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
|
||||
double f1 = 0.0f;
|
||||
double f2 = 0.0f;
|
||||
if (doFillet) {
|
||||
f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
if ( doEdgeFillet) {
|
||||
f2 = *l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
|
||||
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d < ALMOST_ZERO ) {
|
||||
@@ -492,7 +743,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[12] = {-x,-y, x,-y, x,-y+d-dy1, xx, xy, -x+d-dx1,y, -x,y};
|
||||
int fillets[3] = {2,3,4};
|
||||
@@ -501,15 +758,15 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Shape& face) {
|
||||
const bool doEdgeFillet = l->EdgeRadius() ? true : false;
|
||||
const bool doFillet = l->FilletRadius() ? true : false;
|
||||
const bool hasSlope = l->FlangeSlope() ? true : false;
|
||||
const bool doEdgeFillet = l->hasEdgeRadius();
|
||||
const bool doFillet = l->hasFilletRadius();
|
||||
const bool hasSlope = l->hasFlangeSlope();
|
||||
|
||||
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double x = l->FlangeWidth() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double d1 = l->WebThickness() * getValue(GV_LENGTH_UNIT);
|
||||
const double d2 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
|
||||
const double slope = hasSlope ? (*l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
const double slope = hasSlope ? (l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
|
||||
double dy1 = 0.0f;
|
||||
double dy2 = 0.0f;
|
||||
@@ -517,10 +774,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Sh
|
||||
double f2 = 0.0f;
|
||||
|
||||
if (doFillet) {
|
||||
f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
if (doEdgeFillet) {
|
||||
f2 = *l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f2 = l->EdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
|
||||
if (hasSlope) {
|
||||
@@ -534,7 +791,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[16] = {-x,-y, x,-y, x,-y+d2-dy2, -x+d1,-y+d2+dy1, -x+d1,y-d2-dy1, x,y-d2+dy2, x,y, -x,y};
|
||||
int fillets[4] = {2,3,4,5};
|
||||
@@ -543,18 +806,18 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Shape& face) {
|
||||
const bool doFlangeEdgeFillet = l->FlangeEdgeRadius() ? true : false;
|
||||
const bool doWebEdgeFillet = l->WebEdgeRadius() ? true : false;
|
||||
const bool doFillet = l->FilletRadius() ? true : false;
|
||||
const bool hasFlangeSlope = l->FlangeSlope() ? true : false;
|
||||
const bool hasWebSlope = l->WebSlope() ? true : false;
|
||||
const bool doFlangeEdgeFillet = l->hasFlangeEdgeRadius();
|
||||
const bool doWebEdgeFillet = l->hasWebEdgeRadius();
|
||||
const bool doFillet = l->hasFilletRadius();
|
||||
const bool hasFlangeSlope = l->hasFlangeSlope();
|
||||
const bool hasWebSlope = l->hasWebSlope();
|
||||
|
||||
const double y = l->Depth() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double x = l->FlangeWidth() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double d1 = l->WebThickness() * getValue(GV_LENGTH_UNIT);
|
||||
const double d2 = l->FlangeThickness() * getValue(GV_LENGTH_UNIT);
|
||||
const double flangeSlope = hasFlangeSlope ? (*l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
const double webSlope = hasWebSlope ? (*l->WebSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
const double flangeSlope = hasFlangeSlope ? (l->FlangeSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
const double webSlope = hasWebSlope ? (l->WebSlope() * getValue(GV_PLANEANGLE_UNIT)) : 0.;
|
||||
|
||||
if ( x < ALMOST_ZERO || y < ALMOST_ZERO || d1 < ALMOST_ZERO || d2 < ALMOST_ZERO ) {
|
||||
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l->entity);
|
||||
@@ -570,13 +833,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Sh
|
||||
double f3 = 0.0f;
|
||||
|
||||
if (doFillet) {
|
||||
f1 = *l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f1 = l->FilletRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
if (doWebEdgeFillet) {
|
||||
f2 = *l->WebEdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f2 = l->WebEdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
if (doFlangeEdgeFillet) {
|
||||
f3 = *l->FlangeEdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
f3 = l->FlangeEdgeRadius() * getValue(GV_LENGTH_UNIT);
|
||||
}
|
||||
|
||||
double xx, xy;
|
||||
@@ -617,7 +880,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Sh
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
convert(l->Position(),trsf2d);
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[16] = {d1/2.-dx2,-y, xx,xy, x,y-d2+dy2, x,y, -x,y, -x,y-d2+dy2, -xx,xy, -d1/2.+dx2,-y};
|
||||
int fillets[6] = {0,1,2,5,6,7};
|
||||
@@ -632,13 +901,21 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleProfileDef* l, TopoDS_Sh
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf;
|
||||
convert(l->Position(),trsf);
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
gp_Ax2 ax = gp_Ax2().Transformed(trsf2d);
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
gp_Ax2 ax = gp_Ax2().Transformed(trsf);
|
||||
|
||||
Handle(Geom_Circle) circle = new Geom_Circle(ax, r);
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(circle);
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
w.Add(edge);
|
||||
|
||||
TopoDS_Face f;
|
||||
@@ -656,9 +933,16 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleHollowProfileDef* l, Top
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Trsf2d trsf;
|
||||
convert(l->Position(),trsf);
|
||||
gp_Ax2 ax = gp_Ax2().Transformed(trsf);
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
gp_Ax2 ax = gp_Ax2().Transformed(trsf2d);
|
||||
|
||||
BRepBuilderAPI_MakeWire outer;
|
||||
Handle(Geom_Circle) outerCircle = new Geom_Circle(ax, r);
|
||||
@@ -686,15 +970,22 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipseProfileDef* l, TopoDS_S
|
||||
}
|
||||
|
||||
const bool rotated = ry > rx;
|
||||
gp_Trsf2d trsf;
|
||||
convert(l->Position(),trsf);
|
||||
|
||||
gp_Trsf2d trsf2d;
|
||||
bool has_position = true;
|
||||
#ifdef USE_IFC4
|
||||
has_position = l->hasPosition();
|
||||
#endif
|
||||
if (has_position) {
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
gp_Ax2 ax = gp_Ax2();
|
||||
if (rotated) {
|
||||
ax.Rotate(ax.Axis(), M_PI / 2.);
|
||||
std::swap(rx, ry);
|
||||
}
|
||||
ax.Transform(trsf);
|
||||
ax.Transform(trsf2d);
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
Handle(Geom_Ellipse) ellipse = new Geom_Ellipse(ax, rx, ry);
|
||||
@@ -764,7 +1055,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeProfileDef* l, TopoDS
|
||||
builder.MakeCompound(compound);
|
||||
|
||||
IfcSchema::IfcProfileDef::list::ptr profiles = l->Profiles();
|
||||
bool first = true;
|
||||
//bool first = true;
|
||||
for (IfcSchema::IfcProfileDef::list::it it = profiles->begin(); it != profiles->end(); ++it) {
|
||||
TopoDS_Face f;
|
||||
if (convert_face(*it, f)) {
|
||||
@@ -798,20 +1089,32 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcDerivedProfileDef* l, TopoDS_S
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* l, TopoDS_Shape& face) {
|
||||
gp_Pln pln;
|
||||
convert(l, pln);
|
||||
Handle_Geom_Surface surf = new Geom_Plane(pln);
|
||||
#if OCC_VERSION_HEX < 0x60502
|
||||
face = BRepBuilderAPI_MakeFace(surf);
|
||||
#else
|
||||
face = BRepBuilderAPI_MakeFace(surf, getValue(GV_PRECISION));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
|
||||
bool convert_surf(IfcSchema::IfcBSplineSurfaceWithKnots* l, Handle_Geom_Surface& surf) {
|
||||
SHARED_PTR< IfcTemplatedEntityListList<IfcSchema::IfcCartesianPoint> > cps = l->ControlPointsList();
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBSplineSurfaceWithKnots* l, TopoDS_Shape& face) {
|
||||
boost::shared_ptr< IfcTemplatedEntityListList<IfcSchema::IfcCartesianPoint> > cps = l->ControlPointsList();
|
||||
std::vector<double> uknots = l->UKnots();
|
||||
std::vector<double> vknots = l->VKnots();
|
||||
std::vector<int> umults = l->UMultiplicities();
|
||||
std::vector<int> vmults = l->VMultiplicities();
|
||||
|
||||
TColgp_Array2OfPnt Poles (0, cps->size() - 1, 0, (*cps->begin()).size() - 1);
|
||||
TColStd_Array1OfReal UKnots(0, uknots.size() - 1);
|
||||
TColStd_Array1OfReal VKnots(0, vknots.size() - 1);
|
||||
TColStd_Array1OfInteger UMults(0, umults.size() - 1);
|
||||
TColStd_Array1OfInteger VMults(0, vmults.size() - 1);
|
||||
TColgp_Array2OfPnt Poles (0, (int)cps->size() - 1, 0, (int)(*cps->begin()).size() - 1);
|
||||
TColStd_Array1OfReal UKnots(0, (int)uknots.size() - 1);
|
||||
TColStd_Array1OfReal VKnots(0, (int)vknots.size() - 1);
|
||||
TColStd_Array1OfInteger UMults(0, (int)umults.size() - 1);
|
||||
TColStd_Array1OfInteger VMults(0, (int)vmults.size() - 1);
|
||||
Standard_Integer UDegree = l->UDegree();
|
||||
Standard_Integer VDegree = l->VDegree();
|
||||
|
||||
@@ -841,51 +1144,13 @@ bool convert_surf(IfcSchema::IfcBSplineSurfaceWithKnots* l, Handle_Geom_Surface&
|
||||
for (std::vector<int>::const_iterator it = vmults.begin(); it != vmults.end(); ++it, ++i) {
|
||||
VMults(i) = *it;
|
||||
}
|
||||
surf = new Geom_BSplineSurface(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree);
|
||||
return true;
|
||||
}
|
||||
Handle_Geom_Surface surf = new Geom_BSplineSurface(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree);
|
||||
|
||||
bool convert_surf(IfcSchema::IfcPlane* l, Handle_Geom_Surface& surf) {
|
||||
gp_Pln pln;
|
||||
convert(l, pln);
|
||||
surf = new Geom_Plane(pln);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAdvancedFace* l, TopoDS_Shape& face) {
|
||||
IfcSchema::IfcSurface* s = l->FaceSurface();
|
||||
Handle_Geom_Surface surf(0);
|
||||
if (s->is(IfcSchema::Type::IfcBSplineSurfaceWithKnots)) {
|
||||
convert_surf((IfcSchema::IfcBSplineSurfaceWithKnots*)s, surf);
|
||||
} else if (s->is(IfcSchema::Type::IfcPlane)) {
|
||||
convert_surf((IfcSchema::IfcPlane*)s, surf);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeFace mf(surf, Precision::Confusion());
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds = l->Bounds();
|
||||
for (IfcSchema::IfcFaceBound::list::it it = bounds->begin(); it != bounds->end(); ++it) {
|
||||
IfcSchema::IfcLoop* loop = (*it)->Bound();
|
||||
TopoDS_Wire outer_wire;
|
||||
if (!convert_wire(loop, outer_wire)) return false;
|
||||
|
||||
TopoDS_Face temp = BRepBuilderAPI_MakeFace(surf, outer_wire);
|
||||
|
||||
if (BRepCheck_Face(temp).OrientationOfWires() == BRepCheck_BadOrientationOfSubshape) {
|
||||
outer_wire.Reverse();
|
||||
ShapeFix_Face fix(BRepBuilderAPI_MakeFace(surf, outer_wire).Face());
|
||||
fix.FixOrientation();
|
||||
fix.Perform();
|
||||
TopoDS_Face temp = fix.Face();
|
||||
TopExp_Explorer exp(temp, TopAbs_WIRE);
|
||||
outer_wire = TopoDS::Wire(exp.Current());
|
||||
}
|
||||
|
||||
mf.Add(outer_wire);
|
||||
}
|
||||
|
||||
face = mf.Face();
|
||||
#if OCC_VERSION_HEX < 0x60502
|
||||
face = BRepBuilderAPI_MakeFace(surf);
|
||||
#else
|
||||
face = BRepBuilderAPI_MakeFace(surf, getValue(GV_PRECISION));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
/** @file IfcGeomFilter.h
|
||||
@brief A set of predefined product filters for IfcGeom::Iterator */
|
||||
|
||||
#ifndef IFCGEOMFILTER_H
|
||||
#define IFCGEOMFILTER_H
|
||||
|
||||
#include "IfcGeom.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace IfcGeom
|
||||
{
|
||||
/// The filter function (free or member function) or function object (use boost::ref() to reference to it)
|
||||
/// should return true if the geometry for the product is wanted to be included in the output.
|
||||
/// http://www.boost.org/doc/libs/1_62_0/doc/html/function/tutorial.html
|
||||
typedef boost::function<bool(IfcSchema::IfcProduct*)> filter_t;
|
||||
|
||||
struct filter
|
||||
{
|
||||
filter() : include(false), traverse(false), traverse_openings(false) {}
|
||||
filter(bool incl, bool trav, bool trav_openings = false) : include(incl), traverse(trav), traverse_openings(trav_openings) {}
|
||||
/// Should the product be included (true) or excluded (false).
|
||||
bool include;
|
||||
/// If traversal requested, traverse to the parents to see if they satisfy the criteria. E.g. we might be looking for
|
||||
/// children of a storey named "Level 20", or children of entities that have no representation, e.g. IfcCurtainWall.
|
||||
bool traverse;
|
||||
/// Include opening relationships as part of traversal.
|
||||
bool traverse_openings;
|
||||
/// Optional description for the filtering criteria of this filter.
|
||||
std::string description;
|
||||
|
||||
bool match(IfcSchema::IfcProduct* prod, const filter_t& pred) const
|
||||
{
|
||||
bool is_match = pred(prod);
|
||||
if (!is_match && traverse) {
|
||||
is_match = traverse_match(prod, pred);
|
||||
}
|
||||
return is_match == include;
|
||||
}
|
||||
|
||||
bool traverse_match(IfcSchema::IfcProduct* prod, const filter_t& pred) const
|
||||
{
|
||||
IfcSchema::IfcProduct* parent, *current = prod;
|
||||
while ((parent = dynamic_cast<IfcSchema::IfcProduct*>(IfcGeom::Kernel::get_decomposing_entity(current, traverse_openings))) != 0) {
|
||||
if (pred(parent)) {
|
||||
return true;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct wildcard_filter : public filter
|
||||
{
|
||||
wildcard_filter() : filter(false, false) {}
|
||||
wildcard_filter(bool include, bool traverse, const std::set<std::string>& patterns)
|
||||
: filter(include, traverse)
|
||||
{
|
||||
populate(patterns);
|
||||
}
|
||||
|
||||
std::set<boost::regex> values;
|
||||
|
||||
void populate(const std::set<std::string>& patterns)
|
||||
{
|
||||
values.clear();
|
||||
BOOST_FOREACH(const std::string &pattern, patterns) {
|
||||
values.insert(wildcard_string_to_regex(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
bool match(const std::string &str) const { return match_values(values, str); }
|
||||
|
||||
static bool match_values(const std::set<boost::regex>& values, const std::string &str)
|
||||
{
|
||||
BOOST_FOREACH(const boost::regex& r, values) {
|
||||
if (boost::regex_match(str, r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boost::regex wildcard_string_to_regex(std::string str)
|
||||
{
|
||||
// Escape all non-"*?" regex special chars
|
||||
static const std::string special_chars = "\\^.$|()[]+/";
|
||||
BOOST_FOREACH(char c, special_chars) {
|
||||
std::string char_str(1, c);
|
||||
boost::replace_all(str, char_str, "\\" + char_str);
|
||||
}
|
||||
// Convert "*?" to their regex equivalents
|
||||
boost::replace_all(str, "?", ".");
|
||||
boost::replace_all(str, "*", ".*");
|
||||
return boost::regex(str);
|
||||
}
|
||||
};
|
||||
|
||||
/// @note supports only string arguments for now
|
||||
struct string_arg_filter : public wildcard_filter
|
||||
{
|
||||
// Using this for now in order to overcome the fact that different classes have the argument at different indices.
|
||||
typedef std::map<IfcSchema::Type::Enum, unsigned short> arg_map_t;
|
||||
arg_map_t args;
|
||||
|
||||
/// @todo Take only attribute name when IfcBaseClass and IfcLateBoundEntity are merged.
|
||||
string_arg_filter(arg_map_t args) : args(args) { assert_arguments(); }
|
||||
string_arg_filter(IfcSchema::Type::Enum type, unsigned short index) { args[type] = index; assert_arguments(); }
|
||||
string_arg_filter(
|
||||
IfcSchema::Type::Enum type1, unsigned short index1,
|
||||
IfcSchema::Type::Enum type2, unsigned short index2)
|
||||
{
|
||||
args[type1] = index1;
|
||||
args[type2] = index2;
|
||||
assert_arguments();
|
||||
}
|
||||
|
||||
/// @todo this won't be needed when we have the generic argument name access
|
||||
void assert_arguments()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) {
|
||||
IfcEntityInstanceData dummy(it->first);
|
||||
IfcUtil::IfcBaseClass* base = IfcSchema::SchemaEntity(&dummy);
|
||||
assert(it->second < base->getArgumentCount() && "Argument index out of bounds");
|
||||
assert(base->getArgumentType(it->second) == IfcUtil::Argument_STRING && "Argument type not string");
|
||||
delete base;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string value(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) {
|
||||
if (prod->is(it->first) && it->second < prod->entity->getArgumentCount() &&
|
||||
prod->getArgumentType(it->second) == IfcUtil::Argument_STRING) {
|
||||
Argument *arg = prod->entity->getArgument(it->second);
|
||||
if (!arg->isNull()) {
|
||||
return *arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool match(IfcSchema::IfcProduct* prod) const { return wildcard_filter::match(value(prod)); }
|
||||
|
||||
bool operator()(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
return filter::match(prod, std::bind(&string_arg_filter::match, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void update_description()
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude");
|
||||
std::vector<std::string> patterns;
|
||||
BOOST_FOREACH(const boost::regex& r, values) {
|
||||
patterns.push_back("\"" + r.str() + "\"");
|
||||
}
|
||||
|
||||
for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) {
|
||||
IfcEntityInstanceData dummy(it->first);
|
||||
IfcUtil::IfcBaseClass* base = IfcSchema::SchemaEntity(&dummy);
|
||||
try {
|
||||
ss << " " << IfcSchema::Type::ToString(it->first) << "." << base->getArgumentName(it->second);
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
delete base;
|
||||
}
|
||||
|
||||
ss << " values " << boost::algorithm::join(patterns, " ");
|
||||
description = ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct layer_filter : public wildcard_filter
|
||||
{
|
||||
typedef std::map<std::string, IfcSchema::IfcPresentationLayerAssignment*> layer_map_t;
|
||||
|
||||
layer_filter() {}
|
||||
layer_filter(bool include, bool traverse, const std::set<std::string>& patterns)
|
||||
: wildcard_filter(include, traverse, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
bool match(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
layer_map_t layers = IfcGeom::Kernel::get_layers(prod);
|
||||
return std::find_if(layers.begin(), layers.end(), wildcards_match(values)) != layers.end();
|
||||
}
|
||||
|
||||
bool operator()(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
return filter::match(prod, std::bind(&layer_filter::match, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
struct wildcards_match
|
||||
{
|
||||
wildcards_match(const std::set<boost::regex>& patterns) : patterns(patterns) {}
|
||||
bool operator()(const layer_map_t::value_type& layer_map_value) const
|
||||
{
|
||||
return wildcard_filter::match_values(patterns, layer_map_value.first);
|
||||
}
|
||||
|
||||
std::set<boost::regex> patterns;
|
||||
};
|
||||
|
||||
void update_description()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " layers";
|
||||
std::vector<std::string> str_values;
|
||||
BOOST_FOREACH(const boost::regex& r, values) {
|
||||
str_values.push_back(" \"" + r.str() + "\"");
|
||||
}
|
||||
ss << boost::algorithm::join(str_values, " ");
|
||||
description = ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct entity_filter : public filter
|
||||
{
|
||||
entity_filter() {}
|
||||
entity_filter(bool include, bool traverse)
|
||||
: filter(include, traverse)
|
||||
{}
|
||||
|
||||
std::set<IfcSchema::Type::Enum> values;
|
||||
|
||||
void populate(const std::set<std::string>& types)
|
||||
{
|
||||
values.clear();
|
||||
BOOST_FOREACH(const std::string& type, types) {
|
||||
IfcSchema::Type::Enum ty;
|
||||
try {
|
||||
ty = IfcSchema::Type::FromString(boost::to_upper_copy(type));
|
||||
} catch (const IfcParse::IfcException&) {
|
||||
throw IfcParse::IfcException("'" + type + "' does not name a valid IFC entity");
|
||||
}
|
||||
values.insert(ty);
|
||||
/// @todo Add child classes so that containment in set can be in O(log n)
|
||||
}
|
||||
}
|
||||
|
||||
bool match(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
// The set is iterated over to able to filter on subtypes.
|
||||
BOOST_FOREACH(IfcSchema::Type::Enum type, values) {
|
||||
if (prod->is(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(IfcSchema::IfcProduct* prod) const
|
||||
{
|
||||
return filter::match(prod, std::bind(&entity_filter::match, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void update_description()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " entities";
|
||||
BOOST_FOREACH(IfcSchema::Type::Enum type, values) {
|
||||
ss << " " << IfcSchema::Type::ToString(type);
|
||||
}
|
||||
description = ss.str();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
+2957
-274
File diff suppressed because it is too large
Load Diff
+156
-44
@@ -77,6 +77,53 @@
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
// Helper functions (re)set gp_(G)Trsf(2d) forms explicitly to 'Identity'
|
||||
// so that it can be easily identified in the IfcMappedItem processing
|
||||
|
||||
// For axis placements detect equality early in order for the
|
||||
// relatively computionaly expensive gp_Trsf calculation to be skipped
|
||||
template <typename T>
|
||||
bool axis_equal(const T& a, const T& b, double tolerance);
|
||||
template <>
|
||||
bool axis_equal(const gp_Ax3& a, const gp_Ax3& b, double tolerance) {
|
||||
if (!a.Location().IsEqual(b.Location(), tolerance)) return false;
|
||||
// Note that the tolerance below is angular, above is linear. Since architectural
|
||||
// objects are about 1m'ish in scale, it should be somewhat equivalent. Besides,
|
||||
// this is mostly a filter for NULL or default values in the placements.
|
||||
if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false;
|
||||
if (!a.XDirection().IsEqual(b.XDirection(), tolerance)) return false;
|
||||
if (!a.YDirection().IsEqual(b.YDirection(), tolerance)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool axis_equal(const gp_Ax2d& a, const gp_Ax2d& b, double tolerance) {
|
||||
if (!a.Location().IsEqual(b.Location(), tolerance)) return false;
|
||||
if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> struct dimension_count {};
|
||||
template <> struct dimension_count <gp_Trsf2d > { static const int n = 2; };
|
||||
template <> struct dimension_count <gp_GTrsf2d> { static const int n = 2; };
|
||||
template <> struct dimension_count < gp_Trsf > { static const int n = 3; };
|
||||
template <> struct dimension_count < gp_GTrsf > { static const int n = 3; };
|
||||
|
||||
template <typename T>
|
||||
bool is_identity(const T& t, double tolerance) {
|
||||
// Note the {1, n+1} range due to Open Cascade's 1-based indexing
|
||||
// Note the {1, n+2} range due to the translation part of the matrix
|
||||
for (int i = 1; i < dimension_count<T>::n + 2; ++i) {
|
||||
for (int j = 1; j < dimension_count<T>::n + 1; ++j) {
|
||||
const double iden_value = i == j ? 1. : 0.;
|
||||
const double trsf_value = t.Value(j, i);
|
||||
if (fabs(trsf_value - iden_value) > tolerance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianPoint* l, gp_Pnt& point) {
|
||||
IN_CACHE(IfcCartesianPoint,l,gp_Pnt,point)
|
||||
std::vector<double> xyz = l->Coordinates();
|
||||
@@ -111,16 +158,43 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcVector* l, gp_Vec& v) {
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement3D* l, gp_Trsf& trsf) {
|
||||
IN_CACHE(IfcAxis2Placement3D,l,gp_Trsf,trsf)
|
||||
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);gp_Dir refDirection;
|
||||
IfcGeom::Kernel::convert(l->Location(),o);
|
||||
bool hasRef = l->RefDirection() ? true : false;
|
||||
if ( l->Axis() ) IfcGeom::Kernel::convert(*l->Axis(),axis);
|
||||
if ( hasRef ) IfcGeom::Kernel::convert(*l->RefDirection(),refDirection);
|
||||
gp_Ax3 ax3;
|
||||
if ( hasRef ) ax3 = gp_Ax3(o,axis,refDirection);
|
||||
else ax3 = gp_Ax3(o,axis);
|
||||
trsf.SetTransformation(ax3, gp_Ax3(gp_Pnt(),gp_Dir(0,0,1),gp_Dir(1,0,0)));
|
||||
IN_CACHE(IfcAxis2Placement3D, l, gp_Trsf, trsf)
|
||||
|
||||
gp_Pnt o;
|
||||
gp_Dir axis(0, 0, 1);
|
||||
gp_Dir refDirection;
|
||||
|
||||
IfcGeom::Kernel::convert(l->Location(), o);
|
||||
const bool hasAxis = l->hasAxis();
|
||||
const bool hasRef = l->hasRefDirection();
|
||||
|
||||
if (hasAxis != hasRef) {
|
||||
Logger::Warning("Axis and RefDirection should be specified together", l->entity);
|
||||
}
|
||||
|
||||
if (hasAxis) {
|
||||
IfcGeom::Kernel::convert(l->Axis(), axis);
|
||||
}
|
||||
|
||||
if (hasRef) {
|
||||
IfcGeom::Kernel::convert(l->RefDirection(), refDirection);
|
||||
} else {
|
||||
if (!axis.IsParallel(gp::DX(), 1.e-5)) {
|
||||
refDirection = gp::DX();
|
||||
} else {
|
||||
refDirection = gp::DZ();
|
||||
}
|
||||
gp_Vec Xvec = axis.Dot(refDirection) * axis;
|
||||
gp_Vec Xaxis = refDirection.XYZ() - Xvec.XYZ();
|
||||
refDirection = Xaxis;
|
||||
}
|
||||
|
||||
gp_Ax3 ax3(o, axis, refDirection);
|
||||
|
||||
if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) {
|
||||
trsf.SetTransformation(ax3, gp::XOY());
|
||||
}
|
||||
|
||||
CACHE(IfcAxis2Placement3D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
@@ -129,7 +203,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis1Placement* l, gp_Ax1& ax)
|
||||
IN_CACHE(IfcAxis1Placement,l,gp_Ax1,ax)
|
||||
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);
|
||||
IfcGeom::Kernel::convert(l->Location(),o);
|
||||
if ( l->Axis() ) IfcGeom::Kernel::convert(*l->Axis(), axis);
|
||||
if ( l->hasAxis() ) IfcGeom::Kernel::convert(l->Axis(), axis);
|
||||
ax = gp_Ax1(o, axis);
|
||||
CACHE(IfcAxis1Placement,l,ax)
|
||||
return true;
|
||||
@@ -142,14 +216,21 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato
|
||||
gp_Dir axis1 (1.,0.,0.);
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
gp_Dir axis3 (0.,0.,1.);
|
||||
if ( l->Axis1() ) IfcGeom::Kernel::convert(*l->Axis1(),axis1);
|
||||
if ( l->Axis2() ) IfcGeom::Kernel::convert(*l->Axis2(),axis2);
|
||||
if ( l->Axis3() ) IfcGeom::Kernel::convert(*l->Axis3(),axis3);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
if ( l->hasAxis3() ) IfcGeom::Kernel::convert(l->Axis3(),axis3);
|
||||
gp_Ax3 ax3 (origin,axis3,axis1);
|
||||
if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse();
|
||||
trsf.SetTransformation(ax3);
|
||||
trsf.Invert();
|
||||
if ( l->Scale() ) trsf.SetScaleFactor(*l->Scale());
|
||||
|
||||
if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) {
|
||||
trsf.SetTransformation(ax3);
|
||||
trsf.Invert();
|
||||
}
|
||||
|
||||
if (l->hasScale() && !ALMOST_THE_SAME(l->Scale(), 1.)) {
|
||||
trsf.SetScaleFactor(l->Scale());
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator3D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
@@ -162,8 +243,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
|
||||
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
|
||||
if ( l->Axis1() ) IfcGeom::Kernel::convert(*l->Axis1(),axis1);
|
||||
if ( l->Axis2() ) IfcGeom::Kernel::convert(*l->Axis2(),axis2);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
|
||||
const gp_Pnt2d origin2d(origin.X(), origin.Y());
|
||||
const gp_Dir2d axis12d(axis1.X(), axis1.Y());
|
||||
@@ -183,7 +264,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato
|
||||
}
|
||||
|
||||
trsf.Invert();
|
||||
if ( l->Scale() ) trsf.SetScaleFactor(*l->Scale());
|
||||
if ( l->hasScale() && !ALMOST_THE_SAME(l->Scale(), 1.) ) trsf.SetScaleFactor(l->Scale());
|
||||
|
||||
if (is_identity(trsf, getValue(GV_PRECISION))) {
|
||||
trsf = gp_Trsf2d();
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator2D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
@@ -196,21 +282,26 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato
|
||||
gp_Dir axis1 (1.,0.,0.);
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
gp_Dir axis3 (0.,0.,1.);
|
||||
if ( l->Axis1() ) IfcGeom::Kernel::convert(*l->Axis1(),axis1);
|
||||
if ( l->Axis2() ) IfcGeom::Kernel::convert(*l->Axis2(),axis2);
|
||||
if ( l->Axis3() ) IfcGeom::Kernel::convert(*l->Axis3(),axis3);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
if ( l->hasAxis3() ) IfcGeom::Kernel::convert(l->Axis3(),axis3);
|
||||
gp_Ax3 ax3 (origin,axis3,axis1);
|
||||
if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse();
|
||||
trsf.SetTransformation(ax3);
|
||||
trsf.Invert();
|
||||
const double scale1 = l->Scale() ? *l->Scale() : 1.0f;
|
||||
const double scale2 = l->Scale2() ? *l->Scale2() : scale1;
|
||||
const double scale3 = l->Scale3() ? *l->Scale3() : scale1;
|
||||
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
|
||||
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
|
||||
const double scale3 = l->hasScale3() ? l->Scale3() : scale1;
|
||||
gtrsf = gp_GTrsf();
|
||||
gtrsf.SetValue(1,1,scale1);
|
||||
gtrsf.SetValue(2,2,scale2);
|
||||
gtrsf.SetValue(3,3,scale3);
|
||||
gtrsf.PreMultiply(trsf);
|
||||
|
||||
if (is_identity(gtrsf, getValue(GV_PRECISION))) {
|
||||
gtrsf = gp_GTrsf();
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator3DnonUniform,l,gtrsf)
|
||||
return true;
|
||||
}
|
||||
@@ -224,8 +315,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato
|
||||
gp_Dir axis2 (0.,1.,0.);
|
||||
|
||||
IfcGeom::Kernel::convert(l->LocalOrigin(),origin);
|
||||
if ( l->Axis1() ) IfcGeom::Kernel::convert(*l->Axis1(),axis1);
|
||||
if ( l->Axis2() ) IfcGeom::Kernel::convert(*l->Axis2(),axis2);
|
||||
if ( l->hasAxis1() ) IfcGeom::Kernel::convert(l->Axis1(),axis1);
|
||||
if ( l->hasAxis2() ) IfcGeom::Kernel::convert(l->Axis2(),axis2);
|
||||
|
||||
const gp_Pnt2d origin2d(origin.X(), origin.Y());
|
||||
const gp_Dir2d axis12d(axis1.X(), axis1.Y());
|
||||
@@ -241,12 +332,17 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato
|
||||
|
||||
trsf.Invert();
|
||||
|
||||
const double scale1 = l->Scale() ? *l->Scale() : 1.0f;
|
||||
const double scale2 = l->Scale2() ? *l->Scale2() : scale1;
|
||||
const double scale1 = l->hasScale() ? l->Scale() : 1.0f;
|
||||
const double scale2 = l->hasScale2() ? l->Scale2() : scale1;
|
||||
gtrsf = gp_GTrsf2d();
|
||||
gtrsf.SetValue(1,1,scale1);
|
||||
gtrsf.SetValue(2,2,scale2);
|
||||
gtrsf.Multiply(trsf);
|
||||
|
||||
if (is_identity(gtrsf, getValue(GV_PRECISION))) {
|
||||
gtrsf = gp_GTrsf2d();
|
||||
}
|
||||
|
||||
CACHE(IfcCartesianTransformationOperator2DnonUniform,l,gtrsf)
|
||||
return true;
|
||||
}
|
||||
@@ -256,9 +352,9 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPlane* pln, gp_Pln& plane) {
|
||||
IfcSchema::IfcAxis2Placement3D* l = pln->Position();
|
||||
gp_Pnt o;gp_Dir axis = gp_Dir(0,0,1);gp_Dir refDirection;
|
||||
IfcGeom::Kernel::convert(l->Location(),o);
|
||||
bool hasRef = l->RefDirection();
|
||||
if ( l->Axis() ) IfcGeom::Kernel::convert(*l->Axis(),axis);
|
||||
if ( hasRef ) IfcGeom::Kernel::convert(*l->RefDirection(),refDirection);
|
||||
bool hasRef = l->hasRefDirection();
|
||||
if ( l->hasAxis() ) IfcGeom::Kernel::convert(l->Axis(),axis);
|
||||
if ( hasRef ) IfcGeom::Kernel::convert(l->RefDirection(),refDirection);
|
||||
gp_Ax3 ax3;
|
||||
if ( hasRef ) ax3 = gp_Ax3(o,axis,refDirection);
|
||||
else ax3 = gp_Ax3(o,axis);
|
||||
@@ -271,15 +367,23 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement2D* l, gp_Trsf2d
|
||||
IN_CACHE(IfcAxis2Placement2D,l,gp_Trsf2d,trsf)
|
||||
gp_Pnt P; gp_Dir V (1,0,0);
|
||||
IfcGeom::Kernel::convert(l->Location(),P);
|
||||
if ( l->RefDirection() )
|
||||
IfcGeom::Kernel::convert(*l->RefDirection(),V);
|
||||
if ( l->hasRefDirection() )
|
||||
IfcGeom::Kernel::convert(l->RefDirection(),V);
|
||||
|
||||
gp_Ax2d axis(gp_Pnt2d(P.X(),P.Y()), gp_Dir2d(V.X(),V.Y()));
|
||||
|
||||
if (!axis_equal(axis, gp_Ax2d(), getValue(GV_PRECISION))) {
|
||||
trsf.SetTransformation(axis, gp_Ax2d());
|
||||
}
|
||||
|
||||
gp_Ax2d axis(gp_Pnt2d(P.X(),P.Y()),gp_Dir2d(V.X(),V.Y()));
|
||||
trsf.SetTransformation(axis,gp_Ax2d());
|
||||
CACHE(IfcAxis2Placement2D,l,trsf)
|
||||
return true;
|
||||
}
|
||||
|
||||
void IfcGeom::Kernel::set_conversion_placement_rel_to(IfcSchema::Type::Enum type) {
|
||||
placement_rel_to = type;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf) {
|
||||
IN_CACHE(IfcObjectPlacement,l,gp_Trsf,trsf)
|
||||
if ( ! l->is(IfcSchema::Type::IfcLocalPlacement) ) {
|
||||
@@ -287,20 +391,28 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& t
|
||||
return false;
|
||||
}
|
||||
IfcSchema::IfcLocalPlacement* current = (IfcSchema::IfcLocalPlacement*)l;
|
||||
while (1) {
|
||||
for (;;) {
|
||||
gp_Trsf trsf2;
|
||||
IfcSchema::IfcAxis2Placement* relplacement = current->RelativePlacement();
|
||||
if ( relplacement->is(IfcSchema::Type::IfcAxis2Placement3D) ) {
|
||||
IfcGeom::Kernel::convert((IfcSchema::IfcAxis2Placement3D*)relplacement,trsf2);
|
||||
trsf.PreMultiply(trsf2);
|
||||
}
|
||||
if ( current->PlacementRelTo() ) {
|
||||
IfcSchema::IfcObjectPlacement* relto = *current->PlacementRelTo();
|
||||
if ( relto->is(IfcSchema::Type::IfcLocalPlacement) )
|
||||
current = (IfcSchema::IfcLocalPlacement*) relto;
|
||||
else break;
|
||||
if ( current->hasPlacementRelTo() ) {
|
||||
IfcSchema::IfcObjectPlacement* parent = current->PlacementRelTo();
|
||||
IfcSchema::IfcProduct::list::ptr parentPlaces = parent->PlacesObject();
|
||||
bool parentPlacesType = false;
|
||||
for ( IfcSchema::IfcProduct::list::it iter = parentPlaces->begin();
|
||||
iter != parentPlaces->end(); ++iter) {
|
||||
if ( (*iter)->is(placement_rel_to) ) parentPlacesType = true;
|
||||
}
|
||||
|
||||
if ( parentPlacesType ) break;
|
||||
else if ( parent->is(IfcSchema::Type::IfcLocalPlacement) )
|
||||
current = (IfcSchema::IfcLocalPlacement*)current->PlacementRelTo();
|
||||
else break;
|
||||
} else break;
|
||||
}
|
||||
CACHE(IfcObjectPlacement,l,trsf)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+445
-169
@@ -39,7 +39,7 @@
|
||||
* IfcGeomObject.transformation.matrix is a 4x3 matrix that defines the *
|
||||
* orientation and translation of the mesh in relation to the world origin *
|
||||
* *
|
||||
* IfcGeom::Iterator::findContext() *
|
||||
* IfcGeom::Iterator::initialize() *
|
||||
* finds the most suitable representation contexts. Returns true iff *
|
||||
* at least a single representation will process successfully *
|
||||
* *
|
||||
@@ -64,6 +64,8 @@
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <gp_Mat.hxx>
|
||||
#include <gp_Mat2d.hxx>
|
||||
#include <gp_GTrsf.hxx>
|
||||
@@ -78,12 +80,24 @@
|
||||
#include "../ifcgeom/IfcGeomMaterial.h"
|
||||
#include "../ifcgeom/IfcGeomIteratorSettings.h"
|
||||
#include "../ifcgeom/IfcRepresentationShapeItem.h"
|
||||
#include "../ifcgeom/IfcGeomFilter.h"
|
||||
|
||||
// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
template <typename P>
|
||||
class Iterator {
|
||||
private:
|
||||
Iterator(const Iterator&); // N/I
|
||||
Iterator& operator=(const Iterator&); // N/I
|
||||
|
||||
Kernel kernel;
|
||||
IteratorSettings settings;
|
||||
|
||||
@@ -102,12 +116,27 @@ namespace IfcGeom {
|
||||
IfcSchema::IfcProduct::list::ptr ifcproducts;
|
||||
IfcSchema::IfcProduct::list::it ifcproduct_iterator;
|
||||
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations;
|
||||
|
||||
int done;
|
||||
int total;
|
||||
|
||||
std::string unit_name;
|
||||
// double?
|
||||
P unit_magnitude;
|
||||
double unit_magnitude;
|
||||
|
||||
gp_XYZ bounds_min_;
|
||||
gp_XYZ bounds_max_;
|
||||
|
||||
std::vector<filter_t> filters_;
|
||||
|
||||
struct filter_match
|
||||
{
|
||||
filter_match(IfcSchema::IfcProduct *prod) : product(prod) {}
|
||||
bool operator()(const filter_t& filter) const { return filter(product); }
|
||||
|
||||
IfcSchema::IfcProduct* product;
|
||||
};
|
||||
|
||||
void initUnits() {
|
||||
IfcSchema::IfcProject::list::ptr projects = ifc_file->entitiesByType<IfcSchema::IfcProject>();
|
||||
@@ -115,52 +144,55 @@ namespace IfcGeom {
|
||||
IfcSchema::IfcProject* project = *projects->begin();
|
||||
std::pair<std::string, double> length_unit = kernel.initializeUnits(project->UnitsInContext());
|
||||
unit_name = length_unit.first;
|
||||
unit_magnitude = static_cast<P>(length_unit.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<IfcSchema::Type::Enum> entities_to_include_or_exclude;
|
||||
bool include_entities_in_processing;
|
||||
|
||||
void populate_set(const std::set<std::string>& include_or_ignore) {
|
||||
entities_to_include_or_exclude.clear();
|
||||
for (std::set<std::string>::const_iterator it = include_or_ignore.begin(); it != include_or_ignore.end(); ++it) {
|
||||
std::string uppercase_type = *it;
|
||||
for (std::string::iterator c = uppercase_type.begin(); c != uppercase_type.end(); ++c) {
|
||||
*c = toupper(*c);
|
||||
}
|
||||
IfcSchema::Type::Enum ty;
|
||||
try {
|
||||
ty = IfcSchema::Type::FromString(uppercase_type);
|
||||
} catch (const IfcParse::IfcException&) {
|
||||
std::stringstream ss;
|
||||
ss << "'" << *it << "' does not name a valid IFC entity";
|
||||
throw IfcParse::IfcException(ss.str());
|
||||
}
|
||||
entities_to_include_or_exclude.insert(ty);
|
||||
// TODO: Add child classes so that containment in set can be in O(log n)
|
||||
unit_magnitude = length_unit.second;
|
||||
} else {
|
||||
Logger::Error("A single IfcProject is expected (encountered " + boost::lexical_cast<std::string>(projects->size()) + "); unable to read unit information.");
|
||||
}
|
||||
}
|
||||
|
||||
/// @todo public/private sections all over the place: move all public to the beginning of the class
|
||||
public:
|
||||
bool findContext() {
|
||||
Iterator(const IteratorSettings& settings, IfcParse::IfcFile* file, std::vector<IfcGeom::filter_t>& filters)
|
||||
: settings(settings)
|
||||
, ifc_file(file)
|
||||
, filters_(filters)
|
||||
, owns_ifc_file(false)
|
||||
{
|
||||
_initialize();
|
||||
}
|
||||
|
||||
bool initialize() {
|
||||
try {
|
||||
initUnits();
|
||||
} catch (...) {}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
|
||||
std::set<std::string> allowed_context_types;
|
||||
allowed_context_types.insert("model");
|
||||
allowed_context_types.insert("plan");
|
||||
allowed_context_types.insert("notdefined");
|
||||
|
||||
// Really this should only be 'Model', as per
|
||||
// the standard 'Design' is deprecated. So,
|
||||
// just for backwards compatibility:
|
||||
std::set<std::string> context_types;
|
||||
context_types.insert("model");
|
||||
context_types.insert("design");
|
||||
// DDS likes to output 'model view'
|
||||
context_types.insert("model view");
|
||||
if (!settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES)) {
|
||||
// Really this should only be 'Model', as per
|
||||
// the standard 'Design' is deprecated. So,
|
||||
// just for backwards compatibility:
|
||||
context_types.insert("model");
|
||||
context_types.insert("design");
|
||||
// Some earlier (?) versions DDS-CAD output their own ContextTypes
|
||||
context_types.insert("model view");
|
||||
context_types.insert("detail view");
|
||||
}
|
||||
if (settings.get(IteratorSettings::INCLUDE_CURVES)) {
|
||||
context_types.insert("plan");
|
||||
}
|
||||
|
||||
double lowest_precision_encountered = std::numeric_limits<double>::infinity();
|
||||
bool any_precision_encountered = false;
|
||||
|
||||
representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
|
||||
ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list);
|
||||
|
||||
IfcSchema::IfcGeometricRepresentationContext::list::it it;
|
||||
IfcSchema::IfcGeometricRepresentationSubContext::list::it jt;
|
||||
@@ -176,29 +208,47 @@ namespace IfcGeom {
|
||||
// by the parent's context inverse attributes.
|
||||
continue;
|
||||
}
|
||||
if (context->ContextType()) {
|
||||
std::string context_type_lc = *context->ContextType();
|
||||
for (std::string::iterator c = context_type_lc.begin(); c != context_type_lc.end(); ++c) {
|
||||
*c = tolower(*c);
|
||||
}
|
||||
if (context_types.find(context_type_lc) != context_types.end()) {
|
||||
filtered_contexts->push(context);
|
||||
try {
|
||||
if (context->hasContextType()) {
|
||||
std::string context_type = context->ContextType();
|
||||
boost::to_lower(context_type);
|
||||
|
||||
if (allowed_context_types.find(context_type) == allowed_context_types.end()) {
|
||||
Logger::Message(Logger::LOG_ERROR, std::string("ContextType '") + context->ContextType() + "' not allowed:", context->entity);
|
||||
}
|
||||
if (context_types.find(context_type) != context_types.end()) {
|
||||
filtered_contexts->push(context);
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// In case no contexts are identified based on their ContextType, all contexts are
|
||||
// considered. Note that sub contexts are excluded as they are considered later on.
|
||||
if (filtered_contexts->size() == 0) {
|
||||
filtered_contexts = contexts;
|
||||
for (it = contexts->begin(); it != contexts->end(); ++it) {
|
||||
IfcSchema::IfcGeometricRepresentationContext* context = *it;
|
||||
if (!context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) {
|
||||
filtered_contexts->push(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) {
|
||||
IfcSchema::IfcGeometricRepresentationContext* context = *it;
|
||||
|
||||
representations->push(context->RepresentationsInContext());
|
||||
if (context->Precision() && *context->Precision() < lowest_precision_encountered) {
|
||||
lowest_precision_encountered = *context->Precision();
|
||||
any_precision_encountered = true;
|
||||
try {
|
||||
if (context->hasPrecision() && context->Precision() < lowest_precision_encountered) {
|
||||
lowest_precision_encountered = context->Precision();
|
||||
any_precision_encountered = true;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
}
|
||||
|
||||
IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts();
|
||||
for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) {
|
||||
representations->push((*jt)->RepresentationsInContext());
|
||||
@@ -222,7 +272,15 @@ namespace IfcGeom {
|
||||
kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-5);
|
||||
}
|
||||
|
||||
if (representations->size() == 0) return false;
|
||||
if (representations->size() == 0) {
|
||||
Logger::Message(Logger::LOG_ERROR, "No representations encountered in relevant contexts, using all");
|
||||
representations = ifc_file->entitiesByType<IfcSchema::IfcRepresentation>();
|
||||
}
|
||||
|
||||
if (representations->size() == 0) {
|
||||
Logger::Message(Logger::LOG_ERROR, "No representations encountered, aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
representation_iterator = representations->begin();
|
||||
ifcproducts.reset();
|
||||
@@ -237,99 +295,179 @@ namespace IfcGeom {
|
||||
return true;
|
||||
}
|
||||
|
||||
int progress() {
|
||||
return 100 * done / total;
|
||||
}
|
||||
/// Computes model's bounding box (bounds_min and bounds_max).
|
||||
/// @note Can take several minutes for large files.
|
||||
void compute_bounds()
|
||||
{
|
||||
for (int i = 1; i < 4; ++i) {
|
||||
bounds_min_.SetCoord(i, std::numeric_limits<double>::infinity());
|
||||
bounds_max_.SetCoord(i, -std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
const std::string& getUnitName() {
|
||||
return unit_name;
|
||||
}
|
||||
IfcSchema::IfcProduct::list::ptr products = ifc_file->entitiesByType<IfcSchema::IfcProduct>();
|
||||
for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) {
|
||||
IfcSchema::IfcProduct* product = *iter;
|
||||
if (product->hasObjectPlacement()) {
|
||||
// Use a fresh trsf every time in order to prevent the result to be concatenated
|
||||
gp_Trsf trsf;
|
||||
bool success = false;
|
||||
|
||||
const P getUnitMagnitude() {
|
||||
return unit_magnitude;
|
||||
}
|
||||
try {
|
||||
success = kernel.convert(product->ObjectPlacement(), trsf);
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
} catch (...) {
|
||||
Logger::Error("Failed to construct placement");
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const gp_XYZ& pos = trsf.TranslationPart();
|
||||
bounds_min_.SetX(std::min(bounds_min_.X(), pos.X()));
|
||||
bounds_min_.SetY(std::min(bounds_min_.Y(), pos.Y()));
|
||||
bounds_min_.SetZ(std::min(bounds_min_.Z(), pos.Z()));
|
||||
bounds_max_.SetX(std::max(bounds_max_.X(), pos.X()));
|
||||
bounds_max_.SetY(std::max(bounds_max_.Y(), pos.Y()));
|
||||
bounds_max_.SetZ(std::max(bounds_max_.Z(), pos.Z()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int progress() const { return 100 * done / total; }
|
||||
|
||||
const std::string& getUnitName() const { return unit_name; }
|
||||
|
||||
/// @note Double always as per IFC specification.
|
||||
double getUnitMagnitude() const { return unit_magnitude; }
|
||||
|
||||
const std::string getLog() {
|
||||
return Logger::GetLog();
|
||||
}
|
||||
std::string getLog() const { return Logger::GetLog(); }
|
||||
|
||||
IfcParse::IfcFile* getFile() {
|
||||
return ifc_file;
|
||||
}
|
||||
IfcParse::IfcFile* getFile() const { return ifc_file; }
|
||||
|
||||
void includeEntities(const std::set<std::string>& entities) {
|
||||
populate_set(entities);
|
||||
include_entities_in_processing = true;
|
||||
}
|
||||
const std::vector<IfcGeom::filter_t>& filters() const { return filters_; }
|
||||
std::vector<IfcGeom::filter_t>& filters() { return filters_; }
|
||||
|
||||
void excludeEntities(const std::set<std::string>& entities) {
|
||||
populate_set(entities);
|
||||
include_entities_in_processing = false;
|
||||
}
|
||||
const gp_XYZ& bounds_min() const { return bounds_min_; }
|
||||
const gp_XYZ& bounds_max() const { return bounds_max_; }
|
||||
|
||||
private:
|
||||
// Move to the next IfcRepresentation
|
||||
void _nextShape() {
|
||||
// In order to conserve memory and reduce cache insertion times, the cache is
|
||||
// cleared after an arbitrary number of processed representations. This has been
|
||||
// benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47
|
||||
static const int clear_interval = 64;
|
||||
if (done % clear_interval == clear_interval - 1) {
|
||||
kernel.purge_cache();
|
||||
}
|
||||
ifcproducts.reset();
|
||||
++ representation_iterator;
|
||||
++ done;
|
||||
}
|
||||
|
||||
bool geometry_reuse_ok_for_current_representation_;
|
||||
|
||||
bool reuse_ok_(const IfcSchema::IfcProduct::list::ptr& products) {
|
||||
// With world coords enabled, object transformations are directly applied to
|
||||
// the BRep. There is no way to re-use the geometry for multiple products.
|
||||
if (settings.get(IteratorSettings::USE_WORLD_COORDS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<const IfcSchema::IfcMaterial*> associated_single_materials;
|
||||
|
||||
for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) {
|
||||
IfcSchema::IfcProduct* product = *it;
|
||||
|
||||
if (!settings.get(IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && kernel.find_openings(product)->size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.get(IteratorSettings::APPLY_LAYERSETS)) {
|
||||
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) {
|
||||
IfcSchema::IfcRelAssociatesMaterial* assoc = (*jt)->as<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
if (assoc) {
|
||||
if (assoc->RelatingMaterial()->is(IfcSchema::Type::IfcMaterialLayerSetUsage)) {
|
||||
// TODO: Check whether single layer?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note that this can be a nullptr (!), but the fact that set size should be one still holds
|
||||
associated_single_materials.insert(kernel.get_single_material_association(product));
|
||||
if (associated_single_materials.size() > 1) return false;
|
||||
}
|
||||
|
||||
return associated_single_materials.size() == 1;
|
||||
}
|
||||
|
||||
BRepElement<P>* create_shape_model_for_next_entity() {
|
||||
while ( true ) {
|
||||
for (;;) {
|
||||
IfcSchema::IfcRepresentation* representation;
|
||||
|
||||
// Have we reached the end of our list of representations?
|
||||
if ( representation_iterator == representations->end() ) {
|
||||
representations.reset();
|
||||
return 0;
|
||||
return 0; // reached the end of our list of representations
|
||||
}
|
||||
representation = *representation_iterator;
|
||||
|
||||
// Has the list of IfcProducts for this representation been initialized?
|
||||
if (!ifcproducts) {
|
||||
|
||||
IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation();
|
||||
// Init. the list of filtered IfcProducts for this representation
|
||||
ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list);
|
||||
IfcSchema::IfcProduct::list::ptr unfiltered_products(new IfcSchema::IfcProduct::list);
|
||||
IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel.products_represented_by(representation);
|
||||
// Include only the desired products for processing.
|
||||
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
|
||||
IfcSchema::IfcProduct* prod = *jt;
|
||||
if (boost::all(filters_, filter_match(prod))) {
|
||||
ifcproducts->push(prod);
|
||||
}
|
||||
}
|
||||
|
||||
for ( IfcSchema::IfcProductRepresentation::list::it it = prodreps->begin(); it != prodreps->end(); ++it ) {
|
||||
if ( (*it)->is(IfcSchema::Type::IfcProductDefinitionShape) ) {
|
||||
IfcSchema::IfcProductDefinitionShape* pds = (IfcSchema::IfcProductDefinitionShape*)*it;
|
||||
unfiltered_products->push(pds->ShapeOfProduct());
|
||||
} else {
|
||||
// http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm
|
||||
// IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards.
|
||||
// It will be changed into an ABSTRACT supertype in future releases of IFC.
|
||||
if (ifcproducts->size() == 0) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
|
||||
// IfcProductRepresentation also lacks the INVERSE relation to IfcProduct
|
||||
// Let's find the IfcProducts that reference the IfcProductRepresentation anyway
|
||||
unfiltered_products->push((*it)->entity->getInverse(IfcSchema::Type::IfcProduct, -1)->as<IfcSchema::IfcProduct>());
|
||||
geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts);
|
||||
|
||||
IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
|
||||
|
||||
if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) {
|
||||
// unfiltered_products contains products represented by this representation by means of mapped items.
|
||||
// For example because of openings applied to products, reuse might not be acceptable and then the
|
||||
// products will be processed by means of their immediate representation and not the mapped representation.
|
||||
|
||||
// IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map
|
||||
// is indeed used by IfcMappedItems.
|
||||
IfcSchema::IfcRepresentationMap* map = *maps->begin();
|
||||
if (map->MapUsage()->size() > 0) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter the products based on the set of entities being included or excluded for
|
||||
// processing. The set is iterated over te able to filter on subtypes.
|
||||
for ( IfcSchema::IfcProduct::list::it it = unfiltered_products->begin(); it != unfiltered_products->end(); ++it ) {
|
||||
bool found = false;
|
||||
for (std::set<IfcSchema::Type::Enum>::const_iterator jt = entities_to_include_or_exclude.begin(); jt != entities_to_include_or_exclude.end(); ++jt) {
|
||||
if ((*it)->is(*jt)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == include_entities_in_processing) {
|
||||
ifcproducts->push(*it);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Does this representation have any IfcProducts?
|
||||
if (!ifcproducts->size()) {
|
||||
|
||||
// Check if this represenation has (or will be) processed as part its mapped representation
|
||||
bool representation_processed_as_mapped_item = false;
|
||||
IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation);
|
||||
if (representation_mapped_to) {
|
||||
representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && (
|
||||
ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to)));
|
||||
}
|
||||
|
||||
if (representation_processed_as_mapped_item) {
|
||||
ok_mapped_representations->push(representation_mapped_to);
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
|
||||
ifcproduct_iterator = ifcproducts->begin();
|
||||
}
|
||||
|
||||
// Have we reached the end of our list of IfcProducts?
|
||||
if ( ifcproduct_iterator == ifcproducts->end() ) {
|
||||
_nextShape();
|
||||
@@ -337,21 +475,27 @@ namespace IfcGeom {
|
||||
}
|
||||
|
||||
IfcSchema::IfcProduct* product = *ifcproduct_iterator;
|
||||
Logger::SetProduct(product);
|
||||
|
||||
BRepElement<P>* element = kernel.create_brep_for_representation_and_product<P>(settings, representation, product);
|
||||
BRepElement<P>* element;
|
||||
if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) {
|
||||
element = kernel.create_brep_for_representation_and_product<P>(settings, representation, product);
|
||||
} else {
|
||||
element = kernel.create_brep_for_processed_representation(settings, representation, product, current_shape_model);
|
||||
}
|
||||
|
||||
if ( !element ) {
|
||||
Logger::SetProduct(boost::none);
|
||||
|
||||
if (!element) {
|
||||
_nextShape();
|
||||
continue;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool next() {
|
||||
void free_shapes() {
|
||||
// Free all possible representations of the current geometrical entity
|
||||
delete current_triangulation;
|
||||
current_triangulation = 0;
|
||||
@@ -359,7 +503,26 @@ namespace IfcGeom {
|
||||
current_serialization = 0;
|
||||
delete current_shape_model;
|
||||
current_shape_model = 0;
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
/// Returns what would be the product for the next shape representation
|
||||
/// @todo Double-check and test the impl.
|
||||
//IfcSchema::IfcProduct* peek_next() const
|
||||
//{
|
||||
// if (ifcproducts && ifcproduct_iterator + 1 != ifcproducts->end()){
|
||||
// return *(ifcproduct_iterator + 1);
|
||||
// } else {
|
||||
// return 0;
|
||||
// }
|
||||
//}
|
||||
|
||||
/// @todo Would this be as simple as the following code?
|
||||
//void skip_next() { if (ifcproducts) { ++ifcproduct_iterator; } }
|
||||
|
||||
/// Moves to the next shape representation, create its geometry, and returns the associated product.
|
||||
/// Use get() to retrieve the created geometry.
|
||||
IfcSchema::IfcProduct* next() {
|
||||
// Increment the iterator over the list of products using the current
|
||||
// shape representation
|
||||
if (ifcproducts) {
|
||||
@@ -369,123 +532,236 @@ namespace IfcGeom {
|
||||
return create();
|
||||
}
|
||||
|
||||
Element<P>* get() {
|
||||
/// Gets the representation of the current geometrical entity.
|
||||
Element<P>* get()
|
||||
{
|
||||
// TODO: Test settings and throw
|
||||
Element<P>* ret = 0;
|
||||
if (current_triangulation) { ret = current_triangulation; }
|
||||
else if (current_serialization) { ret = current_serialization; }
|
||||
else if (current_shape_model) { ret = current_shape_model; }
|
||||
|
||||
// If we want to organize the element considering their hierarchy
|
||||
if (settings.get(IteratorSettings::SEARCH_FLOOR))
|
||||
{
|
||||
// We are going to build a vector with the element parents.
|
||||
// First, create the parent vector
|
||||
std::vector<const IfcGeom::Element<P>*> parents;
|
||||
|
||||
// if the element has a parent
|
||||
if (ret->parent_id() != -1)
|
||||
{
|
||||
const IfcGeom::Element<P>* parent_object = NULL;
|
||||
bool hasParent = true;
|
||||
|
||||
// get the parent
|
||||
try {
|
||||
parent_object = getObject(ret->parent_id());
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
hasParent = false;
|
||||
}
|
||||
|
||||
// Add the previously found parent to the vector
|
||||
if (hasParent) parents.insert(parents.begin(), parent_object);
|
||||
|
||||
// We need to find all the parents
|
||||
while (parent_object != NULL && hasParent && parent_object->parent_id() != -1)
|
||||
{
|
||||
// Find the next parent
|
||||
try {
|
||||
parent_object = getObject(parent_object->parent_id());
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
hasParent = false;
|
||||
}
|
||||
|
||||
// Add the previously found parent to the vector
|
||||
if (hasParent) parents.insert(parents.begin(), parent_object);
|
||||
|
||||
hasParent = hasParent && parent_object->parent_id() != -1;
|
||||
}
|
||||
|
||||
// when done push the parent list in the Element object
|
||||
ret->SetParents(parents);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Gets the native (Open Cascade) representation of the current geometrical entity.
|
||||
BRepElement<P>* get_native()
|
||||
{
|
||||
// TODO: Test settings and throw
|
||||
if (current_triangulation) return current_triangulation;
|
||||
else if (current_serialization) return current_serialization;
|
||||
else if (current_shape_model) return current_shape_model;
|
||||
else return 0;
|
||||
return current_shape_model;
|
||||
}
|
||||
|
||||
const Element<P>* getObject(int id) {
|
||||
|
||||
gp_Trsf trsf;
|
||||
int parent_id = -1;
|
||||
std::string instance_type, product_name, product_guid;
|
||||
|
||||
IfcSchema::IfcProduct* ifc_product = 0;
|
||||
|
||||
try {
|
||||
const IfcUtil::IfcBaseClass* ifc_entity = ifc_file->entityById(id);
|
||||
IfcUtil::IfcBaseClass* ifc_entity = ifc_file->entityById(id);
|
||||
instance_type = IfcSchema::Type::ToString(ifc_entity->type());
|
||||
if ( ifc_entity->is(IfcSchema::Type::IfcProduct) ) {
|
||||
IfcSchema::IfcProduct* ifc_product = (IfcSchema::IfcProduct*)ifc_entity;
|
||||
|
||||
product_guid = ifc_product->GlobalId();
|
||||
product_name = ifc_product->hasName() ? ifc_product->Name() : "";
|
||||
|
||||
|
||||
if (ifc_entity->is(IfcSchema::Type::IfcRoot)) {
|
||||
IfcSchema::IfcRoot* ifc_root = ifc_entity->as<IfcSchema::IfcRoot>();
|
||||
product_guid = ifc_root->GlobalId();
|
||||
product_name = ifc_root->hasName() ? ifc_root->Name() : "";
|
||||
}
|
||||
|
||||
if (ifc_entity->is(IfcSchema::Type::IfcProduct)) {
|
||||
ifc_product = ifc_entity->as<IfcSchema::IfcProduct>();
|
||||
parent_id = -1;
|
||||
try {
|
||||
IfcSchema::IfcObjectDefinition* parent_object = kernel.get_decomposing_entity(ifc_product);
|
||||
if (parent_object) {
|
||||
parent_id = parent_object->entity->id();
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
} catch (...) {
|
||||
Logger::Error("Failed to find decomposing entity");
|
||||
}
|
||||
|
||||
try {
|
||||
kernel.convert(ifc_product->ObjectPlacement(), trsf);
|
||||
} catch (...) {}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
} catch (...) {
|
||||
Logger::Error("Failed to construct placement");
|
||||
}
|
||||
}
|
||||
} catch(...) {}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error returning product");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error returning product");
|
||||
}
|
||||
|
||||
ElementSettings element_settings(settings, unit_magnitude, instance_type);
|
||||
Element<P>* ifc_object = new Element<P>(element_settings, id, parent_id, product_name, instance_type, product_guid, trsf);
|
||||
|
||||
|
||||
Element<P>* ifc_object = new Element<P>(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product);
|
||||
return ifc_object;
|
||||
}
|
||||
|
||||
bool create() {
|
||||
IfcSchema::IfcProduct* create() {
|
||||
IfcGeom::BRepElement<P>* next_shape_model = 0;
|
||||
IfcGeom::SerializedElement<P>* next_serialization = 0;
|
||||
IfcGeom::TriangulationElement<P>* next_triangulation = 0;
|
||||
|
||||
try {
|
||||
current_shape_model = create_shape_model_for_next_entity();
|
||||
} catch (...) {}
|
||||
if (!current_shape_model) return false;
|
||||
if (settings.use_brep_data()) {
|
||||
try {
|
||||
current_serialization = new SerializedElement<P>(*current_shape_model);
|
||||
} catch (...) {}
|
||||
return !!current_serialization;
|
||||
} else if (!settings.disable_triangulation()) {
|
||||
try {
|
||||
current_triangulation = new TriangulationElement<P>(*current_shape_model);
|
||||
} catch (...) {}
|
||||
return !!current_triangulation;
|
||||
} else {
|
||||
return true;
|
||||
next_shape_model = create_shape_model_for_next_entity();
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Error(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Error("Unknown error creating geometry");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Error("Unknown error creating geometry");
|
||||
}
|
||||
|
||||
if (next_shape_model) {
|
||||
if (settings.get(IteratorSettings::USE_BREP_DATA)) {
|
||||
try {
|
||||
next_serialization = new SerializedElement<P>(*next_shape_model);
|
||||
} catch (...) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed.");
|
||||
}
|
||||
} else if (!settings.get(IteratorSettings::DISABLE_TRIANGULATION)) {
|
||||
try {
|
||||
if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) {
|
||||
next_triangulation = new TriangulationElement<P>(*next_shape_model);
|
||||
} else {
|
||||
next_triangulation = new TriangulationElement<P>(*next_shape_model, current_triangulation->geometry_pointer());
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_shapes();
|
||||
|
||||
current_shape_model = next_shape_model;
|
||||
current_serialization = next_serialization;
|
||||
current_triangulation = next_triangulation;
|
||||
|
||||
return next_shape_model ? next_shape_model->product() : 0;
|
||||
}
|
||||
private:
|
||||
void initialize() {
|
||||
void _initialize() {
|
||||
current_triangulation = 0;
|
||||
current_shape_model = 0;
|
||||
current_serialization = 0;
|
||||
|
||||
// Upon initialisation, the (empty) set of entity names,
|
||||
// should be excluded, or no products would be processed.
|
||||
include_entities_in_processing = false;
|
||||
|
||||
unit_name = "METER";
|
||||
unit_magnitude = 1.f;
|
||||
|
||||
kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.sew_shells() ? 1000 : -1);
|
||||
kernel.setValue(IfcGeom::Kernel::GV_FORCE_CCW_FACE_ORIENTATION, settings.force_ccw_face_orientation() ? 1 : -1);
|
||||
kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.get(IteratorSettings::SEW_SHELLS) ? 1000 : -1);
|
||||
kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES)
|
||||
? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.));
|
||||
if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) {
|
||||
if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
|
||||
Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement");
|
||||
}
|
||||
kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcBuilding);
|
||||
} else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) {
|
||||
kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcSite);
|
||||
}
|
||||
}
|
||||
|
||||
bool owns_ifc_file;
|
||||
public:
|
||||
Iterator(const IteratorSettings& settings, IfcParse::IfcFile* file)
|
||||
: settings(settings)
|
||||
, ifc_file(file)
|
||||
, owns_ifc_file(false)
|
||||
{
|
||||
initialize();
|
||||
_initialize();
|
||||
}
|
||||
Iterator(const IteratorSettings& settings, const std::string& filename)
|
||||
: settings(settings)
|
||||
, ifc_file(new IfcParse::IfcFile)
|
||||
, owns_ifc_file(true)
|
||||
{
|
||||
ifc_file->Init(filename);
|
||||
initialize();
|
||||
_initialize();
|
||||
}
|
||||
Iterator(const IteratorSettings& settings, void* data, int length)
|
||||
: settings(settings)
|
||||
, ifc_file(new IfcParse::IfcFile)
|
||||
, owns_ifc_file(true)
|
||||
{
|
||||
ifc_file->Init(data, length);
|
||||
initialize();
|
||||
_initialize();
|
||||
}
|
||||
Iterator(const IteratorSettings& settings, std::istream& filestream, int length)
|
||||
: settings(settings)
|
||||
, ifc_file(new IfcParse::IfcFile)
|
||||
, owns_ifc_file(true)
|
||||
{
|
||||
ifc_file->Init(filestream, length);
|
||||
initialize();
|
||||
_initialize();
|
||||
}
|
||||
|
||||
~Iterator() {
|
||||
// TODO: Correctly implement destructor for IfcFile
|
||||
delete ifc_file;
|
||||
if (owns_ifc_file) {
|
||||
delete ifc_file;
|
||||
}
|
||||
|
||||
delete current_triangulation;
|
||||
current_triangulation = 0;
|
||||
delete current_serialization;
|
||||
current_serialization = 0;
|
||||
delete current_shape_model;
|
||||
current_shape_model = 0;
|
||||
free_shapes();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,153 +20,138 @@
|
||||
#ifndef IFCGEOMITERATORSETTINGS_H
|
||||
#define IFCGEOMITERATORSETTINGS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ifc_geom_api.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
|
||||
namespace IfcGeom {
|
||||
namespace IfcGeom
|
||||
{
|
||||
class IFC_GEOM_API IteratorSettings
|
||||
{
|
||||
public:
|
||||
/// Enumeration of setting identifiers. These settings define the
|
||||
/// behaviour of various aspects of IfcOpenShell.
|
||||
enum Setting
|
||||
{
|
||||
/// Specifies whether vertices are welded, meaning that the coordinates
|
||||
/// vector will only contain unique xyz-triplets. This results in a
|
||||
/// manifold mesh which is useful for modelling applications, but might
|
||||
/// result in unwanted shading artifacts in rendering applications.
|
||||
WELD_VERTICES = 1,
|
||||
/// Specifies whether to apply the local placements of building elements
|
||||
/// directly to the coordinates of the representation mesh rather than
|
||||
/// to represent the local placement in the 4x3 matrix, which will in that
|
||||
/// case be the identity matrix.
|
||||
USE_WORLD_COORDS = 1 << 1,
|
||||
/// Internally IfcOpenShell measures everything in meters. This settings
|
||||
/// specifies whether to convert IfcGeomObjects back to the units in which
|
||||
/// the geometry in the IFC file is specified.
|
||||
CONVERT_BACK_UNITS = 1 << 2,
|
||||
/// Specifies whether to use the Open Cascade BREP format for representation
|
||||
/// items rather than to create triangle meshes. This is useful is IfcOpenShell
|
||||
/// is used as a library in an application that is also built on Open Cascade.
|
||||
USE_BREP_DATA = 1 << 3,
|
||||
/// Specifies whether to sew IfcConnectedFaceSets (open and closed shells) to
|
||||
/// TopoDS_Shells or whether to keep them as a loose collection of faces.
|
||||
SEW_SHELLS = 1 << 4,
|
||||
/// Specifies whether to compose IfcOpeningElements into a single compound
|
||||
/// in order to speed up the processing of opening subtractions.
|
||||
FASTER_BOOLEANS = 1 << 5,
|
||||
/// Disables the subtraction of IfcOpeningElement representations from
|
||||
/// the related building element representations.
|
||||
DISABLE_OPENING_SUBTRACTIONS = 1 << 6,
|
||||
/// Disables the triangulation of the topological representations. Useful if
|
||||
/// the client application understands Open Cascade's native format.
|
||||
DISABLE_TRIANGULATION = 1 << 7,
|
||||
/// Applies default materials to entity instances without a surface style.
|
||||
APPLY_DEFAULT_MATERIALS = 1 << 8,
|
||||
/// Specifies whether to include subtypes of IfcCurve.
|
||||
INCLUDE_CURVES = 1 << 9,
|
||||
/// Specifies whether to exclude subtypes of IfcSolidModel and IfcSurface.
|
||||
EXCLUDE_SOLIDS_AND_SURFACES = 1 << 10,
|
||||
/// Disables computation of normals. Saves time and file size and is useful
|
||||
/// in instances where you're going to recompute normals for the exported
|
||||
/// model in other modelling application in any case.
|
||||
NO_NORMALS = 1 << 11,
|
||||
/// Generates UVs by using simple box projection. Requires normals.
|
||||
/// Applicable for OBJ and DAE output.
|
||||
GENERATE_UVS = 1 << 12,
|
||||
/// Specifies whether to slice representations according to associated IfcLayerSets.
|
||||
APPLY_LAYERSETS = 1 << 13,
|
||||
/// Search for a parent of type IfcBuildingStorey for each representation
|
||||
SEARCH_FLOOR = 1 << 14,
|
||||
///
|
||||
SITE_LOCAL_PLACEMENT = 1 << 15,
|
||||
///
|
||||
BUILDING_LOCAL_PLACEMENT = 1 << 16,
|
||||
/// Number of different setting flags.
|
||||
NUM_SETTINGS = 16
|
||||
};
|
||||
/// Used to store logical OR combination of setting flags.
|
||||
typedef unsigned SettingField;
|
||||
|
||||
class IteratorSettings {
|
||||
public:
|
||||
// Enumeration of setting identifiers. These settings define the
|
||||
// behaviour of various aspects of IfcOpenShell.
|
||||
IteratorSettings()
|
||||
: settings_(WELD_VERTICES) // OR options that default to true here
|
||||
, deflection_tolerance_(1.e-3)
|
||||
{
|
||||
}
|
||||
|
||||
// Specifies whether vertices are welded, meaning that the coordinates
|
||||
// vector will only contain unique xyz-triplets. This results in a
|
||||
// manifold mesh which is useful for modelling applications, but might
|
||||
// result in unwanted shading artifacts in rendering applications.
|
||||
static const int WELD_VERTICES = 1;
|
||||
// Specifies whether to apply the local placements of building elements
|
||||
// directly to the coordinates of the representation mesh rather than
|
||||
// to represent the local placement in the 4x3 matrix, which will in that
|
||||
// case be the identity matrix.
|
||||
static const int USE_WORLD_COORDS = 2;
|
||||
// Internally IfcOpenShell measures everything in meters. This settings
|
||||
// specifies whether to convert IfcGeomObjects back to the units in which
|
||||
// the geometry in the IFC file is specified.
|
||||
static const int CONVERT_BACK_UNITS = 3;
|
||||
// Specifies whether to use the Open Cascade BREP format for representation
|
||||
// items rather than to create triangle meshes. This is useful is IfcOpenShell
|
||||
// is used as a library in an application that is also built on Open Cascade.
|
||||
static const int USE_BREP_DATA = 4;
|
||||
// Specifies whether to sew IfcConnectedFaceSets (open and closed shells) to
|
||||
// TopoDS_Shells or whether to keep them as a loose collection of faces.
|
||||
static const int SEW_SHELLS = 5;
|
||||
// Specifies whether to compose IfcOpeningElements into a single compound
|
||||
// in order to speed up the processing of opening subtractions.
|
||||
static const int FASTER_BOOLEANS = 6;
|
||||
// By default singular faces have no explicitly defined orientation, to
|
||||
// force faces to be defined CounterClockWise set this to true.
|
||||
static const int FORCE_CCW_FACE_ORIENTATION = 7;
|
||||
// Disables the subtraction of IfcOpeningElement representations from
|
||||
// the related building element representations.
|
||||
static const int DISABLE_OPENING_SUBTRACTIONS = 8;
|
||||
// Disables the triangulation of the topological representations. Useful if
|
||||
// the client application understands Open Cascade's native format.
|
||||
static const int DISABLE_TRIANGULATION = 9;
|
||||
// Applies default materials to entity instances without a surface style.
|
||||
static const int APPLY_DEFAULT_MATERIALS = 10;
|
||||
/// Note that this is independent of the IFC length unit, one millimeter by default.
|
||||
double deflection_tolerance() const { return deflection_tolerance_; }
|
||||
|
||||
// End of settings enumeration.
|
||||
void set_deflection_tolerance(double value)
|
||||
{
|
||||
/// @todo Using deflection tolerance of 1e-6 or smaller hangs the conversion, research more in-depth.
|
||||
/// This bug can be reproduced e.g. with the Duplex model that can be found from http://www.nibs.org/?page=bsa_commonbimfiles#project1
|
||||
deflection_tolerance_ = value;
|
||||
if (deflection_tolerance_ <= 1e-6) {
|
||||
Logger::Message(Logger::LOG_WARNING, "Deflection tolerance cannot be set to <= 1e-6; using the default value 1e-3");
|
||||
deflection_tolerance_ = 1e-3;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool _weld_vertices, _use_world_coords, _convert_back_units, _use_brep_data, _sew_shells, _faster_booleans, _force_ccw_face_orientation, _disable_opening_subtractions, _disable_triangulation, _apply_default_materials;
|
||||
double _deflection_tolerance;
|
||||
public:
|
||||
IteratorSettings()
|
||||
: _weld_vertices(true)
|
||||
, _use_world_coords(false)
|
||||
, _convert_back_units(false)
|
||||
, _use_brep_data(false)
|
||||
, _sew_shells(false)
|
||||
, _faster_booleans(false)
|
||||
, _force_ccw_face_orientation(false)
|
||||
, _disable_opening_subtractions(false)
|
||||
, _disable_triangulation(false)
|
||||
, _apply_default_materials(false)
|
||||
// TODO: Make deflection tolerance into a command line argument
|
||||
// For now, stick to one millimeter. Note that this is independent of the IFC length unit.
|
||||
, _deflection_tolerance(1.e-3)
|
||||
{}
|
||||
/// Get boolean value for a single settings or for a combination of settings.
|
||||
bool get(SettingField setting) const
|
||||
{
|
||||
/// @todo If unknown setting value/combination: throw IfcParse::IfcException("Invalid IteratorSetting")?
|
||||
return (settings_ & setting) != 0;
|
||||
}
|
||||
|
||||
const bool& weld_vertices() const { return _weld_vertices; }
|
||||
bool& weld_vertices() { return _weld_vertices; }
|
||||
const bool& use_world_coords() const { return _use_world_coords; }
|
||||
bool& use_world_coords() { return _use_world_coords; }
|
||||
const bool& convert_back_units() const { return _convert_back_units; }
|
||||
bool& convert_back_units() { return _convert_back_units; }
|
||||
const bool& use_brep_data() const { return _use_brep_data; }
|
||||
bool& use_brep_data() { return _use_brep_data; }
|
||||
const bool& sew_shells() const { return _sew_shells; }
|
||||
bool& sew_shells() { return _sew_shells; }
|
||||
const bool& faster_booleans() const { return _faster_booleans; }
|
||||
bool& faster_booleans() { return _faster_booleans; }
|
||||
const bool& force_ccw_face_orientation() const { return _force_ccw_face_orientation; }
|
||||
bool& force_ccw_face_orientation() { return _force_ccw_face_orientation; }
|
||||
const bool& disable_opening_subtractions() const { return _disable_opening_subtractions; }
|
||||
bool& disable_opening_subtractions() { return _disable_opening_subtractions; }
|
||||
const bool& disable_triangulation() const { return _disable_triangulation; }
|
||||
bool& disable_triangulation() { return _disable_triangulation; }
|
||||
const bool& apply_default_materials() const { return _apply_default_materials; }
|
||||
bool& apply_default_materials() { return _apply_default_materials; }
|
||||
|
||||
const double& deflection_tolerance() const { return _deflection_tolerance; }
|
||||
double& deflection_tolerance() { return _deflection_tolerance; }
|
||||
|
||||
void set(int setting, bool value) {
|
||||
switch (setting) {
|
||||
case USE_WORLD_COORDS:
|
||||
_use_world_coords = value;
|
||||
break;
|
||||
case WELD_VERTICES:
|
||||
_weld_vertices = value;
|
||||
break;
|
||||
case CONVERT_BACK_UNITS:
|
||||
_convert_back_units = value;
|
||||
break;
|
||||
case USE_BREP_DATA:
|
||||
_use_brep_data = value;
|
||||
break;
|
||||
case FASTER_BOOLEANS:
|
||||
_faster_booleans = value;
|
||||
break;
|
||||
case SEW_SHELLS:
|
||||
_sew_shells = value;
|
||||
break;
|
||||
case FORCE_CCW_FACE_ORIENTATION:
|
||||
_force_ccw_face_orientation = value;
|
||||
break;
|
||||
case DISABLE_OPENING_SUBTRACTIONS:
|
||||
_disable_opening_subtractions = value;
|
||||
break;
|
||||
case DISABLE_TRIANGULATION:
|
||||
_disable_triangulation = value;
|
||||
break;
|
||||
case APPLY_DEFAULT_MATERIALS:
|
||||
_apply_default_materials = value;
|
||||
break;
|
||||
default: throw IfcParse::IfcException("Invalid IteratorSetting");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ElementSettings : public IteratorSettings {
|
||||
private:
|
||||
double _unit_magnitude;
|
||||
std::string _element_type;
|
||||
public:
|
||||
ElementSettings(const IteratorSettings& settings,
|
||||
double unit_magnitude,
|
||||
const std::string& element_type)
|
||||
: IteratorSettings(settings)
|
||||
, _unit_magnitude(unit_magnitude)
|
||||
, _element_type(element_type)
|
||||
{}
|
||||
/// Set boolean value for a single settings or for a combination of settings.
|
||||
void set(SettingField setting, bool value)
|
||||
{
|
||||
/// @todo If unknown setting value/combination: throw IfcParse::IfcException("Invalid IteratorSetting")?
|
||||
if (value) {
|
||||
settings_ |= setting;
|
||||
} else {
|
||||
settings_ &= ~setting;
|
||||
}
|
||||
}
|
||||
|
||||
const double& unit_magnitude() const { return _unit_magnitude; }
|
||||
const std::string& element_type() const { return _element_type; }
|
||||
};
|
||||
protected:
|
||||
SettingField settings_;
|
||||
double deflection_tolerance_;
|
||||
};
|
||||
|
||||
class IFC_GEOM_API ElementSettings : public IteratorSettings
|
||||
{
|
||||
public:
|
||||
ElementSettings(const IteratorSettings& settings,
|
||||
double unit_magnitude,
|
||||
const std::string& element_type)
|
||||
: IteratorSettings(settings)
|
||||
, unit_magnitude_(unit_magnitude)
|
||||
, element_type_(element_type)
|
||||
{
|
||||
}
|
||||
|
||||
double unit_magnitude() const { return unit_magnitude_; }
|
||||
const std::string& element_type() const { return element_type_; }
|
||||
|
||||
private:
|
||||
double unit_magnitude_;
|
||||
std::string element_type_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -30,5 +30,6 @@ const double* IfcGeom::Material::diffuse() const { if (hasDiffuse()) return &((*
|
||||
const double* IfcGeom::Material::specular() const { if (hasSpecular()) return &((*style->Specular()).R()); else return black; }
|
||||
double IfcGeom::Material::transparency() const { if (hasTransparency()) return *style->Transparency(); else return 0; }
|
||||
double IfcGeom::Material::specularity() const { if (hasSpecularity()) return *style->Specularity(); else return 0; }
|
||||
const std::string IfcGeom::Material::name() const { return style->Name(); }
|
||||
const std::string &IfcGeom::Material::name() const { return style->Name(); }
|
||||
const std::string &IfcGeom::Material::original_name() const { return style->original_name(); }
|
||||
bool IfcGeom::Material::operator==(const IfcGeom::Material& other) const { return style == other.style; }
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
class Material {
|
||||
class IFC_GEOM_API Material {
|
||||
private:
|
||||
const IfcGeom::SurfaceStyle* style;
|
||||
public:
|
||||
@@ -41,10 +41,11 @@ namespace IfcGeom {
|
||||
const double* specular() const;
|
||||
double transparency() const;
|
||||
double specularity() const;
|
||||
const std::string name() const;
|
||||
const std::string &name() const;
|
||||
const std::string &original_name() const;
|
||||
bool operator==(const Material& other) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -17,11 +17,17 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "IfcGeom.h"
|
||||
|
||||
bool process_colour(IfcSchema::IfcColourRgb* colour, std::tr1::array<double, 3>& rgb) {
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
bool process_colour(IfcSchema::IfcColourRgb* colour, double* rgb) {
|
||||
if (colour != 0) {
|
||||
rgb[0] = colour->Red();
|
||||
rgb[1] = colour->Green();
|
||||
@@ -30,7 +36,7 @@ bool process_colour(IfcSchema::IfcColourRgb* colour, std::tr1::array<double, 3>&
|
||||
return colour != 0;
|
||||
}
|
||||
|
||||
bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, std::tr1::array<double, 3>& rgb) {
|
||||
bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, double* rgb) {
|
||||
if (factor != 0) {
|
||||
const double f = *factor;
|
||||
rgb[0] = rgb[1] = rgb[2] = f;
|
||||
@@ -38,7 +44,7 @@ bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, std::tr1::arra
|
||||
return factor != 0;
|
||||
}
|
||||
|
||||
bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, std::tr1::array<double, 3>& rgb) {
|
||||
bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, double* rgb) {
|
||||
if (colour_or_factor == 0) {
|
||||
return false;
|
||||
} else if (colour_or_factor->is(IfcSchema::Type::IfcColourRgb)) {
|
||||
@@ -50,43 +56,42 @@ bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, std::tr1::ar
|
||||
}
|
||||
}
|
||||
|
||||
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcRepresentationItem* item) {
|
||||
std::pair<IfcSchema::IfcSurfaceStyle*, IfcSchema::IfcSurfaceStyleShading*> shading_styles = get_surface_style<IfcSchema::IfcSurfaceStyleShading>(item);
|
||||
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::internalize_surface_style(const std::pair<IfcSchema::IfcSurfaceStyle*, IfcSchema::IfcSurfaceStyleShading*>& shading_styles) {
|
||||
if (shading_styles.second == 0) {
|
||||
return 0;
|
||||
}
|
||||
int surface_style_id = shading_styles.first->entity->id();
|
||||
std::map<int,SurfaceStyle>::const_iterator it = cache.Style.find(surface_style_id);
|
||||
if (it != cache.Style.end()) {
|
||||
std::map<int,SurfaceStyle>::const_iterator it = style_cache.find(surface_style_id);
|
||||
if (it != style_cache.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
SurfaceStyle surface_style;
|
||||
if (shading_styles.first->Name()) {
|
||||
surface_style = SurfaceStyle(surface_style_id, *shading_styles.first->Name());
|
||||
if (shading_styles.first->hasName()) {
|
||||
surface_style = SurfaceStyle(surface_style_id, shading_styles.first->Name());
|
||||
} else {
|
||||
surface_style = SurfaceStyle(surface_style_id);
|
||||
}
|
||||
std::tr1::array<double, 3> rgb;
|
||||
double rgb[3];
|
||||
if (process_colour(shading_styles.second->SurfaceColour(), rgb)) {
|
||||
surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]));
|
||||
}
|
||||
if (shading_styles.second->is(IfcSchema::Type::IfcSurfaceStyleRendering)) {
|
||||
IfcSchema::IfcSurfaceStyleRendering* rendering_style = static_cast<IfcSchema::IfcSurfaceStyleRendering*>(shading_styles.second);
|
||||
if (rendering_style->DiffuseColour() && process_colour(*rendering_style->DiffuseColour(), rgb)) {
|
||||
if (rendering_style->hasDiffuseColour() && process_colour(rendering_style->DiffuseColour(), rgb)) {
|
||||
SurfaceStyle::ColorComponent diffuse = surface_style.Diffuse().get_value_or(SurfaceStyle::ColorComponent(1,1,1));
|
||||
surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(diffuse.R() * rgb[0], diffuse.G() * rgb[1], diffuse.B() * rgb[2]));
|
||||
}
|
||||
if (rendering_style->DiffuseTransmissionColour()) {
|
||||
if (rendering_style->hasDiffuseTransmissionColour()) {
|
||||
// Not supported
|
||||
}
|
||||
if (rendering_style->ReflectionColour()) {
|
||||
if (rendering_style->hasReflectionColour()) {
|
||||
// Not supported
|
||||
}
|
||||
if (rendering_style->SpecularColour() && process_colour(*rendering_style->SpecularColour(), rgb)) {
|
||||
if (rendering_style->hasSpecularColour() && process_colour(rendering_style->SpecularColour(), rgb)) {
|
||||
surface_style.Specular().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]));
|
||||
}
|
||||
if (rendering_style->SpecularHighlight()) {
|
||||
IfcSchema::IfcSpecularHighlightSelect* highlight = *rendering_style->SpecularHighlight();
|
||||
if (rendering_style->hasSpecularHighlight()) {
|
||||
IfcSchema::IfcSpecularHighlightSelect* highlight = rendering_style->SpecularHighlight();
|
||||
if (highlight->is(IfcSchema::Type::IfcSpecularRoughness)) {
|
||||
double roughness = *((IfcSchema::IfcSpecularRoughness*)highlight);
|
||||
if (roughness >= 1e-9) {
|
||||
@@ -96,15 +101,38 @@ const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcRepr
|
||||
surface_style.Specularity().reset(*((IfcSchema::IfcSpecularExponent*)highlight));
|
||||
}
|
||||
}
|
||||
if (rendering_style->TransmissionColour()) {
|
||||
if (rendering_style->hasTransmissionColour()) {
|
||||
// Not supported
|
||||
}
|
||||
if (rendering_style->Transparency()) {
|
||||
const double d = *rendering_style->Transparency();
|
||||
if (rendering_style->hasTransparency()) {
|
||||
const double d = rendering_style->Transparency();
|
||||
surface_style.Transparency().reset(d);
|
||||
}
|
||||
}
|
||||
return &(cache.Style[surface_style_id] = surface_style);
|
||||
return &(style_cache[surface_style_id] = surface_style);
|
||||
}
|
||||
|
||||
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcRepresentationItem* item) {
|
||||
return internalize_surface_style(get_surface_style<IfcSchema::IfcSurfaceStyleShading>(item));
|
||||
}
|
||||
|
||||
const IfcGeom::SurfaceStyle* IfcGeom::Kernel::get_style(const IfcSchema::IfcMaterial* material) {
|
||||
IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation();
|
||||
for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) {
|
||||
IfcSchema::IfcRepresentation::list::ptr reps = (*jt)->Representations();
|
||||
IfcSchema::IfcStyledItem::list::ptr styles(new IfcSchema::IfcStyledItem::list);
|
||||
for (IfcSchema::IfcRepresentation::list::it it = reps->begin(); it != reps->end(); ++it) {
|
||||
styles->push((**it).Items()->as<IfcSchema::IfcStyledItem>());
|
||||
}
|
||||
for (IfcSchema::IfcStyledItem::list::it it = styles->begin(); it != styles->end(); ++it) {
|
||||
const std::pair<IfcSchema::IfcSurfaceStyle*, IfcSchema::IfcSurfaceStyleShading*> ss = get_surface_style<IfcSchema::IfcSurfaceStyleShading>(*it);
|
||||
if (ss.second) {
|
||||
return internalize_surface_style(ss);
|
||||
}
|
||||
}
|
||||
}
|
||||
IfcGeom::SurfaceStyle material_style = IfcGeom::SurfaceStyle(material->id(), material->Name());
|
||||
return &(style_cache[material->id()] = material_style);
|
||||
}
|
||||
|
||||
static std::map<std::string, IfcGeom::SurfaceStyle> default_materials;
|
||||
@@ -149,12 +177,73 @@ void InitDefaultMaterials() {
|
||||
default_materials_initialized = true;
|
||||
}
|
||||
|
||||
boost::optional<IfcGeom::SurfaceStyle::ColorComponent> read_colour_component(const boost::optional<pt::ptree&> list) {
|
||||
if (!list) {
|
||||
return boost::none;
|
||||
}
|
||||
double rgb[3];
|
||||
int i = 0;
|
||||
for (pt::ptree::value_type &colour : list.get()) {
|
||||
if (3 <= i) {
|
||||
throw std::runtime_error("rgb array over 3 elements large");
|
||||
}
|
||||
rgb[i] = colour.second.get_value<double>();
|
||||
i++;
|
||||
}
|
||||
if (i != 3) {
|
||||
throw std::runtime_error("rgb array less than 3 elements large (was " + std::to_string(i) + ")");
|
||||
}
|
||||
return IfcGeom::SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
void IfcGeom::set_default_style_file(const std::string& json_file) {
|
||||
if (!default_materials_initialized) InitDefaultMaterials();
|
||||
default_materials.clear();
|
||||
|
||||
// @todo this will probably need to be updated for UTF-8 paths on Windows
|
||||
pt::ptree root;
|
||||
pt::read_json(json_file, root);
|
||||
|
||||
for (pt::ptree::value_type &material_pair : root) {
|
||||
std::string name = material_pair.first;
|
||||
default_materials.insert(std::make_pair(name, IfcGeom::SurfaceStyle(name)));
|
||||
|
||||
pt::ptree material = material_pair.second;
|
||||
boost::optional<pt::ptree&> diffuse = material.get_child_optional("diffuse");
|
||||
default_materials[name].Diffuse() = read_colour_component(diffuse);
|
||||
|
||||
boost::optional<pt::ptree&> specular = material.get_child_optional("specular");
|
||||
default_materials[name].Specular() = read_colour_component(specular);
|
||||
|
||||
if (material.get_child_optional("specular-roughness")) {
|
||||
default_materials[name].Specularity().reset(1.0 / material.get<double>("specular-roughness"));
|
||||
}
|
||||
if (material.get_child_optional("transparency")) {
|
||||
default_materials[name].Transparency() = material.get<double>("transparency");
|
||||
}
|
||||
}
|
||||
|
||||
// Is "*" present? If yes, remove it and make it the default style.
|
||||
std::map<std::string, IfcGeom::SurfaceStyle>::const_iterator it = default_materials.find("*");
|
||||
if (it != default_materials.end()) {
|
||||
IfcGeom::SurfaceStyle star = it->second;
|
||||
default_material.Diffuse() = star.Diffuse();
|
||||
default_material.Specular() = star.Specular();
|
||||
default_material.Specularity() = star.Specularity();
|
||||
default_material.Transparency() = star.Transparency();
|
||||
default_materials.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
const IfcGeom::SurfaceStyle* IfcGeom::get_default_style(const std::string& s) {
|
||||
if (!default_materials_initialized) InitDefaultMaterials();
|
||||
std::map<std::string, IfcGeom::SurfaceStyle>::const_iterator it = default_materials.find(s);
|
||||
if (it == default_materials.end()) {
|
||||
default_materials.insert(std::make_pair(s, IfcGeom::SurfaceStyle(s)));
|
||||
default_materials[s].Diffuse().reset(*default_material.Diffuse());
|
||||
default_materials[s].Diffuse() = default_material.Diffuse();
|
||||
default_materials[s].Specular() = default_material.Specular();
|
||||
default_materials[s].Specularity() = default_material.Specularity();
|
||||
default_materials[s].Transparency() = default_material.Transparency();
|
||||
it = default_materials.find(s);
|
||||
}
|
||||
const IfcGeom::SurfaceStyle& surface_style = it->second;
|
||||
|
||||
@@ -20,24 +20,22 @@
|
||||
#ifndef IFCGEOMRENDERSTYLES_H
|
||||
#define IFCGEOMRENDERSTYLES_H
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <tr1/array>
|
||||
#else
|
||||
#include <array>
|
||||
#endif
|
||||
|
||||
#include "ifc_geom_api.h"
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
namespace IfcGeom {
|
||||
class SurfaceStyle {
|
||||
class IFC_GEOM_API SurfaceStyle {
|
||||
public:
|
||||
class ColorComponent {
|
||||
private:
|
||||
std::tr1::array<double, 3> data;
|
||||
double data[3];
|
||||
public:
|
||||
ColorComponent(double r, double g, double b) {
|
||||
data[0] = r; data[1] = g; data[2] = b;
|
||||
@@ -50,24 +48,27 @@ namespace IfcGeom {
|
||||
double& B() { return data[2]; }
|
||||
};
|
||||
private:
|
||||
boost::optional<std::string> name;
|
||||
std::string name;
|
||||
std::string original_name_;
|
||||
boost::optional<int> id;
|
||||
boost::optional<ColorComponent> diffuse, specular;
|
||||
boost::optional<double> transparency;
|
||||
boost::optional<double> specularity;
|
||||
public:
|
||||
SurfaceStyle() {
|
||||
this->name = "IfcSurfaceStyleShading";
|
||||
}
|
||||
SurfaceStyle() : name("surface-style") {}
|
||||
SurfaceStyle(int id) : id(id) {
|
||||
std::stringstream sstr;
|
||||
sstr << "IfcSurfaceStyleShading_" << id;
|
||||
sstr << "surface-style-" << id;
|
||||
this->name = sstr.str();
|
||||
}
|
||||
SurfaceStyle(const std::string& name) : name(name) {}
|
||||
SurfaceStyle(int id, const std::string& name) : id(id) {
|
||||
SurfaceStyle(const std::string& name) : name(name), original_name_(name) {}
|
||||
SurfaceStyle(int id, const std::string& name) : original_name_(name), id(id)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << id << "_" << name;
|
||||
std::string sanitized = name;
|
||||
boost::to_lower(sanitized);
|
||||
boost::replace_all(sanitized, " ", "-");
|
||||
sstr << "surface-style-" << id << "-" << sanitized;
|
||||
this->name = sstr.str();
|
||||
}
|
||||
|
||||
@@ -76,16 +77,14 @@ namespace IfcGeom {
|
||||
// pointer addresses of the styles, as they are always referenced
|
||||
// from out of a global map of some sort.
|
||||
bool operator==(const SurfaceStyle& other) {
|
||||
if (name && other.name) {
|
||||
return *name == *other.name;
|
||||
} else if (id && other.id) {
|
||||
return *id == *other.id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return name == other.name;
|
||||
}
|
||||
|
||||
const std::string& Name() const { return *name; }
|
||||
/// ID name, e.g. "surface-style-66675-metal---aluminium"
|
||||
const std::string& Name() const { return name; }
|
||||
|
||||
/// Original name, if available, e.g. "Metal - Aluminium"
|
||||
const std::string& original_name() const { return original_name_; }
|
||||
|
||||
const boost::optional<ColorComponent>& Diffuse() const { return diffuse; }
|
||||
const boost::optional<ColorComponent>& Specular() const { return specular; }
|
||||
@@ -97,7 +96,8 @@ namespace IfcGeom {
|
||||
boost::optional<double>& Specularity() { return specularity; }
|
||||
};
|
||||
|
||||
const SurfaceStyle* get_default_style(const std::string& ifc_type);
|
||||
IFC_GEOM_API const SurfaceStyle* get_default_style(const std::string& ifc_type);
|
||||
IFC_GEOM_API void set_default_style_file(const std::string& json_file);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <BRep_Builder.hxx>
|
||||
|
||||
#include <TopoDS_Compound.hxx>
|
||||
#include <BRepBuilderAPI_GTransform.hxx>
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
@@ -30,30 +29,47 @@
|
||||
|
||||
IfcGeom::Representation::Serialization::Serialization(const BRep& brep)
|
||||
: Representation(brep.settings())
|
||||
, _id(brep.getId())
|
||||
, id_(brep.id())
|
||||
{
|
||||
TopoDS_Compound compound = brep.as_compound();
|
||||
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = brep.begin(); it != brep.end(); ++ it) {
|
||||
if (it->hasStyle() && it->Style().Diffuse()) {
|
||||
const IfcGeom::SurfaceStyle::ColorComponent& clr = *it->Style().Diffuse();
|
||||
surface_styles_.push_back(clr.R());
|
||||
surface_styles_.push_back(clr.G());
|
||||
surface_styles_.push_back(clr.B());
|
||||
} else {
|
||||
surface_styles_.push_back(-1.);
|
||||
surface_styles_.push_back(-1.);
|
||||
surface_styles_.push_back(-1.);
|
||||
}
|
||||
if (it->hasStyle() && it->Style().Transparency()) {
|
||||
surface_styles_.push_back(1. - *it->Style().Transparency());
|
||||
} else {
|
||||
surface_styles_.push_back(1.);
|
||||
}
|
||||
}
|
||||
std::stringstream sstream;
|
||||
BRepTools::Write(compound,sstream);
|
||||
brep_data_ = sstream.str();
|
||||
}
|
||||
|
||||
TopoDS_Compound IfcGeom::Representation::BRep::as_compound() const {
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
builder.MakeCompound(compound);
|
||||
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = brep.begin(); it != brep.end(); ++ it) {
|
||||
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = begin(); it != end(); ++it) {
|
||||
const TopoDS_Shape& s = it->Shape();
|
||||
gp_GTrsf trsf = it->Placement();
|
||||
if (settings().convert_back_units()) {
|
||||
|
||||
if (settings().get(IteratorSettings::CONVERT_BACK_UNITS)) {
|
||||
gp_Trsf scale;
|
||||
scale.SetScaleFactor(1.0 / settings().unit_magnitude());
|
||||
trsf.PreMultiply(scale);
|
||||
}
|
||||
bool trsf_valid = false;
|
||||
gp_Trsf _trsf;
|
||||
try {
|
||||
_trsf = trsf.Trsf();
|
||||
trsf_valid = true;
|
||||
} catch (...) {}
|
||||
const TopoDS_Shape moved_shape = trsf_valid ? s.Moved(_trsf) :
|
||||
BRepBuilderAPI_GTransform(s,trsf,true).Shape();
|
||||
builder.Add(compound,moved_shape);
|
||||
|
||||
const TopoDS_Shape moved_shape = IfcGeom::Kernel::apply_transformation(s, trsf);
|
||||
builder.Add(compound, moved_shape);
|
||||
}
|
||||
std::stringstream sstream;
|
||||
BRepTools::Write(compound,sstream);
|
||||
_brep_data = sstream.str();
|
||||
return compound;
|
||||
}
|
||||
@@ -28,53 +28,66 @@
|
||||
#include <TColgp_Array1OfPnt2d.hxx>
|
||||
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
|
||||
#include <BRepAdaptor_Curve.hxx>
|
||||
#include <GCPnts_QuasiUniformDeflection.hxx>
|
||||
#include <Geom_SphericalSurface.hxx>
|
||||
|
||||
#include "../ifcgeom/IfcGeomIteratorSettings.h"
|
||||
#include "../ifcgeom/IfcGeomMaterial.h"
|
||||
#include "../ifcgeom/IfcRepresentationShapeItem.h"
|
||||
|
||||
#include <TopoDS_Compound.hxx>
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
namespace Representation {
|
||||
|
||||
class Representation {
|
||||
class IFC_GEOM_API Representation {
|
||||
Representation(const Representation&); //N/A
|
||||
Representation& operator =(const Representation&); //N/A
|
||||
protected:
|
||||
const ElementSettings _settings;
|
||||
const ElementSettings settings_;
|
||||
public:
|
||||
explicit Representation(const ElementSettings& settings)
|
||||
: _settings(settings)
|
||||
: settings_(settings)
|
||||
{}
|
||||
const ElementSettings& settings() const { return _settings; }
|
||||
const ElementSettings& settings() const { return settings_; }
|
||||
virtual ~Representation() {}
|
||||
};
|
||||
|
||||
class BRep : public Representation {
|
||||
class IFC_GEOM_API BRep : public Representation {
|
||||
private:
|
||||
unsigned int id;
|
||||
const IfcGeom::IfcRepresentationShapeItems shapes;
|
||||
std::string id_;
|
||||
const IfcGeom::IfcRepresentationShapeItems shapes_;
|
||||
BRep(const BRep& other);
|
||||
BRep& operator=(const BRep& other);
|
||||
public:
|
||||
BRep(const ElementSettings& settings, unsigned int id, const IfcGeom::IfcRepresentationShapeItems& shapes)
|
||||
BRep(const ElementSettings& settings, const std::string& id, const IfcGeom::IfcRepresentationShapeItems& shapes)
|
||||
: Representation(settings)
|
||||
, id(id)
|
||||
, shapes(shapes)
|
||||
, id_(id)
|
||||
, shapes_(shapes)
|
||||
{}
|
||||
virtual ~BRep() {}
|
||||
IfcGeom::IfcRepresentationShapeItems::const_iterator begin() const { return shapes.begin(); }
|
||||
IfcGeom::IfcRepresentationShapeItems::const_iterator end() const { return shapes.end(); }
|
||||
const unsigned int& getId() const { return id; }
|
||||
IfcGeom::IfcRepresentationShapeItems::const_iterator begin() const { return shapes_.begin(); }
|
||||
IfcGeom::IfcRepresentationShapeItems::const_iterator end() const { return shapes_.end(); }
|
||||
const IfcGeom::IfcRepresentationShapeItems& shapes() const { return shapes_; }
|
||||
const std::string& id() const { return id_; }
|
||||
TopoDS_Compound as_compound() const;
|
||||
};
|
||||
|
||||
class Serialization : public Representation {
|
||||
class IFC_GEOM_API Serialization : public Representation {
|
||||
private:
|
||||
int _id;
|
||||
std::string _brep_data;
|
||||
std::string id_;
|
||||
std::string brep_data_;
|
||||
std::vector<double> surface_styles_;
|
||||
public:
|
||||
int id() const { return _id; }
|
||||
const std::string& brep_data() const { return _brep_data; }
|
||||
const std::string& brep_data() const { return brep_data_; }
|
||||
const std::vector<double>& surface_styles() const { return surface_styles_; }
|
||||
Serialization(const BRep& brep);
|
||||
virtual ~Serialization() {}
|
||||
const std::string& id() const { return id_; }
|
||||
private:
|
||||
Serialization();
|
||||
Serialization(const Serialization&);
|
||||
@@ -91,54 +104,57 @@ namespace IfcGeom {
|
||||
typedef std::map<VertexKey, int> VertexKeyMap;
|
||||
typedef std::pair<int, int> Edge;
|
||||
|
||||
int _id;
|
||||
std::string id_;
|
||||
std::vector<P> _verts;
|
||||
std::vector<int> _faces;
|
||||
std::vector<int> _edges;
|
||||
std::vector<P> _normals;
|
||||
std::vector<P> uvs_;
|
||||
std::vector<int> _material_ids;
|
||||
std::vector<Material> _materials;
|
||||
VertexKeyMap welds;
|
||||
|
||||
public:
|
||||
int id() const { return _id; }
|
||||
const std::string& id() const { return id_; }
|
||||
const std::vector<P>& verts() const { return _verts; }
|
||||
const std::vector<int>& faces() const { return _faces; }
|
||||
const std::vector<int>& edges() const { return _edges; }
|
||||
const std::vector<P>& normals() const { return _normals; }
|
||||
const std::vector<P>& uvs() const { return uvs_; }
|
||||
const std::vector<int>& material_ids() const { return _material_ids; }
|
||||
const std::vector<Material>& materials() const { return _materials; }
|
||||
|
||||
Triangulation(const BRep& shape_model)
|
||||
: Representation(shape_model.settings())
|
||||
, _id(shape_model.getId())
|
||||
, id_(shape_model.id())
|
||||
{
|
||||
for ( IfcGeom::IfcRepresentationShapeItems::const_iterator it = shape_model.begin(); it != shape_model.end(); ++ it ) {
|
||||
for ( IfcGeom::IfcRepresentationShapeItems::const_iterator iit = shape_model.begin(); iit != shape_model.end(); ++ iit ) {
|
||||
|
||||
int surface_style_id = -1;
|
||||
if (it->hasStyle()) {
|
||||
Material adapter(&it->Style());
|
||||
if (iit->hasStyle()) {
|
||||
Material adapter(&iit->Style());
|
||||
std::vector<Material>::const_iterator jt = std::find(_materials.begin(), _materials.end(), adapter);
|
||||
if (jt == _materials.end()) {
|
||||
surface_style_id = _materials.size();
|
||||
surface_style_id = (int)_materials.size();
|
||||
_materials.push_back(adapter);
|
||||
} else {
|
||||
surface_style_id = jt - _materials.begin();
|
||||
surface_style_id = (int)(jt - _materials.begin());
|
||||
}
|
||||
}
|
||||
|
||||
if (settings().apply_default_materials() && surface_style_id == -1) {
|
||||
if (settings().get(IteratorSettings::APPLY_DEFAULT_MATERIALS) && surface_style_id == -1) {
|
||||
Material material(IfcGeom::get_default_style(settings().element_type()));
|
||||
std::vector<Material>::const_iterator it = std::find(_materials.begin(), _materials.end(), material);
|
||||
if (it == _materials.end()) {
|
||||
surface_style_id = _materials.size();
|
||||
std::vector<Material>::const_iterator mit = std::find(_materials.begin(), _materials.end(), material);
|
||||
if (mit == _materials.end()) {
|
||||
surface_style_id = (int)_materials.size();
|
||||
_materials.push_back(material);
|
||||
} else {
|
||||
surface_style_id = it - _materials.begin();
|
||||
surface_style_id = (int)(mit - _materials.begin());
|
||||
}
|
||||
}
|
||||
|
||||
const TopoDS_Shape& s = it->Shape();
|
||||
const gp_GTrsf& trsf = it->Placement();
|
||||
const TopoDS_Shape& s = iit->Shape();
|
||||
const gp_GTrsf& trsf = iit->Placement();
|
||||
|
||||
// Triangulate the shape
|
||||
try {
|
||||
@@ -150,10 +166,11 @@ namespace IfcGeom {
|
||||
Logger::Message(Logger::LOG_ERROR,"Failed to triangulate shape");
|
||||
continue;
|
||||
}
|
||||
TopExp_Explorer exp;
|
||||
|
||||
// Iterates over the faces of the shape
|
||||
for ( exp.Init(s,TopAbs_FACE); exp.More(); exp.Next() ) {
|
||||
int num_faces = 0;
|
||||
TopExp_Explorer exp;
|
||||
for ( exp.Init(s,TopAbs_FACE); exp.More(); exp.Next(), ++num_faces ) {
|
||||
TopoDS_Face face = TopoDS::Face(exp.Current());
|
||||
TopLoc_Location loc;
|
||||
Handle_Poly_Triangulation tri = BRep_Tool::Triangulation(face,loc);
|
||||
@@ -174,8 +191,9 @@ namespace IfcGeom {
|
||||
BRepGProp_Face prop(face);
|
||||
std::map<int,int> dict;
|
||||
|
||||
// Vertex normals are only calculated if vertices are not welded
|
||||
const bool calculate_normals = !settings().weld_vertices();
|
||||
// Vertex normals are only calculated if vertices are not welded and calculation is not disable explicitly.
|
||||
const bool calculate_normals = !settings().get(IteratorSettings::WELD_VERTICES) &&
|
||||
!settings().get(IteratorSettings::NO_NORMALS);
|
||||
|
||||
for( int i = 1; i <= nodes.Length(); ++ i ) {
|
||||
coords.push_back(nodes(i).Transformed(loc).XYZ());
|
||||
@@ -190,10 +208,22 @@ namespace IfcGeom {
|
||||
gp_Vec normal(0., 0., 0.);
|
||||
if (normal_direction.Magnitude() > ALMOST_ZERO) {
|
||||
normal = gp_Dir(normal_direction.XYZ() * rotation_matrix);
|
||||
} else {
|
||||
Handle_Geom_Surface surf = BRep_Tool::Surface(face);
|
||||
// Special case the normal at the poles of a spherical surface
|
||||
if (surf->DynamicType() == STANDARD_TYPE(Geom_SphericalSurface)) {
|
||||
if (ALMOST_THE_SAME(fabs(uv.Y()), M_PI / 2.)) {
|
||||
const bool is_top = uv.Y() > 0;
|
||||
const bool is_forward = face.Orientation() == TopAbs_FORWARD;
|
||||
const double z = (is_top == is_forward) ? 1. : -1.;
|
||||
normal = gp_Dir(gp_XYZ(0, 0, z) * rotation_matrix);
|
||||
}
|
||||
}
|
||||
// TODO: Do the same for conical surfaces, but they are rare in IFC.
|
||||
}
|
||||
_normals.push_back((float)normal.X());
|
||||
_normals.push_back((float)normal.Y());
|
||||
_normals.push_back((float)normal.Z());
|
||||
_normals.push_back(static_cast<P>(normal.X()));
|
||||
_normals.push_back(static_cast<P>(normal.Y()));
|
||||
_normals.push_back(static_cast<P>(normal.Z()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,26 +254,137 @@ namespace IfcGeom {
|
||||
|
||||
_material_ids.push_back(surface_style_id);
|
||||
|
||||
addEdge(n1,n2,edgecount,edges_temp);
|
||||
addEdge(n2,n3,edgecount,edges_temp);
|
||||
addEdge(n3,n1,edgecount,edges_temp);
|
||||
addEdge(dict[n1], dict[n2], edgecount, edges_temp);
|
||||
addEdge(dict[n2], dict[n3], edgecount, edges_temp);
|
||||
addEdge(dict[n3], dict[n1], edgecount, edges_temp);
|
||||
}
|
||||
for ( std::vector<std::pair<int,int> >::const_iterator it = edges_temp.begin(); it != edges_temp.end(); ++it ) {
|
||||
_edges.push_back(edgecount[*it]==1);
|
||||
for ( std::vector<std::pair<int,int> >::const_iterator jt = edges_temp.begin(); jt != edges_temp.end(); ++jt ) {
|
||||
if (edgecount[*jt] == 1) {
|
||||
// non manifold edge, face boundary
|
||||
_edges.push_back(jt->first);
|
||||
_edges.push_back(jt->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_normals.empty() && settings().get(IfcGeom::IteratorSettings::GENERATE_UVS)) {
|
||||
uvs_ = box_project_uvs(_verts, _normals);
|
||||
}
|
||||
|
||||
if (num_faces == 0) {
|
||||
// Edges are only emitted if there are no faces. A mixed representation of faces
|
||||
// and loose edges is discouraged by the standard. An alternative would be to use
|
||||
// TopExp_Explorer texp(s, TopAbs_EDGE, TopAbs_FACE) to find edges that do not
|
||||
// belong to any face.
|
||||
for (TopExp_Explorer texp(s, TopAbs_EDGE); texp.More(); texp.Next()) {
|
||||
BRepAdaptor_Curve crv(TopoDS::Edge(texp.Current()));
|
||||
GCPnts_QuasiUniformDeflection tessellater(crv, settings().deflection_tolerance());
|
||||
int n = tessellater.NbPoints();
|
||||
int start = (int)_verts.size() / 3;
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
gp_XYZ p = tessellater.Value(i).XYZ();
|
||||
|
||||
/*
|
||||
// In case you want direction arrows on your edges
|
||||
double u = tessellater.Parameter(i);
|
||||
gp_XYZ p2, p3;
|
||||
gp_Pnt tmp;
|
||||
gp_Vec tmp2;
|
||||
crv.D1(u, tmp, tmp2);
|
||||
gp_Dir d1, d2, d3, d4;
|
||||
d1 = tmp2;
|
||||
if (texp.Current().Orientation() == TopAbs_REVERSED) {
|
||||
d1 = -d1;
|
||||
}
|
||||
if (fabs(d1.Z()) < 0.5) {
|
||||
d2 = d1.Crossed(gp::DZ());
|
||||
} else {
|
||||
d2 = d1.Crossed(gp::DY());
|
||||
}
|
||||
d3 = d1.XYZ() + d2.XYZ();
|
||||
d4 = d1.XYZ() - d2.XYZ();
|
||||
p2 = p - d3.XYZ() / 10.;
|
||||
p3 = p - d4.XYZ() / 10.;
|
||||
trsf.Transforms(p2);
|
||||
trsf.Transforms(p3);
|
||||
_material_ids.push_back(surface_style_id);
|
||||
_material_ids.push_back(surface_style_id);
|
||||
_verts.push_back(static_cast<P>(p2.X()));
|
||||
_verts.push_back(static_cast<P>(p2.Y()));
|
||||
_verts.push_back(static_cast<P>(p2.Z()));
|
||||
_verts.push_back(static_cast<P>(p3.X()));
|
||||
_verts.push_back(static_cast<P>(p3.Y()));
|
||||
_verts.push_back(static_cast<P>(p3.Z()));
|
||||
*/
|
||||
|
||||
trsf.Transforms(p);
|
||||
|
||||
_material_ids.push_back(surface_style_id);
|
||||
|
||||
_verts.push_back(static_cast<P>(p.X()));
|
||||
_verts.push_back(static_cast<P>(p.Y()));
|
||||
_verts.push_back(static_cast<P>(p.Z()));
|
||||
|
||||
if (i > 1) {
|
||||
_edges.push_back(start + i - 2);
|
||||
_edges.push_back(start + i - 1);
|
||||
// _edges.push_back(start + 3 * (i - 2) + 2);
|
||||
// _edges.push_back(start + 3 * (i - 1) + 2);
|
||||
}
|
||||
|
||||
// _edges.push_back(start + 3 * (i - 1) + 0);
|
||||
// _edges.push_back(start + 3 * (i - 1) + 2);
|
||||
// _edges.push_back(start + 3 * (i - 1) + 1);
|
||||
// _edges.push_back(start + 3 * (i - 1) + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BRepTools::Clean(s);
|
||||
}
|
||||
}
|
||||
virtual ~Triangulation() {}
|
||||
|
||||
/// Generates UVs for a single mesh using box projection.
|
||||
/// @todo Very simple impl. Assumes that input vertices and normals match 1:1.
|
||||
static std::vector<P> box_project_uvs(const std::vector<P> &vertices, const std::vector<P> &normals)
|
||||
{
|
||||
std::vector<P> uvs;
|
||||
uvs.resize(vertices.size() / 3 * 2);
|
||||
for (size_t uv_idx = 0, v_idx = 0;
|
||||
uv_idx < uvs.size() && v_idx < vertices.size() && v_idx < normals.size();
|
||||
uv_idx += 2, v_idx += 3) {
|
||||
|
||||
P n_x = normals[v_idx], n_y = normals[v_idx + 1], n_z = normals[v_idx + 2];
|
||||
P v_x = vertices[v_idx], v_y = vertices[v_idx + 1], v_z = vertices[v_idx + 2];
|
||||
|
||||
if (std::abs(n_x) > std::abs(n_y) && std::abs(n_x) > std::abs(n_z)) {
|
||||
uvs[uv_idx] = v_z;
|
||||
uvs[uv_idx + 1] = v_y;
|
||||
}
|
||||
if (std::abs(n_y) > std::abs(n_x) && std::abs(n_y) > std::abs(n_z)) {
|
||||
uvs[uv_idx] = v_x;
|
||||
uvs[uv_idx + 1] = v_z;
|
||||
}
|
||||
if (std::abs(n_z) > std::abs(n_x) && std::abs(n_z) > std::abs(n_y)) {
|
||||
uvs[uv_idx] = v_x;
|
||||
uvs[uv_idx + 1] = v_y;
|
||||
}
|
||||
}
|
||||
|
||||
return uvs;
|
||||
}
|
||||
|
||||
private:
|
||||
// Welds vertices that belong to different faces
|
||||
int addVertex(int material_index, const gp_XYZ& p) {
|
||||
const P X = static_cast<P>(settings().convert_back_units() ? (p.X() / settings().unit_magnitude()) : p.X());
|
||||
const P Y = static_cast<P>(settings().convert_back_units() ? (p.Y() / settings().unit_magnitude()) : p.Y());
|
||||
const P Z = static_cast<P>(settings().convert_back_units() ? (p.Z() / settings().unit_magnitude()) : p.Z());
|
||||
const bool convert = settings().get(IteratorSettings::CONVERT_BACK_UNITS);
|
||||
const P X = static_cast<P>(convert ? (p.X() / settings().unit_magnitude()) : p.X());
|
||||
const P Y = static_cast<P>(convert ? (p.Y() / settings().unit_magnitude()) : p.Y());
|
||||
const P Z = static_cast<P>(convert ? (p.Z() / settings().unit_magnitude()) : p.Z());
|
||||
int i = (int) _verts.size() / 3;
|
||||
if (settings().weld_vertices()) {
|
||||
if (settings().get(IteratorSettings::WELD_VERTICES)) {
|
||||
const VertexKey key = std::make_pair(material_index, std::make_pair(X, std::make_pair(Y, Z)));
|
||||
typename VertexKeyMap::const_iterator it = welds.find(key);
|
||||
if ( it != welds.end() ) return it->second;
|
||||
@@ -269,4 +410,4 @@ namespace IfcGeom {
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,660 @@
|
||||
#include <Geom_Line.hxx>
|
||||
#include <Geom_Circle.hxx>
|
||||
#include <Geom_Ellipse.hxx>
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
#include <Geom_CylindricalSurface.hxx>
|
||||
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
|
||||
#include <TColgp_Array2OfPnt.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array2OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
|
||||
#include "IfcGeom.h"
|
||||
|
||||
template <typename T, typename U>
|
||||
int convert_to_ifc(const T& t, U*& u, bool /*advanced*/) {
|
||||
std::vector<double> coords(3);
|
||||
coords[0] = t.X(); coords[1] = t.Y(); coords[2] = t.Z();
|
||||
u = new U(coords);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcCartesianPoint*& p, bool advanced) {
|
||||
gp_Pnt pnt = BRep_Tool::Pnt(v);
|
||||
return convert_to_ifc(pnt, p, advanced);
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Vertex& v, IfcSchema::IfcVertex*& vertex, bool advanced) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
convert_to_ifc(v, p, advanced);
|
||||
vertex = new IfcSchema::IfcVertexPoint(p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const gp_Ax2& a, IfcSchema::IfcAxis2Placement3D*& ax, bool advanced) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
IfcSchema::IfcDirection *x, *z;
|
||||
if (!(convert_to_ifc(a.Location(), p, advanced) && convert_to_ifc(a.Direction(), z, advanced) && convert_to_ifc(a.XDirection(), x, advanced))) {
|
||||
ax = 0;
|
||||
return 0;
|
||||
}
|
||||
ax = new IfcSchema::IfcAxis2Placement3D(p, z, x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void opencascade_array_to_vector(T& t, std::vector<U>& u) {
|
||||
u.reserve(t.Length());
|
||||
for (int i = t.Lower(); i <= t.Upper(); ++i) {
|
||||
u.push_back(t.Value(i));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void opencascade_array_to_vector2(T& t, std::vector< std::vector<U> >& u) {
|
||||
u.reserve(t.RowLength());
|
||||
for (int j = t.LowerRow(); j <= t.UpperRow(); ++j) {
|
||||
std::vector<U> v;
|
||||
v.reserve(t.ColLength());
|
||||
for (int i = t.LowerCol(); i <= t.UpperCol(); ++i) {
|
||||
v.push_back(t.Value(j, i));
|
||||
}
|
||||
u.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcKnotType::IfcKnotType opencascade_knotspec_to_ifc(GeomAbs_BSplKnotDistribution bspline_knot_spec) {
|
||||
IfcSchema::IfcKnotType::IfcKnotType knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED;
|
||||
if (bspline_knot_spec == GeomAbs_Uniform) {
|
||||
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_UNIFORM_KNOTS;
|
||||
} else if (bspline_knot_spec == GeomAbs_QuasiUniform) {
|
||||
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_QUASI_UNIFORM_KNOTS;
|
||||
} else if (bspline_knot_spec == GeomAbs_PiecewiseBezier) {
|
||||
knot_spec = IfcSchema::IfcKnotType::IfcKnotType_PIECEWISE_BEZIER_KNOTS;
|
||||
}
|
||||
return knot_spec;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const Handle_Geom_Curve& c, IfcSchema::IfcCurve*& curve, bool advanced) {
|
||||
if (c->DynamicType() == STANDARD_TYPE(Geom_Line)) {
|
||||
IfcSchema::IfcDirection* d;
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
|
||||
Handle_Geom_Line line = Handle_Geom_Line::DownCast(c);
|
||||
|
||||
if (!convert_to_ifc(line->Position().Location(), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
if (!convert_to_ifc(line->Position().Direction(), d, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IfcSchema::IfcVector* v = new IfcSchema::IfcVector(d, 1.);
|
||||
curve = new IfcSchema::IfcLine(p, v);
|
||||
|
||||
return 1;
|
||||
} else if (c->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
|
||||
IfcSchema::IfcAxis2Placement3D* ax;
|
||||
|
||||
Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(c);
|
||||
|
||||
convert_to_ifc(circle->Position(), ax, advanced);
|
||||
curve = new IfcSchema::IfcCircle(ax, circle->Radius());
|
||||
|
||||
return 1;
|
||||
} else if (c->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
IfcSchema::IfcAxis2Placement3D* ax;
|
||||
|
||||
Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(c);
|
||||
|
||||
convert_to_ifc(ellipse->Position(), ax, advanced);
|
||||
curve = new IfcSchema::IfcEllipse(ax, ellipse->MajorRadius(), ellipse->MinorRadius());
|
||||
|
||||
return 1;
|
||||
}
|
||||
#ifdef USE_IFC4
|
||||
else if (c->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve)) {
|
||||
Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c);
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
TColgp_Array1OfPnt poles(1, bspline->NbPoles());
|
||||
bspline->Poles(poles);
|
||||
for (int i = 1; i <= bspline->NbPoles(); ++i) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
if (!convert_to_ifc(poles.Value(i), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
points->push(p);
|
||||
}
|
||||
IfcSchema::IfcKnotType::IfcKnotType knot_spec = opencascade_knotspec_to_ifc(bspline->KnotDistribution());
|
||||
|
||||
std::vector<int> mults;
|
||||
std::vector<double> knots;
|
||||
std::vector<double> weights;
|
||||
|
||||
TColStd_Array1OfInteger bspline_mults(1, bspline->NbKnots());
|
||||
TColStd_Array1OfReal bspline_knots(1, bspline->NbKnots());
|
||||
TColStd_Array1OfReal bspline_weights(1, bspline->NbPoles());
|
||||
|
||||
bspline->Multiplicities(bspline_mults);
|
||||
bspline->Knots(bspline_knots);
|
||||
bspline->Weights(bspline_weights);
|
||||
|
||||
opencascade_array_to_vector(bspline_mults, mults);
|
||||
opencascade_array_to_vector(bspline_knots, knots);
|
||||
opencascade_array_to_vector(bspline_weights, weights);
|
||||
|
||||
bool rational = false;
|
||||
for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it) {
|
||||
if ((*it) != 1.) {
|
||||
rational = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rational) {
|
||||
curve = new IfcSchema::IfcRationalBSplineCurveWithKnots(
|
||||
bspline->Degree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
|
||||
bspline->IsClosed() != 0,
|
||||
false,
|
||||
mults,
|
||||
knots,
|
||||
knot_spec,
|
||||
weights
|
||||
);
|
||||
} else {
|
||||
curve = new IfcSchema::IfcBSplineCurveWithKnots(
|
||||
bspline->Degree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
|
||||
bspline->IsClosed() != 0,
|
||||
false,
|
||||
mults,
|
||||
knots,
|
||||
knot_spec
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const Handle_Geom_Surface& s, IfcSchema::IfcSurface*& surface, bool advanced) {
|
||||
if (s->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
|
||||
Handle_Geom_Plane plane = Handle_Geom_Plane::DownCast(s);
|
||||
IfcSchema::IfcAxis2Placement3D* place;
|
||||
/// @todo: Note that the Ax3 is converted to an Ax2 here
|
||||
if (!convert_to_ifc(plane->Position().Ax2(), place, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
surface = new IfcSchema::IfcPlane(place);
|
||||
return 1;
|
||||
}
|
||||
#ifdef USE_IFC4
|
||||
else if (s->DynamicType() == STANDARD_TYPE(Geom_CylindricalSurface)) {
|
||||
Handle_Geom_CylindricalSurface cyl = Handle_Geom_CylindricalSurface::DownCast(s);
|
||||
IfcSchema::IfcAxis2Placement3D* place;
|
||||
/// @todo: Note that the Ax3 is converted to an Ax2 here
|
||||
if (!convert_to_ifc(cyl->Position().Ax2(), place, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
surface = new IfcSchema::IfcCylindricalSurface(place, cyl->Radius());
|
||||
return 1;
|
||||
} else if (s->DynamicType() == STANDARD_TYPE(Geom_BSplineSurface)) {
|
||||
typedef IfcTemplatedEntityListList<IfcSchema::IfcCartesianPoint> points_t;
|
||||
|
||||
Handle_Geom_BSplineSurface bspline = Handle_Geom_BSplineSurface::DownCast(s);
|
||||
points_t::ptr points(new points_t);
|
||||
|
||||
TColgp_Array2OfPnt poles(1, bspline->NbUPoles(), 1, bspline->NbVPoles());
|
||||
bspline->Poles(poles);
|
||||
for (int i = 1; i <= bspline->NbUPoles(); ++i) {
|
||||
std::vector<IfcSchema::IfcCartesianPoint*> ps;
|
||||
ps.reserve(bspline->NbVPoles());
|
||||
for (int j = 1; j <= bspline->NbVPoles(); ++j) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
if (!convert_to_ifc(poles.Value(i, j), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
ps.push_back(p);
|
||||
}
|
||||
points->push(ps);
|
||||
}
|
||||
|
||||
IfcSchema::IfcKnotType::IfcKnotType knot_spec_u = opencascade_knotspec_to_ifc(bspline->UKnotDistribution());
|
||||
IfcSchema::IfcKnotType::IfcKnotType knot_spec_v = opencascade_knotspec_to_ifc(bspline->VKnotDistribution());
|
||||
|
||||
if (knot_spec_u != knot_spec_v) {
|
||||
knot_spec_u = IfcSchema::IfcKnotType::IfcKnotType_UNSPECIFIED;
|
||||
}
|
||||
|
||||
std::vector<int> umults;
|
||||
std::vector<int> vmults;
|
||||
std::vector<double> uknots;
|
||||
std::vector<double> vknots;
|
||||
std::vector< std::vector<double> > weights;
|
||||
|
||||
TColStd_Array1OfInteger bspline_umults(1, bspline->NbUKnots());
|
||||
TColStd_Array1OfInteger bspline_vmults(1, bspline->NbVKnots());
|
||||
TColStd_Array1OfReal bspline_uknots(1, bspline->NbUKnots());
|
||||
TColStd_Array1OfReal bspline_vknots(1, bspline->NbVKnots());
|
||||
TColStd_Array2OfReal bspline_weights(1, bspline->NbUPoles(), 1, bspline->NbVPoles());
|
||||
|
||||
bspline->UMultiplicities(bspline_umults);
|
||||
bspline->VMultiplicities(bspline_vmults);
|
||||
bspline->UKnots(bspline_uknots);
|
||||
bspline->VKnots(bspline_vknots);
|
||||
bspline->Weights(bspline_weights);
|
||||
|
||||
opencascade_array_to_vector(bspline_umults, umults);
|
||||
opencascade_array_to_vector(bspline_vmults, vmults);
|
||||
opencascade_array_to_vector(bspline_uknots, uknots);
|
||||
opencascade_array_to_vector(bspline_vknots, vknots);
|
||||
opencascade_array_to_vector2(bspline_weights, weights);
|
||||
|
||||
bool rational = false;
|
||||
for (std::vector< std::vector<double> >::const_iterator it = weights.begin(); it != weights.end(); ++it) {
|
||||
for (std::vector<double>::const_iterator jt = it->begin(); jt != it->end(); ++jt) {
|
||||
if ((*jt) != 1.) {
|
||||
rational = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rational) {
|
||||
surface = new IfcSchema::IfcRationalBSplineSurfaceWithKnots(
|
||||
bspline->UDegree(),
|
||||
bspline->VDegree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED,
|
||||
bspline->IsUClosed() != 0,
|
||||
bspline->IsVClosed() != 0,
|
||||
false,
|
||||
umults,
|
||||
vmults,
|
||||
uknots,
|
||||
vknots,
|
||||
knot_spec_u,
|
||||
weights
|
||||
);
|
||||
} else {
|
||||
surface = new IfcSchema::IfcBSplineSurfaceWithKnots(
|
||||
bspline->UDegree(),
|
||||
bspline->VDegree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineSurfaceForm::IfcBSplineSurfaceForm_UNSPECIFIED,
|
||||
bspline->IsUClosed() != 0,
|
||||
bspline->IsVClosed() != 0,
|
||||
false,
|
||||
umults,
|
||||
vmults,
|
||||
uknots,
|
||||
vknots,
|
||||
knot_spec_u
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcCurve*& c, bool advanced) {
|
||||
double a, b;
|
||||
IfcSchema::IfcCurve* base;
|
||||
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
|
||||
if (!convert_to_ifc(crv, base, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
trim1->push(new IfcSchema::IfcParameterValue(a));
|
||||
trim2->push(new IfcSchema::IfcParameterValue(b));
|
||||
|
||||
c = new IfcSchema::IfcTrimmedCurve(base, trim1, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcEdge*& edge, bool advanced) {
|
||||
double a, b;
|
||||
|
||||
TopExp_Explorer exp(e, TopAbs_VERTEX);
|
||||
if (!exp.More()) return 0;
|
||||
TopoDS_Vertex v1 = TopoDS::Vertex(exp.Current());
|
||||
exp.Next();
|
||||
if (!exp.More()) return 0;
|
||||
TopoDS_Vertex v2 = TopoDS::Vertex(exp.Current());
|
||||
|
||||
IfcSchema::IfcVertex *vertex1, *vertex2;
|
||||
if (!(convert_to_ifc(v1, vertex1, advanced) && convert_to_ifc(v2, vertex2, advanced))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);
|
||||
|
||||
if (crv.IsNull()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (crv->DynamicType() == STANDARD_TYPE(Geom_Line) && !advanced) {
|
||||
IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdge(vertex1, vertex2);
|
||||
edge = new IfcSchema::IfcOrientedEdge(edge2, true);
|
||||
return 1;
|
||||
} else {
|
||||
IfcSchema::IfcCurve* curve;
|
||||
if (!convert_to_ifc(crv, curve, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
/// @todo probably not correct
|
||||
const bool sense = e.Orientation() == TopAbs_FORWARD;
|
||||
IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, curve, true);
|
||||
edge = new IfcSchema::IfcOrientedEdge(edge2, sense);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Wire& wire, IfcSchema::IfcLoop*& loop, bool advanced) {
|
||||
bool polygonal = true;
|
||||
for (TopExp_Explorer exp(wire, TopAbs_EDGE); exp.More(); exp.Next()) {
|
||||
double a, b;
|
||||
Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
|
||||
if (crv.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
|
||||
polygonal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!polygonal && !advanced) {
|
||||
return 0;
|
||||
} else if (polygonal && !advanced) {
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
for (; exp.More(); exp.Next()) {
|
||||
if (convert_to_ifc(exp.CurrentVertex(), p, advanced)) {
|
||||
points->push(p);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
loop = new IfcSchema::IfcPolyLoop(points);
|
||||
return 1;
|
||||
} else {
|
||||
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
|
||||
BRepTools_WireExplorer exp(wire);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcEdge* edge;
|
||||
// With advanced set to true convert_to_ifc(TopoDS_Edge&) will always create an IfcOrientedEdge
|
||||
if (!convert_to_ifc(exp.Current(), edge, true)) {
|
||||
double a, b;
|
||||
if (BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b).IsNull()) {
|
||||
continue;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
edges->push(edge->as<IfcSchema::IfcOrientedEdge>());
|
||||
}
|
||||
loop = new IfcSchema::IfcEdgeLoop(edges);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
int convert_to_ifc(const TopoDS_Face& f, IfcSchema::IfcFace*& face, bool advanced) {
|
||||
Handle_Geom_Surface surf = BRep_Tool::Surface(f);
|
||||
TopExp_Explorer exp(f, TopAbs_WIRE);
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list);
|
||||
int index = 0;
|
||||
for (; exp.More(); exp.Next(), ++index) {
|
||||
IfcSchema::IfcLoop* loop;
|
||||
if (!convert_to_ifc(TopoDS::Wire(exp.Current()), loop, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
IfcSchema::IfcFaceBound* bnd;
|
||||
if (index == 0) {
|
||||
bnd = new IfcSchema::IfcFaceOuterBound(loop, true);
|
||||
} else {
|
||||
bnd = new IfcSchema::IfcFaceBound(loop, true);
|
||||
}
|
||||
bounds->push(bnd);
|
||||
}
|
||||
|
||||
const bool is_planar = surf->DynamicType() == STANDARD_TYPE(Geom_Plane);
|
||||
|
||||
if (!is_planar && !advanced) {
|
||||
return 0;
|
||||
}
|
||||
if (is_planar && !advanced) {
|
||||
face = new IfcSchema::IfcFace(bounds);
|
||||
return 1;
|
||||
} else {
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcSurface* surface;
|
||||
if (!convert_to_ifc(surf, surface, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
face = new IfcSchema::IfcAdvancedFace(bounds, surface, f.Orientation() == TopAbs_FORWARD);
|
||||
return 1;
|
||||
#else
|
||||
// No IfcAdvancedFace in Ifc2x3
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
int convert_to_ifc(const TopoDS_Shape& s, U*& item, bool advanced) {
|
||||
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
|
||||
IfcSchema::IfcFace* f;
|
||||
|
||||
for (TopExp_Explorer exp(s, TopAbs_FACE); exp.More(); exp.Next()) {
|
||||
if (convert_to_ifc(TopoDS::Face(exp.Current()), f, advanced)) {
|
||||
faces->push(f);
|
||||
} else {
|
||||
/// Cleanup:
|
||||
for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) {
|
||||
IfcEntityList::ptr data = IfcParse::traverse(*it)->unique();
|
||||
for (IfcEntityList::it jt = data->begin(); jt != data->end(); ++jt) {
|
||||
delete *jt;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
item = new U(faces);
|
||||
return faces->size();
|
||||
}
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* IfcGeom::serialise(const TopoDS_Shape& shape, bool advanced) {
|
||||
#ifndef USE_IFC4
|
||||
advanced = false;
|
||||
#endif
|
||||
|
||||
for (TopExp_Explorer exp(shape, TopAbs_COMPSOLID); exp.More();) {
|
||||
/// @todo CompSolids are not supported
|
||||
return 0;
|
||||
}
|
||||
|
||||
IfcSchema::IfcRepresentation* rep = 0;
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list);
|
||||
|
||||
// First check if there is a solid with one or more shells
|
||||
for (TopExp_Explorer exp(shape, TopAbs_SOLID); exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcClosedShell* outer = 0;
|
||||
IfcSchema::IfcClosedShell::list::ptr inner(new IfcSchema::IfcClosedShell::list);
|
||||
for (TopExp_Explorer exp2(exp.Current(), TopAbs_SHELL); exp2.More(); exp2.Next()) {
|
||||
IfcSchema::IfcClosedShell* shell;
|
||||
if (!convert_to_ifc(exp2.Current(), shell, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
/// @todo Are shells always in this order or does Orientation() needs to be checked?
|
||||
if (outer) {
|
||||
inner->push(shell);
|
||||
} else {
|
||||
outer = shell;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
if (advanced) {
|
||||
if (inner->size()) {
|
||||
items->push(new IfcSchema::IfcAdvancedBrepWithVoids(outer, inner));
|
||||
} else {
|
||||
items->push(new IfcSchema::IfcAdvancedBrep(outer));
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
|
||||
/// @todo this is not necessarily correct as the shell is not necessarily facetted.
|
||||
if (inner->size()) {
|
||||
items->push(new IfcSchema::IfcFacetedBrepWithVoids(outer, inner));
|
||||
} else {
|
||||
items->push(new IfcSchema::IfcFacetedBrep(outer));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (items->size() > 0) {
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
|
||||
} else {
|
||||
|
||||
// If not, see if there is a shell
|
||||
IfcSchema::IfcOpenShell::list::ptr shells(new IfcSchema::IfcOpenShell::list);
|
||||
for (TopExp_Explorer exp(shape, TopAbs_SHELL); exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcOpenShell* shell;
|
||||
if (!convert_to_ifc(exp.Current(), shell, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
shells->push(shell);
|
||||
}
|
||||
|
||||
if (shells->size() > 0) {
|
||||
items->push(new IfcSchema::IfcShellBasedSurfaceModel(shells->generalize()));
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
|
||||
} else {
|
||||
|
||||
// If not, see if there is are one of more faces. Note that they will be grouped into a shell.
|
||||
IfcSchema::IfcOpenShell* shell;
|
||||
int face_count = convert_to_ifc(shape, shell, advanced);
|
||||
|
||||
if (face_count > 0) {
|
||||
items->push(shell);
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Body"), std::string("Brep"), items);
|
||||
} else {
|
||||
|
||||
// If not, see if there are any edges. Note that wires are skipped as
|
||||
// they are not commonly top-level geometrical descriptions in IFC.
|
||||
// Also note that edges are written as trimmed curves rather than edges.
|
||||
|
||||
IfcEntityList::ptr edges(new IfcEntityList);
|
||||
|
||||
for (TopExp_Explorer exp(shape, TopAbs_EDGE); exp.More(); exp.Next()) {
|
||||
IfcSchema::IfcCurve* c;
|
||||
if (!convert_to_ifc(TopoDS::Edge(exp.Current()), c, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
edges->push(c);
|
||||
}
|
||||
|
||||
if (edges->size() == 0) {
|
||||
return 0;
|
||||
} else if (edges->size() == 1) {
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("Curve2D"), edges->as<IfcSchema::IfcRepresentationItem>());
|
||||
} else {
|
||||
// A geometric set is created as that probably (?) makes more sense in IFC
|
||||
IfcSchema::IfcGeometricCurveSet* curves = new IfcSchema::IfcGeometricCurveSet(edges);
|
||||
items->push(curves);
|
||||
rep = new IfcSchema::IfcShapeRepresentation(0, std::string("Axis"), std::string("GeometricCurveSet"), items->as<IfcSchema::IfcRepresentationItem>());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
|
||||
reps->push(rep);
|
||||
return new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
}
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* IfcGeom::tesselate(const TopoDS_Shape& shape, double deflection) {
|
||||
BRepMesh_IncrementalMesh(shape, deflection);
|
||||
|
||||
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
|
||||
|
||||
for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
|
||||
const TopoDS_Face& face = TopoDS::Face(exp.Current());
|
||||
TopLoc_Location loc;
|
||||
Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(face, loc);
|
||||
|
||||
if (!tri.IsNull()) {
|
||||
const TColgp_Array1OfPnt& nodes = tri->Nodes();
|
||||
std::vector<IfcSchema::IfcCartesianPoint*> vertices;
|
||||
for (int i = 1; i <= nodes.Length(); ++i) {
|
||||
gp_Pnt pnt = nodes(i).Transformed(loc);
|
||||
std::vector<double> xyz; xyz.push_back(pnt.X()); xyz.push_back(pnt.Y()); xyz.push_back(pnt.Z());
|
||||
IfcSchema::IfcCartesianPoint* cpnt = new IfcSchema::IfcCartesianPoint(xyz);
|
||||
vertices.push_back(cpnt);
|
||||
}
|
||||
const Poly_Array1OfTriangle& triangles = tri->Triangles();
|
||||
for (int i = 1; i <= triangles.Length(); ++i) {
|
||||
int n1, n2, n3;
|
||||
triangles(i).Get(n1, n2, n3);
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(vertices[n1 - 1]);
|
||||
points->push(vertices[n2 - 1]);
|
||||
points->push(vertices[n3 - 1]);
|
||||
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
|
||||
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, face.Orientation() != TopAbs_REVERSED);
|
||||
IfcSchema::IfcFaceBound::list::ptr bounds(new IfcSchema::IfcFaceBound::list);
|
||||
bounds->push(bound);
|
||||
IfcSchema::IfcFace* face2 = new IfcSchema::IfcFace(bounds);
|
||||
faces->push(face2);
|
||||
}
|
||||
}
|
||||
}
|
||||
IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces);
|
||||
IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list);
|
||||
shells->push(shell);
|
||||
IfcSchema::IfcFaceBasedSurfaceModel* surface_model = new IfcSchema::IfcFaceBasedSurfaceModel(shells);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list);
|
||||
|
||||
items->push(surface_model);
|
||||
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
0, std::string("Facetation"), std::string("SurfaceModel"), items);
|
||||
|
||||
reps->push(rep);
|
||||
IfcSchema::IfcProductDefinitionShape* shapedef = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
|
||||
return shapedef;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFCGEOMSHAPETYPE_H
|
||||
#define IFCGEOMSHAPETYPE_H
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
enum ShapeType {
|
||||
ST_SHAPELIST,
|
||||
ST_SHAPE,
|
||||
ST_FACE,
|
||||
ST_WIRE,
|
||||
ST_CURVE,
|
||||
ST_EDGE,
|
||||
ST_VERTEX,
|
||||
ST_OTHER
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+576
-167
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,278 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFCGEOMTREE_H
|
||||
#define IFCGEOMTREE_H
|
||||
|
||||
#include "../ifcparse/IfcFile.h"
|
||||
#include "../ifcgeom/IfcGeomIterator.h"
|
||||
|
||||
#include <NCollection_UBTree.hxx>
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <BRepAlgoAPI_Common.hxx>
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
#include <BRepClass3d_SolidClassifier.hxx>
|
||||
|
||||
namespace IfcGeom {
|
||||
|
||||
namespace impl {
|
||||
template <typename T>
|
||||
class tree {
|
||||
|
||||
public:
|
||||
|
||||
void add(const T& t, const Bnd_Box& b) {
|
||||
tree_.Add(t, b);
|
||||
}
|
||||
|
||||
void add(const T& t, const TopoDS_Shape& s) {
|
||||
Bnd_Box b;
|
||||
BRepBndLib::AddClose(s, b);
|
||||
add(t, b);
|
||||
shapes_[t] = s;
|
||||
}
|
||||
|
||||
std::vector<T> select_box(const T& t, bool completely_within = false, double extend=-1.e-5) const {
|
||||
typename map_t::const_iterator it = shapes_.find(t);
|
||||
if (it == shapes_.end()) {
|
||||
return std::vector<T>();
|
||||
}
|
||||
|
||||
Bnd_Box b;
|
||||
BRepBndLib::AddClose(it->second, b);
|
||||
|
||||
// Gap is assumed to be positive throughout the codebase,
|
||||
// but at least for IsOut() in the selector a negative
|
||||
// Gap should work as well.
|
||||
b.SetGap(b.GetGap() + extend);
|
||||
|
||||
return select_box(b, completely_within);
|
||||
}
|
||||
|
||||
std::vector<T> select_box(const gp_Pnt& p) const {
|
||||
Bnd_Box b;
|
||||
b.Add(p);
|
||||
return select_box(b);
|
||||
}
|
||||
|
||||
std::vector<T> select_box(const Bnd_Box& b, bool completely_within = false) const {
|
||||
selector s(b);
|
||||
tree_.Select(s);
|
||||
if (completely_within) {
|
||||
std::vector<T> ts = s.results();
|
||||
std::vector<T> ts_filtered;
|
||||
ts_filtered.reserve(ts.size());
|
||||
typename std::vector<T>::const_iterator it = ts.begin();
|
||||
for (; it != ts.end(); ++it) {
|
||||
const TopoDS_Shape& shp = shapes_.find(*it)->second;
|
||||
Bnd_Box B;
|
||||
BRepBndLib::AddClose(shp, B);
|
||||
|
||||
// BndBox::CornerMin() /-Max() introduced in OCCT 6.8
|
||||
double x1, y1, z1, x2, y2, z2;
|
||||
b.Get(x1, y1, z1, x2, y2, z2);
|
||||
double gap = B.GetGap();
|
||||
gp_Pnt p1(x1 - gap, y1 - gap, z1 - gap);
|
||||
gp_Pnt p2(x2 + gap, y2 + gap, z2 + gap);
|
||||
|
||||
if (!b.IsOut(p1) && !b.IsOut(p2)) {
|
||||
ts_filtered.push_back(*it);
|
||||
}
|
||||
}
|
||||
return ts_filtered;
|
||||
} else {
|
||||
return s.results();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<T> select(const T& t, bool completely_within = false) const {
|
||||
std::vector<T> ts = select_box(t);
|
||||
if (ts.empty()) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::vector<T> ts_filtered;
|
||||
|
||||
const TopoDS_Shape& A = shapes_.find(t)->second;
|
||||
if (IfcGeom::Kernel::count(A, TopAbs_SHELL) == 0) {
|
||||
return ts_filtered;
|
||||
}
|
||||
|
||||
ts_filtered.reserve(ts.size());
|
||||
|
||||
typename std::vector<T>::const_iterator it = ts.begin();
|
||||
for (it = ts.begin(); it != ts.end(); ++it) {
|
||||
const TopoDS_Shape& B = shapes_.find(*it)->second;
|
||||
if (IfcGeom::Kernel::count(B, TopAbs_SHELL) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (completely_within) {
|
||||
BRepAlgoAPI_Cut cut(B, A);
|
||||
if (cut.IsDone()) {
|
||||
if (IfcGeom::Kernel::count(cut.Shape(), TopAbs_SHELL) == 0) {
|
||||
ts_filtered.push_back(*it);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BRepAlgoAPI_Common common(A, B);
|
||||
if (common.IsDone()) {
|
||||
if (IfcGeom::Kernel::count(common.Shape(), TopAbs_SHELL) > 0) {
|
||||
ts_filtered.push_back(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ts_filtered;
|
||||
}
|
||||
|
||||
std::vector<T> select(const TopoDS_Shape& s) const {
|
||||
Bnd_Box bb;
|
||||
BRepBndLib::AddClose(s, bb);
|
||||
|
||||
std::vector<T> ts;
|
||||
|
||||
if (IfcGeom::Kernel::count(s, TopAbs_SHELL) == 0) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
ts = select_box(bb);
|
||||
|
||||
if (ts.empty()) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::vector<T> ts_filtered;
|
||||
ts_filtered.reserve(ts.size());
|
||||
|
||||
typename std::vector<T>::const_iterator it = ts.begin();
|
||||
for (it = ts.begin(); it != ts.end(); ++it) {
|
||||
const TopoDS_Shape& B = shapes_.find(*it)->second;
|
||||
|
||||
if (IfcGeom::Kernel::count(B, TopAbs_SHELL) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BRepAlgoAPI_Common common(s, B);
|
||||
if (common.IsDone()) {
|
||||
if (IfcGeom::Kernel::count(common.Shape(), TopAbs_SHELL) > 0) {
|
||||
ts_filtered.push_back(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ts_filtered;
|
||||
}
|
||||
|
||||
std::vector<T> select(const gp_Pnt& p) const {
|
||||
std::vector<T> ts = select_box(p);
|
||||
if (ts.empty()) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::vector<T> ts_filtered;
|
||||
ts_filtered.reserve(ts.size());
|
||||
|
||||
typename std::vector<T>::const_iterator it = ts.begin();
|
||||
for (it = ts.begin(); it != ts.end(); ++it) {
|
||||
const TopoDS_Shape& B = shapes_.find(*it)->second;
|
||||
TopExp_Explorer exp(B, TopAbs_SOLID);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
BRepClass3d_SolidClassifier cls(exp.Current(), p, 1e-5);
|
||||
if (cls.State() != TopAbs_OUT) {
|
||||
ts_filtered.push_back(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ts_filtered;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
typedef NCollection_UBTree<T, Bnd_Box> tree_t;
|
||||
typedef std::map<T, TopoDS_Shape> map_t;
|
||||
tree_t tree_;
|
||||
map_t shapes_;
|
||||
|
||||
class selector : public tree_t::Selector
|
||||
{
|
||||
public:
|
||||
selector(const Bnd_Box& b)
|
||||
: tree_t::Selector()
|
||||
, bounds_(b)
|
||||
{}
|
||||
|
||||
Standard_Boolean Reject(const Bnd_Box& b) const {
|
||||
return bounds_.IsOut(b);
|
||||
}
|
||||
|
||||
Standard_Boolean Accept(const T& o) {
|
||||
results_.push_back(o);
|
||||
return Standard_True;
|
||||
}
|
||||
|
||||
const std::vector<T>& results() const {
|
||||
return results_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> results_;
|
||||
const Bnd_Box& bounds_;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
class tree : public impl::tree<IfcSchema::IfcProduct*> {
|
||||
public:
|
||||
|
||||
tree() {};
|
||||
|
||||
tree(IfcParse::IfcFile& f) {
|
||||
add_file(f, IfcGeom::IteratorSettings());
|
||||
}
|
||||
|
||||
tree(IfcParse::IfcFile& f, const IfcGeom::IteratorSettings& settings) {
|
||||
add_file(f, settings);
|
||||
}
|
||||
|
||||
void add_file(IfcParse::IfcFile& f, const IfcGeom::IteratorSettings& settings) {
|
||||
IfcGeom::IteratorSettings settings_ = settings;
|
||||
settings_.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
|
||||
settings_.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);
|
||||
settings_.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
|
||||
|
||||
IfcGeom::Iterator<double> it(settings_, &f);
|
||||
|
||||
if (it.initialize()) {
|
||||
do {
|
||||
IfcGeom::BRepElement<double>* elem = (IfcGeom::BRepElement<double>*)it.get();
|
||||
add((IfcSchema::IfcProduct*)f.entityById(elem->id()), elem->geometry().as_compound());
|
||||
} while (it.next());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+575
-85
@@ -43,6 +43,8 @@
|
||||
#include <gp_Pln.hxx>
|
||||
#include <gp_Circ.hxx>
|
||||
|
||||
#include <GC_MakeCircle.hxx>
|
||||
|
||||
#include <TColgp_Array1OfPnt.hxx>
|
||||
#include <TColgp_Array1OfPnt2d.hxx>
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
@@ -65,6 +67,8 @@
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Wire.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
|
||||
#include <TopExp.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopLoc_Location.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
@@ -81,8 +85,218 @@
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <ShapeFix_Solid.hxx>
|
||||
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
#include <BRepTools_WireExplorer.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
||||
|
||||
#include <BRepAdaptor_CompCurve.hxx>
|
||||
#include <BRepAdaptor_HCompCurve.hxx>
|
||||
#include <Approx_Curve3d.hxx>
|
||||
|
||||
#include "../ifcgeom/IfcGeom.h"
|
||||
|
||||
namespace {
|
||||
// Returns the other vertex of an edge
|
||||
TopoDS_Vertex other(const TopoDS_Edge& e, const TopoDS_Vertex& v) {
|
||||
TopoDS_Vertex a, b;
|
||||
TopExp::Vertices(e, a, b);
|
||||
return v.IsSame(b) ? a : b;
|
||||
}
|
||||
|
||||
TopoDS_Edge first_edge(const TopoDS_Wire& w) {
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(w, v1, v2);
|
||||
TopTools_IndexedDataMapOfShapeListOfShape wm;
|
||||
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, wm);
|
||||
return TopoDS::Edge(wm.FindFromKey(v1).First());
|
||||
}
|
||||
|
||||
// Returns new wire with the edge replaced by a linear edge with the vertex v moved to p
|
||||
TopoDS_Wire adjust(const TopoDS_Wire& w, const TopoDS_Vertex& v, const gp_Pnt& p) {
|
||||
TopTools_IndexedDataMapOfShapeListOfShape map;
|
||||
TopExp::MapShapesAndAncestors(w, TopAbs_VERTEX, TopAbs_EDGE, map);
|
||||
|
||||
bool all_linear = true, single_circle = false, first = true;
|
||||
|
||||
const TopTools_ListOfShape& edges = map.FindFromKey(v);
|
||||
TopTools_ListIteratorOfListOfShape it(edges);
|
||||
for (; it.More(); it.Next()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(it.Value());
|
||||
double _, __;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, _, __);
|
||||
const bool is_line = crv->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
const bool is_circle = crv->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
all_linear = all_linear && is_line;
|
||||
single_circle = first && is_circle;
|
||||
}
|
||||
|
||||
if (all_linear) {
|
||||
BRep_Builder b;
|
||||
TopoDS_Vertex v2;
|
||||
b.MakeVertex(v2, p, BRep_Tool::Tolerance(v));
|
||||
|
||||
ShapeBuild_ReShape reshape;
|
||||
reshape.Replace(v.Oriented(TopAbs_FORWARD), v2);
|
||||
|
||||
return TopoDS::Wire(reshape.Apply(w));
|
||||
} else if (single_circle) {
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(w, v1, v2);
|
||||
|
||||
gp_Pnt p1, p2, p3;
|
||||
p1 = v.IsEqual(v1) ? p : BRep_Tool::Pnt(v1);
|
||||
p3 = v.IsEqual(v2) ? p : BRep_Tool::Pnt(v2);
|
||||
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(edges.First()), a, b);
|
||||
crv->D0((a + b) / 2., p2);
|
||||
|
||||
GC_MakeCircle mc(p1, p2, p3);
|
||||
if (!mc.IsDone()) {
|
||||
throw IfcGeom::geometry_exception("Failed to adjust circle");
|
||||
}
|
||||
|
||||
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(mc.Value(), p1, p3).Edge();
|
||||
BRepBuilderAPI_MakeWire builder;
|
||||
builder.Add(edge);
|
||||
return builder.Wire();
|
||||
} else {
|
||||
throw IfcGeom::geometry_exception("Unexpected wire to adjust");
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper around BRepBuilderAPI_MakeWire that makes sure segments are connected either by moving end points or by adding intermediate segments
|
||||
class wire_builder {
|
||||
private:
|
||||
BRepBuilderAPI_MakeWire mw_;
|
||||
double p_;
|
||||
bool override_next_;
|
||||
gp_Pnt next_override_;
|
||||
const IfcUtil::IfcBaseClass* inst_;
|
||||
|
||||
public:
|
||||
wire_builder(double p, const IfcUtil::IfcBaseClass* inst = 0) : p_(p), override_next_(false), inst_(inst) {}
|
||||
|
||||
void operator()(const TopoDS_Shape& a) {
|
||||
const TopoDS_Wire& w = TopoDS::Wire(a);
|
||||
if (override_next_) {
|
||||
override_next_ = false;
|
||||
TopoDS_Edge e = first_edge(w);
|
||||
mw_.Add(adjust(w, TopExp::FirstVertex(e, true), next_override_));
|
||||
} else {
|
||||
mw_.Add(w);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const TopoDS_Shape& a, const TopoDS_Shape& b, bool last) {
|
||||
TopoDS_Wire w1 = TopoDS::Wire(a);
|
||||
const TopoDS_Wire& w2 = TopoDS::Wire(b);
|
||||
|
||||
if (override_next_) {
|
||||
override_next_ = false;
|
||||
TopoDS_Edge e = first_edge(w1);
|
||||
w1 = adjust(w1, TopExp::FirstVertex(e, true), next_override_);
|
||||
}
|
||||
|
||||
TopoDS_Vertex w11, w12, w21, w22;
|
||||
TopExp::Vertices(w1, w11, w12);
|
||||
TopExp::Vertices(w2, w21, w22);
|
||||
|
||||
gp_Pnt p1 = BRep_Tool::Pnt(w12);
|
||||
gp_Pnt p2 = BRep_Tool::Pnt(w21);
|
||||
|
||||
double dist = p1.Distance(p2);
|
||||
|
||||
// Distance is within tolerance, this is fine
|
||||
if (dist < p_) {
|
||||
mw_.Add(w1);
|
||||
goto check;
|
||||
}
|
||||
|
||||
// Distance is too large for attempting to move end points, add intermediate edge
|
||||
if (dist > 1000. * p_) {
|
||||
mw_.Add(w1);
|
||||
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
Logger::Message(Logger::LOG_ERROR, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_->entity);
|
||||
goto check;
|
||||
}
|
||||
|
||||
{
|
||||
TopTools_IndexedDataMapOfShapeListOfShape wmap1, wmap2;
|
||||
|
||||
// Find edges connected to end- and begin vertex
|
||||
TopExp::MapShapesAndAncestors(w1, TopAbs_VERTEX, TopAbs_EDGE, wmap1);
|
||||
TopExp::MapShapesAndAncestors(w2, TopAbs_VERTEX, TopAbs_EDGE, wmap2);
|
||||
|
||||
const TopTools_ListOfShape& last_edges = wmap1.FindFromKey(w12);
|
||||
const TopTools_ListOfShape& first_edges = wmap2.FindFromKey(w21);
|
||||
|
||||
double _, __;
|
||||
if (last_edges.Extent() == 1 && first_edges.Extent() == 1) {
|
||||
Handle(Geom_Curve) c1 = BRep_Tool::Curve(TopoDS::Edge(last_edges.First()), _, __);
|
||||
Handle(Geom_Curve) c2 = BRep_Tool::Curve(TopoDS::Edge(first_edges.First()), _, __);
|
||||
|
||||
const bool is_line1 = c1->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
const bool is_line2 = c2->DynamicType() == STANDARD_TYPE(Geom_Line);
|
||||
|
||||
const bool is_circle1 = c1->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
const bool is_circle2 = c2->DynamicType() == STANDARD_TYPE(Geom_Circle);
|
||||
|
||||
// Preferably adjust the segment that is linear
|
||||
if (is_line1 || (is_circle1 && !is_line2)) {
|
||||
mw_.Add(adjust(w1, w12, p2));
|
||||
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_->entity);
|
||||
} else if ((is_line2 || is_circle2) && !last) {
|
||||
mw_.Add(w1);
|
||||
override_next_ = true;
|
||||
next_override_ = p1;
|
||||
Logger::Message(Logger::LOG_ERROR, "Adjusted edge end-point with distance " + boost::lexical_cast<std::string>(dist) + " on:", inst_->entity);
|
||||
} else {
|
||||
// In all other cases an edge is added
|
||||
mw_.Add(w1);
|
||||
mw_.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
Logger::Message(Logger::LOG_ERROR, "Added additional segment to close gap with length " + boost::lexical_cast<std::string>(dist) + " to:", inst_->entity);
|
||||
}
|
||||
} else {
|
||||
Logger::Error("Internal error, inconsistent wire segments", inst_->entity);
|
||||
mw_.Add(w1);
|
||||
}
|
||||
}
|
||||
|
||||
check:
|
||||
if (mw_.Error() == BRepBuilderAPI_NonManifoldWire) {
|
||||
Logger::Error("Non-manifold curve segments:", inst_->entity);
|
||||
} else if (mw_.Error() == BRepBuilderAPI_DisconnectedWire) {
|
||||
Logger::Error("Failed to join curve segments:", inst_->entity);
|
||||
}
|
||||
}
|
||||
|
||||
const TopoDS_Wire& wire() { return mw_.Wire(); }
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void shape_pair_enumerate(TopTools_ListIteratorOfListOfShape& it, Fn& fn, bool closed) {
|
||||
bool is_first = true;
|
||||
TopoDS_Shape first, previous, current;
|
||||
for (; it.More(); it.Next(), is_first = false) {
|
||||
current = it.Value();
|
||||
if (is_first) {
|
||||
first = current;
|
||||
} else {
|
||||
fn(previous, current, false);
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
if (closed) {
|
||||
fn(current, first, true);
|
||||
} else {
|
||||
fn(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire& wire) {
|
||||
if ( getValue(GV_PLANEANGLE_UNIT)<0 ) {
|
||||
Logger::Message(Logger::LOG_WARNING,"Creating a composite curve without unit information:",l->entity);
|
||||
@@ -99,13 +313,33 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
|
||||
TopoDS_Wire wire_radians, wire_degrees;
|
||||
try {
|
||||
succes_radians = IfcGeom::Kernel::convert(l,wire_radians);
|
||||
} catch (...) {}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Notice(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Notice(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Notice("Unknown error using radians");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Notice("Unknown error using radians");
|
||||
}
|
||||
|
||||
// Now try degrees
|
||||
setValue(GV_PLANEANGLE_UNIT,0.0174532925199433);
|
||||
try {
|
||||
succes_degrees = IfcGeom::Kernel::convert(l,wire_degrees);
|
||||
} catch (...) {}
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Notice(e);
|
||||
} catch (const Standard_Failure& e) {
|
||||
if (e.GetMessageString() && strlen(e.GetMessageString())) {
|
||||
Logger::Notice(e.GetMessageString());
|
||||
} else {
|
||||
Logger::Notice("Unknown error using degrees");
|
||||
}
|
||||
} catch (...) {
|
||||
Logger::Notice("Unknown error using degrees");
|
||||
}
|
||||
|
||||
// Restore to unknown unit state
|
||||
setValue(GV_PLANEANGLE_UNIT,-1.0);
|
||||
@@ -121,7 +355,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
|
||||
use_radians = true;
|
||||
} else {
|
||||
// No heuristic left to prefer the one over the other,
|
||||
// apparently both variants are equally succesful.
|
||||
// apparently both variants are equally successful.
|
||||
// The curve might be composed of only straight segments.
|
||||
// Let's go with the wire created using radians as that
|
||||
// at least is a SI unit.
|
||||
@@ -139,57 +373,112 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCompositeCurve* l, TopoDS_Wire
|
||||
|
||||
return use_radians || use_degrees;
|
||||
}
|
||||
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment::list::ptr segments = l->Segments();
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
//TopoDS_Vertex last_vertex;
|
||||
for( IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++ it ) {
|
||||
|
||||
TopTools_ListOfShape converted_segments;
|
||||
|
||||
for (IfcSchema::IfcCompositeCurveSegment::list::it it = segments->begin(); it != segments->end(); ++it) {
|
||||
|
||||
IfcSchema::IfcCurve* curve = (*it)->ParentCurve();
|
||||
TopoDS_Wire wire2;
|
||||
if ( !convert_wire(curve,wire2) ) {
|
||||
Logger::Message(Logger::LOG_ERROR,"Failed to convert curve:",curve->entity);
|
||||
TopoDS_Wire segment;
|
||||
|
||||
if (!convert_wire(curve, segment)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert curve:", curve->entity);
|
||||
continue;
|
||||
}
|
||||
if ( ! (*it)->SameSense() ) wire2.Reverse();
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(wire2, getValue(GV_WIRE_CREATION_TOLERANCE), TopAbs_WIRE);
|
||||
/*if ( it != segments->begin() ) {
|
||||
TopExp_Explorer exp (wire2,TopAbs_VERTEX);
|
||||
const TopoDS_Vertex& first_vertex = TopoDS::Vertex(exp.Current());
|
||||
gp_Pnt first = BRep_Tool::Pnt(first_vertex);
|
||||
gp_Pnt last = BRep_Tool::Pnt(last_vertex);
|
||||
Standard_Real distance = first.Distance(last);
|
||||
if ( distance > ALMOST_ZERO ) {
|
||||
w.Add( BRepBuilderAPI_MakeEdge( last_vertex, first_vertex ) );
|
||||
}
|
||||
}*/
|
||||
w.Add(wire2);
|
||||
//last_vertex = w.Vertex();
|
||||
if ( w.Error() != BRepBuilderAPI_WireDone ) {
|
||||
Logger::Message(Logger::LOG_ERROR,"Failed to join curve segments:",l->entity);
|
||||
return false;
|
||||
|
||||
if (!(*it)->SameSense()) {
|
||||
segment.Reverse();
|
||||
}
|
||||
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
FTol.SetTolerance(segment, getValue(GV_PRECISION), TopAbs_WIRE);
|
||||
|
||||
converted_segments.Append(segment);
|
||||
|
||||
}
|
||||
wire = w.Wire();
|
||||
|
||||
if (converted_segments.Extent() == 0) {
|
||||
Logger::Message(Logger::LOG_ERROR, "No segment succesfully converted:", l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
TopoDS_Vertex wire_first_vertex, wire_last_vertex, edge_first_vertex, edge_last_vertex;
|
||||
|
||||
TopTools_ListIteratorOfListOfShape it(converted_segments);
|
||||
|
||||
IfcEntityList::ptr profile = l->entity->getInverse(IfcSchema::Type::IfcProfileDef, -1);
|
||||
const bool force_close = profile && profile->size() > 0;
|
||||
|
||||
wire_builder bld(getValue(GV_PRECISION), l);
|
||||
shape_pair_enumerate(it, bld, force_close);
|
||||
wire = bld.wire();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
Below is code to deduce the formula below in SageMath
|
||||
|
||||
| R, b = var('R b')
|
||||
|
|
||||
| Bxy = R * cos(b), R * sin(b)
|
||||
| Cxy = R * cos(b/2), R * sin(b/2)
|
||||
|
|
||||
| def dot(v, w):
|
||||
| return v[0] * w[0] + v[1] * w[1]
|
||||
|
|
||||
| def norm(v):
|
||||
| l = sqrt(v[0]^2 + v[1]^2)
|
||||
| return v[0] / l, v[1] / l
|
||||
|
|
||||
| (R - R*dot(norm(Cxy), norm(Bxy))).full_simplify()
|
||||
*/
|
||||
|
||||
double deflection_for_approximating_circle(double radius, double param) {
|
||||
return -radius * std::cos(1. / 2. * param) * std::cos(param) - radius * std::sin(1. / 2. * param) * std::sin(param) + radius;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire& wire) {
|
||||
IfcSchema::IfcCurve* basis_curve = l->BasisCurve();
|
||||
bool isConic = basis_curve->is(IfcSchema::Type::IfcConic);
|
||||
double parameterFactor = isConic ? getValue(GV_PLANEANGLE_UNIT) : getValue(GV_LENGTH_UNIT);
|
||||
|
||||
Handle(Geom_Curve) curve;
|
||||
if ( !convert_curve(basis_curve,curve) ) return false;
|
||||
bool trim_cartesian = l->MasterRepresentation() == IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN;
|
||||
if (shape_type(basis_curve) == ST_CURVE) {
|
||||
if (!convert_curve(basis_curve, curve)) return false;
|
||||
} else if (shape_type(basis_curve) == ST_WIRE) {
|
||||
Logger::Warning("Approximating BasisCurve due to possible discontinuities", l->entity);
|
||||
TopoDS_Wire w;
|
||||
if (!convert_wire(basis_curve, w)) return false;
|
||||
BRepAdaptor_CompCurve cc(w, true);
|
||||
Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc));
|
||||
// @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them.
|
||||
Approx_Curve3d approx(hcc, getValue(GV_PRECISION), GeomAbs_C0, 10, 10);
|
||||
curve = approx.Curve();
|
||||
} else {
|
||||
Logger::Error("Unknown BasisCurve", l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool trim_cartesian = l->MasterRepresentation() != IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER;
|
||||
IfcEntityList::ptr trims1 = l->Trim1();
|
||||
IfcEntityList::ptr trims2 = l->Trim2();
|
||||
bool trimmed1 = false;
|
||||
bool trimmed2 = false;
|
||||
|
||||
unsigned sense_agreement = l->SenseAgreement() ? 0 : 1;
|
||||
double flts[2];
|
||||
gp_Pnt pnts[2];
|
||||
bool has_flts[2] = {false,false};
|
||||
bool has_pnts[2] = {false,false};
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
|
||||
TopoDS_Edge e;
|
||||
|
||||
for ( IfcEntityList::it it = trims1->begin(); it != trims1->end(); it ++ ) {
|
||||
IfcUtil::IfcBaseClass* i = *it;
|
||||
if ( i->is(IfcSchema::Type::IfcCartesianPoint) ) {
|
||||
@@ -201,6 +490,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
||||
has_flts[sense_agreement] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ( IfcEntityList::it it = trims2->begin(); it != trims2->end(); it ++ ) {
|
||||
IfcUtil::IfcBaseClass* i = *it;
|
||||
if ( i->is(IfcSchema::Type::IfcCartesianPoint) ) {
|
||||
@@ -212,29 +502,31 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
||||
has_flts[1-sense_agreement] = true;
|
||||
}
|
||||
}
|
||||
|
||||
trim_cartesian &= has_pnts[0] && has_pnts[1];
|
||||
bool trim_cartesian_failed = !trim_cartesian;
|
||||
if ( trim_cartesian ) {
|
||||
if ( pnts[0].Distance(pnts[1]) < getValue(GV_WIRE_CREATION_TOLERANCE) ) {
|
||||
if ( pnts[0].Distance(pnts[1]) < 2 * getValue(GV_PRECISION) ) {
|
||||
Logger::Message(Logger::LOG_WARNING,"Skipping segment with length below tolerance level:",l->entity);
|
||||
return false;
|
||||
}
|
||||
ShapeFix_ShapeTolerance FTol;
|
||||
TopoDS_Vertex v1 = BRepBuilderAPI_MakeVertex(pnts[0]);
|
||||
TopoDS_Vertex v2 = BRepBuilderAPI_MakeVertex(pnts[1]);
|
||||
FTol.SetTolerance(v1, getValue(GV_WIRE_CREATION_TOLERANCE), TopAbs_VERTEX);
|
||||
FTol.SetTolerance(v2, getValue(GV_WIRE_CREATION_TOLERANCE), TopAbs_VERTEX);
|
||||
BRepBuilderAPI_MakeEdge e (curve,v1,v2);
|
||||
if ( ! e.IsDone() ) {
|
||||
BRepBuilderAPI_EdgeError err = e.Error();
|
||||
FTol.SetTolerance(v1, getValue(GV_PRECISION), TopAbs_VERTEX);
|
||||
FTol.SetTolerance(v2, getValue(GV_PRECISION), TopAbs_VERTEX);
|
||||
BRepBuilderAPI_MakeEdge me (curve,v1,v2);
|
||||
if (!me.IsDone()) {
|
||||
BRepBuilderAPI_EdgeError err = me.Error();
|
||||
if ( err == BRepBuilderAPI_PointProjectionFailed ) {
|
||||
Logger::Message(Logger::LOG_WARNING,"Point projection failed for:",l->entity);
|
||||
trim_cartesian_failed = true;
|
||||
}
|
||||
} else {
|
||||
w.Add(e.Edge());
|
||||
e = me.Edge();
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!trim_cartesian || trim_cartesian_failed) && (has_flts[0] && has_flts[1]) ) {
|
||||
// The Geom_Line is constructed from a gp_Pnt and gp_Dir, whereas the IfcLine
|
||||
// is defined by an IfcCartesianPoint and an IfcVector with Magnitude. Because
|
||||
@@ -255,17 +547,50 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
||||
flts[1] -= M_PI / 2.;
|
||||
}
|
||||
}
|
||||
if ( isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],(double)(M_PI*2.0)),0.0f) ) {
|
||||
w.Add(BRepBuilderAPI_MakeEdge(curve));
|
||||
if ( isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.),0.) ) {
|
||||
e = BRepBuilderAPI_MakeEdge(curve).Edge();
|
||||
} else {
|
||||
BRepBuilderAPI_MakeEdge e (curve,flts[0],flts[1]);
|
||||
w.Add(e.Edge());
|
||||
BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]);
|
||||
e = me.Edge();
|
||||
}
|
||||
} else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) {
|
||||
w.Add(BRepBuilderAPI_MakeEdge(pnts[0],pnts[1]));
|
||||
e = BRepBuilderAPI_MakeEdge(pnts[0], pnts[1]).Edge();
|
||||
}
|
||||
if ( w.IsDone() ) {
|
||||
|
||||
if (isConic) {
|
||||
// Tiny circle segnments can cause issues later on, for example
|
||||
// when the comp curve is used as the sweeping directrix.
|
||||
double a, b;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(e, a, b);
|
||||
double radius = -1.;
|
||||
if (crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
|
||||
radius = Handle(Geom_Circle)::DownCast(crv)->Radius();
|
||||
} else if (crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
|
||||
// The formula above is for circles, but probably good enough
|
||||
radius = Handle(Geom_Ellipse)::DownCast(crv)->MajorRadius();
|
||||
}
|
||||
if (radius > 0. && deflection_for_approximating_circle(radius, b - a) < getValue(GV_PRECISION)) {
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(e, v0, v1);
|
||||
e = TopoDS::Edge(BRepBuilderAPI_MakeEdge(v0, v1).Edge().Oriented(e.Orientation()));
|
||||
Logger::Warning("Subsituted edge with linear approximation", l->entity);
|
||||
}
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
w.Add(e);
|
||||
|
||||
if (w.IsDone()) {
|
||||
wire = w.Wire();
|
||||
|
||||
// When SenseAgreement == .F. the vertices above have been reversed to
|
||||
// comply with the direction of conical curves. The ordering of the
|
||||
// vertices then still needs to be reversed in order to have begin and
|
||||
// end vertex consistent with IFC.
|
||||
if (sense_agreement != 0) { // .F.
|
||||
wire.Reverse();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -283,14 +608,29 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& resu
|
||||
polygon.Append(pnt);
|
||||
}
|
||||
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
const bool closed_by_proximity = polygon.Length() >= 3 && polygon.First().Distance(polygon.Last()) < eps;
|
||||
if (closed_by_proximity) {
|
||||
// tfk: note 1-based
|
||||
polygon.Remove(polygon.Length());
|
||||
}
|
||||
|
||||
// Remove points that are too close to one another
|
||||
remove_redundant_points_from_loop(polygon, false);
|
||||
remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps);
|
||||
|
||||
if (polygon.Length() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakePolygon w;
|
||||
for (int i = 1; i <= polygon.Length(); ++i) {
|
||||
w.Add(polygon.Value(i));
|
||||
}
|
||||
|
||||
if (closed_by_proximity) {
|
||||
w.Close();
|
||||
}
|
||||
|
||||
result = w.Wire();
|
||||
return true;
|
||||
}
|
||||
@@ -314,7 +654,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu
|
||||
}
|
||||
|
||||
// Remove points that are too close to one another
|
||||
remove_redundant_points_from_loop(polygon, true);
|
||||
const double eps = getValue(GV_PRECISION) * 10;
|
||||
remove_duplicate_points_from_loop(polygon, true, eps);
|
||||
|
||||
int count = polygon.Length();
|
||||
if (original_count - count != 0) {
|
||||
@@ -333,7 +674,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu
|
||||
}
|
||||
w.Close();
|
||||
|
||||
result = w.Wire();
|
||||
result = w.Wire();
|
||||
|
||||
TopTools_ListOfShape results;
|
||||
if (wire_intersections(result, results)) {
|
||||
Logger::Error("Self-intersections with " + boost::lexical_cast<std::string>(results.Extent()) + " cycles detected", l->entity);
|
||||
select_largest(results, result);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -367,33 +715,58 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeCurve* l, TopoDS_Wire& res
|
||||
const bool is_bounded = l->EdgeGeometry()->is(IfcSchema::Type::IfcBoundedCurve);
|
||||
|
||||
if (!is_bounded && convert_curve(l->EdgeGeometry(), crv)) {
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(crv, p1, p2));
|
||||
BRepBuilderAPI_MakeEdge me(crv, p1, p2);
|
||||
if (!me.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
mw.Add(me.Edge());
|
||||
result = mw;
|
||||
return true;
|
||||
} else if (is_bounded && convert_wire(l->EdgeGeometry(), result)) {
|
||||
if (!l->SameSense()) std::swap(pnt1, pnt2);
|
||||
TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||
if (!l->SameSense()) {
|
||||
result.Reverse();
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||
|
||||
while (exp.More()) {
|
||||
const TopoDS_Edge& ed = TopoDS::Edge(exp.Current());
|
||||
Standard_Real u1, u2;
|
||||
Handle(Geom_Curve) ecrv = BRep_Tool::Curve(ed, u1, u2);
|
||||
exp.Next();
|
||||
const bool last = !exp.More();
|
||||
first = false;
|
||||
|
||||
gp_Pnt a, b;
|
||||
|
||||
if (first && last) {
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, p1, p2));
|
||||
a = p1;
|
||||
b = p2;
|
||||
} else if (first) {
|
||||
gp_Pnt pu;
|
||||
ecrv->D0(u2, pu);
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, p1, pu));
|
||||
a = p1;
|
||||
ecrv->D0(u2, b);
|
||||
} else if (last) {
|
||||
gp_Pnt pu;
|
||||
ecrv->D0(u1, pu);
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, pu, p2));
|
||||
ecrv->D0(u1, a);
|
||||
b = p2;
|
||||
} else {
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, u1, u2));
|
||||
BRepBuilderAPI_MakeEdge me(ecrv, u1, u2);
|
||||
if (!me.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
mw.Add(me.Edge());
|
||||
first = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
BRep_Builder builder;
|
||||
TopoDS_Vertex v1, v2;
|
||||
/// @todo project first and emit warnings accordingly
|
||||
builder.MakeVertex(v1, a, getValue(GV_PRECISION));
|
||||
builder.MakeVertex(v2, b, getValue(GV_PRECISION));
|
||||
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(ecrv, v1, v2));
|
||||
|
||||
first = false;
|
||||
}
|
||||
result = mw;
|
||||
return true;
|
||||
@@ -406,32 +779,149 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdgeLoop* l, TopoDS_Wire& resu
|
||||
IfcSchema::IfcOrientedEdge::list::ptr li = l->EdgeList();
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
for (IfcSchema::IfcOrientedEdge::list::it it = li->begin(); it != li->end(); ++it) {
|
||||
IfcSchema::IfcOrientedEdge* e = *it;
|
||||
|
||||
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) e->EdgeStart())->VertexGeometry();
|
||||
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) e->EdgeEnd())->VertexGeometry();
|
||||
if (!pnt1->is(IfcSchema::Type::IfcCartesianPoint) || !pnt2->is(IfcSchema::Type::IfcCartesianPoint)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Pnt p1, p2;
|
||||
if (!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
|
||||
!IfcGeom::Kernel::convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
continue;
|
||||
|
||||
IfcSchema::IfcEdge* base = e->EdgeElement();
|
||||
TopoDS_Wire w;
|
||||
if (convert_wire(e->EdgeElement(), w)) {
|
||||
if (!e->Orientation()) w.Reverse();
|
||||
mw.Add(w);
|
||||
if (convert_wire(*it, w)) {
|
||||
mw.Add(TopoDS::Edge(TopoDS_Iterator(w).Value()));
|
||||
}
|
||||
}
|
||||
result = mw;
|
||||
if (!mw.IsDone()) {
|
||||
return false;
|
||||
}
|
||||
result = mw.Wire();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcEdge* l, TopoDS_Wire& result) {
|
||||
if (!l->EdgeStart()->is(IfcSchema::Type::IfcVertexPoint) || !l->EdgeEnd()->is(IfcSchema::Type::IfcVertexPoint)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Only IfcVertexPoints are supported for EdgeStart and -End", l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
IfcSchema::IfcPoint* pnt1 = ((IfcSchema::IfcVertexPoint*) l->EdgeStart())->VertexGeometry();
|
||||
IfcSchema::IfcPoint* pnt2 = ((IfcSchema::IfcVertexPoint*) l->EdgeEnd())->VertexGeometry();
|
||||
if (!pnt1->is(IfcSchema::Type::IfcCartesianPoint) || !pnt2->is(IfcSchema::Type::IfcCartesianPoint)) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Only IfcCartesianPoints are supported for VertexGeometry", l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
gp_Pnt p1, p2;
|
||||
if (!convert(((IfcSchema::IfcCartesianPoint*)pnt1), p1) ||
|
||||
!convert(((IfcSchema::IfcCartesianPoint*)pnt2), p2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(p1, p2));
|
||||
|
||||
result = mw.Wire();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcOrientedEdge* l, TopoDS_Wire& result) {
|
||||
if (convert_wire(l->EdgeElement(), result)) {
|
||||
if (!l->Orientation()) {
|
||||
result.Reverse();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcSubedge* l, TopoDS_Wire& result) {
|
||||
TopoDS_Wire temp;
|
||||
if (convert_wire(l->ParentEdge(), result) && convert((IfcSchema::IfcEdge*) l, temp)) {
|
||||
TopExp_Explorer exp(result, TopAbs_EDGE);
|
||||
TopoDS_Edge edge = TopoDS::Edge(exp.Current());
|
||||
Standard_Real u1, u2;
|
||||
Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u1, u2);
|
||||
TopoDS_Vertex v1, v2;
|
||||
TopExp::Vertices(temp, v1, v2);
|
||||
BRepBuilderAPI_MakeWire mw;
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(crv, v1, v2));
|
||||
result = mw.Wire();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wire& result) {
|
||||
|
||||
IfcSchema::IfcCartesianPointList* point_list = l->Points();
|
||||
std::vector< std::vector<double> > coordinates;
|
||||
if (point_list->as<IfcSchema::IfcCartesianPointList2D>()) {
|
||||
coordinates = point_list->as<IfcSchema::IfcCartesianPointList2D>()->CoordList();
|
||||
} else if (point_list->as<IfcSchema::IfcCartesianPointList3D>()) {
|
||||
coordinates = point_list->as<IfcSchema::IfcCartesianPointList3D>()->CoordList();
|
||||
}
|
||||
|
||||
std::vector<gp_Pnt> points;
|
||||
points.reserve(coordinates.size());
|
||||
for (std::vector< std::vector<double> >::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) {
|
||||
const std::vector<double>& coords = *it;
|
||||
points.push_back(gp_Pnt(
|
||||
coords.size() < 1 ? 0. : coords[0] * getValue(GV_LENGTH_UNIT),
|
||||
coords.size() < 2 ? 0. : coords[1] * getValue(GV_LENGTH_UNIT),
|
||||
coords.size() < 3 ? 0. : coords[2] * getValue(GV_LENGTH_UNIT)));
|
||||
}
|
||||
|
||||
int max_index = points.size();
|
||||
|
||||
BRepBuilderAPI_MakeWire w;
|
||||
|
||||
if(l->hasSegments()) {
|
||||
IfcEntityList::ptr segments = l->Segments();
|
||||
for (IfcEntityList::it it = segments->begin(); it != segments->end(); ++it) {
|
||||
IfcUtil::IfcBaseClass* segment = *it;
|
||||
if (segment->is(IfcSchema::Type::IfcLineIndex)) {
|
||||
IfcSchema::IfcLineIndex* line = (IfcSchema::IfcLineIndex*) segment;
|
||||
std::vector<int> indices = *line;
|
||||
gp_Pnt previous;
|
||||
for (std::vector<int>::const_iterator jt = indices.begin(); jt != indices.end(); ++jt) {
|
||||
if (*jt < 1 || *jt > max_index) {
|
||||
throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast<std::string>(*jt));
|
||||
}
|
||||
const gp_Pnt& current = points[*jt - 1];
|
||||
if (jt != indices.begin()) {
|
||||
w.Add(BRepBuilderAPI_MakeEdge(previous, current));
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
} else if (segment->is(IfcSchema::Type::IfcArcIndex)) {
|
||||
IfcSchema::IfcArcIndex* arc = (IfcSchema::IfcArcIndex*) segment;
|
||||
std::vector<int> indices = *arc;
|
||||
if (indices.size() != 3) {
|
||||
throw IfcParse::IfcException("Invalid IfcArcIndex encountered");
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const int& idx = indices[i];
|
||||
if (idx < 1 || idx > max_index) {
|
||||
throw IfcParse::IfcException("IfcIndexedPolyCurve index out of bounds for index " + boost::lexical_cast<std::string>(idx));
|
||||
}
|
||||
}
|
||||
const gp_Pnt& a = points[indices[0] - 1];
|
||||
const gp_Pnt& b = points[indices[1] - 1];
|
||||
const gp_Pnt& c = points[indices[2] - 1];
|
||||
Handle(Geom_Circle) circ = GC_MakeCircle(a, b, c).Value();
|
||||
w.Add(BRepBuilderAPI_MakeEdge(circ, a, c));
|
||||
} else {
|
||||
throw IfcParse::IfcException("Unexpected IfcIndexedPolyCurve segment of type " + IfcSchema::Type::ToString(segment->type()));
|
||||
}
|
||||
}
|
||||
} else if (points.begin() < points.end()) {
|
||||
std::vector<gp_Pnt>::const_iterator previous = points.begin();
|
||||
for (std::vector<gp_Pnt>::const_iterator current = previous+1; current < points.end(); ++current){
|
||||
w.Add(BRepBuilderAPI_MakeEdge(*previous, *current));
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
result = w.Wire();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,34 +18,83 @@
|
||||
********************************************************************************/
|
||||
|
||||
#include "IfcGeom.h"
|
||||
#include "IfcGeomShapeType.h"
|
||||
|
||||
using namespace IfcSchema;
|
||||
using namespace IfcUtil;
|
||||
|
||||
bool IfcGeom::Kernel::convert_shapes(const IfcBaseClass* l, IfcRepresentationShapeItems& r) {
|
||||
if (shape_type(l) != ST_SHAPELIST) {
|
||||
TopoDS_Shape shp;
|
||||
if (convert_shape(l, shp)) {
|
||||
r.push_back(IfcGeom::IfcRepresentationShapeItem(shp, get_style(l->as<IfcSchema::IfcRepresentationItem>())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "IfcRegisterConvertShapes.h"
|
||||
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::is_shape_collection(const IfcBaseClass* l) {
|
||||
#include "IfcRegisterIsShapeCollection.h"
|
||||
return false;
|
||||
IfcGeom::ShapeType IfcGeom::Kernel::shape_type(const IfcBaseClass* l) {
|
||||
#include "IfcRegisterShapeType.h"
|
||||
return ST_OTHER;
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert_shape(const IfcBaseClass* l, TopoDS_Shape& r) {
|
||||
const unsigned int id = l->entity->id();
|
||||
bool success = false;
|
||||
bool processed = false;
|
||||
bool ignored = false;
|
||||
|
||||
#ifndef NO_CACHE
|
||||
std::map<int,TopoDS_Shape>::const_iterator it = cache.Shape.find(id);
|
||||
if ( it != cache.Shape.end() ) { r = it->second; return true; }
|
||||
#endif
|
||||
const bool include_curves = getValue(GV_DIMENSIONALITY) != +1;
|
||||
const bool include_solids_and_surfaces = getValue(GV_DIMENSIONALITY) != -1;
|
||||
|
||||
IfcGeom::ShapeType st = shape_type(l);
|
||||
ignored = (!include_solids_and_surfaces && (st == ST_SHAPE || st == ST_FACE)) || (!include_curves && (st == ST_WIRE || st == ST_CURVE));
|
||||
if (st == ST_SHAPELIST) {
|
||||
processed = true;
|
||||
IfcRepresentationShapeItems items;
|
||||
success = convert_shapes(l, items) && flatten_shape_list(items, r, false);
|
||||
} else if (st == ST_SHAPE && include_solids_and_surfaces) {
|
||||
#include "IfcRegisterConvertShape.h"
|
||||
if ( processed ) {
|
||||
} else if (st == ST_FACE && include_solids_and_surfaces) {
|
||||
processed = true;
|
||||
success = convert_face(l, r);
|
||||
} else if (st == ST_WIRE && include_curves) {
|
||||
processed = true;
|
||||
TopoDS_Wire w;
|
||||
success = convert_wire(l, w);
|
||||
if (success) {
|
||||
r = w;
|
||||
}
|
||||
} else if (st == ST_CURVE && include_curves) {
|
||||
processed = true;
|
||||
Handle(Geom_Curve) crv;
|
||||
TopoDS_Wire w;
|
||||
success = convert_curve(l, crv) && convert_curve_to_wire(crv, w);
|
||||
if (success) {
|
||||
r = w;
|
||||
}
|
||||
}
|
||||
|
||||
if ( processed && success ) {
|
||||
const double precision = getValue(GV_PRECISION);
|
||||
apply_tolerance(r, precision);
|
||||
#ifndef NO_CACHE
|
||||
cache.Shape[id] = r;
|
||||
} else {
|
||||
Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity);
|
||||
#endif
|
||||
} else if (!ignored) {
|
||||
const char* const msg = processed
|
||||
? "Failed to convert:"
|
||||
: "No operation defined for:";
|
||||
Logger::Message(Logger::LOG_ERROR, msg, l->entity);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -38,26 +38,36 @@
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <gp_Trsf2d.hxx>
|
||||
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcParse.h"
|
||||
|
||||
SHAPES(IfcShellBasedSurfaceModel);
|
||||
SHAPES(IfcFaceBasedSurfaceModel);
|
||||
SHAPES(IfcShapeRepresentation);
|
||||
SHAPES(IfcRepresentation);
|
||||
SHAPES(IfcMappedItem);
|
||||
SHAPES(IfcFacetedBrep);
|
||||
// IfcFacetedBrep included
|
||||
// IfcAdvancedBrep included
|
||||
// IfcFacetedBrepWithVoids included
|
||||
// IfcAdvancedBrepWithVoids included
|
||||
SHAPES(IfcManifoldSolidBrep);
|
||||
SHAPES(IfcGeometricSet);
|
||||
|
||||
#ifdef USE_IFC4
|
||||
SHAPE(IfcCylindricalSurface);
|
||||
SHAPE(IfcAdvancedBrep);
|
||||
// FIXME: Surfaces should have a shape type of their own
|
||||
SHAPE(IfcBSplineSurfaceWithKnots);
|
||||
SHAPE(IfcTriangulatedFaceSet);
|
||||
SHAPE(IfcExtrudedAreaSolidTapered);
|
||||
#endif
|
||||
SHAPE(IfcPlane);
|
||||
SHAPE(IfcExtrudedAreaSolid);
|
||||
SHAPE(IfcRevolvedAreaSolid);
|
||||
SHAPE(IfcConnectedFaceSet);
|
||||
SHAPE(IfcBooleanResult);
|
||||
SHAPE(IfcPolygonalBoundedHalfSpace);
|
||||
SHAPE(IfcHalfSpaceSolid);
|
||||
// FIXME: Surfaces should have a shape type of their own
|
||||
SHAPE(IfcSurfaceOfLinearExtrusion);
|
||||
SHAPE(IfcSurfaceOfRevolution);
|
||||
SHAPE(IfcBlock);
|
||||
@@ -71,9 +81,6 @@ SHAPE(IfcRectangularTrimmedSurface);
|
||||
SHAPE(IfcSurfaceCurveSweptAreaSolid);
|
||||
SHAPE(IfcSweptDiskSolid);
|
||||
|
||||
#ifdef USE_IFC4
|
||||
FACE(IfcAdvancedFace);
|
||||
#endif
|
||||
FACE(IfcArbitraryProfileDefWithVoids);
|
||||
FACE(IfcArbitraryClosedProfileDef);
|
||||
FACE(IfcRoundedRectangleProfileDef);
|
||||
@@ -93,19 +100,31 @@ FACE(IfcEllipseProfileDef);
|
||||
FACE(IfcCenterLineProfileDef);
|
||||
FACE(IfcCompositeProfileDef);
|
||||
FACE(IfcDerivedProfileDef);
|
||||
// IfcFaceSurface included
|
||||
// IfcAdvancedFace included in case of IFC4
|
||||
FACE(IfcFace);
|
||||
|
||||
WIRE(IfcEdgeCurve);
|
||||
WIRE(IfcSubedge);
|
||||
WIRE(IfcOrientedEdge);
|
||||
WIRE(IfcEdge);
|
||||
WIRE(IfcEdgeLoop);
|
||||
WIRE(IfcPolyline);
|
||||
WIRE(IfcPolyLoop);
|
||||
WIRE(IfcCompositeCurve);
|
||||
WIRE(IfcTrimmedCurve);
|
||||
WIRE(IfcArbitraryOpenProfileDef);
|
||||
#ifdef USE_IFC4
|
||||
WIRE(IfcIndexedPolyCurve)
|
||||
#endif
|
||||
|
||||
CURVE(IfcCircle);
|
||||
CURVE(IfcEllipse);
|
||||
CURVE(IfcLine);
|
||||
#ifdef USE_IFC4
|
||||
// IfcRationalBSplineCurveWithKnots included
|
||||
CURVE(IfcBSplineCurveWithKnots);
|
||||
#endif
|
||||
|
||||
CLASS(IfcCartesianPoint,gp_Pnt);
|
||||
CLASS(IfcDirection,gp_Dir);
|
||||
|
||||
@@ -6,8 +6,17 @@
|
||||
if ( convert((T*)l,r) ) { \
|
||||
success = true; \
|
||||
} \
|
||||
} catch(...) { } \
|
||||
if ( !success) { \
|
||||
} catch (const std::exception& e) { \
|
||||
Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + "\nFailed to convert:", l->entity); \
|
||||
return false; \
|
||||
} catch (const Standard_Failure& f) { \
|
||||
if (f.GetMessageString() && strlen(f.GetMessageString())) \
|
||||
Logger::Message(Logger::LOG_ERROR, std::string("Error in: ") + f.GetMessageString() + "\nFailed to convert:", l->entity); \
|
||||
else \
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert:", l->entity); \
|
||||
return false; \
|
||||
} \
|
||||
if (!success) { \
|
||||
Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l->entity); \
|
||||
return false; \
|
||||
} \
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
if ( l->is(T::Class()) ) { \
|
||||
try { \
|
||||
return convert((T*)l,r); \
|
||||
} catch (...) { } \
|
||||
Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l->entity); \
|
||||
} catch (const std::exception& e) { \
|
||||
Logger::Message(Logger::LOG_ERROR, std::string(e.what()) + "\nFailed to convert:", l->entity); \
|
||||
} catch (const Standard_Failure& f) { \
|
||||
if (f.GetMessageString()) \
|
||||
Logger::Message(Logger::LOG_ERROR, std::string("Error in: ") + f.GetMessageString() + "\nFailed to convert:", l->entity); \
|
||||
else \
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to convert:", l->entity); \
|
||||
} \
|
||||
return false; \
|
||||
}
|
||||
#include "IfcRegisterDef.h"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "IfcRegisterUndef.h"
|
||||
#define SHAPES(T) \
|
||||
if ( l->is(T::Class()) ) return true;
|
||||
#include "IfcRegisterDef.h"
|
||||
|
||||
#include "IfcRegister.h"
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "IfcRegisterUndef.h"
|
||||
#define SHAPES(T) \
|
||||
if ( l->is(T::Class()) ) return ST_SHAPELIST;
|
||||
#define SHAPE(T) \
|
||||
if ( l->is(T::Class()) ) return ST_SHAPE;
|
||||
#define WIRE(T) \
|
||||
if ( l->is(T::Class()) ) return ST_WIRE;
|
||||
#define FACE(T) \
|
||||
if ( l->is(T::Class()) ) return ST_FACE;
|
||||
#define CURVE(T) \
|
||||
if ( l->is(T::Class()) ) return ST_CURVE;
|
||||
#include "IfcRegisterDef.h"
|
||||
|
||||
#include "IfcRegister.h"
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "../ifcgeom/IfcGeomRenderStyles.h"
|
||||
|
||||
namespace IfcGeom {
|
||||
class IfcRepresentationShapeItem {
|
||||
class IFC_GEOM_API IfcRepresentationShapeItem {
|
||||
private:
|
||||
gp_GTrsf placement;
|
||||
TopoDS_Shape shape;
|
||||
@@ -46,6 +46,7 @@ namespace IfcGeom {
|
||||
const gp_GTrsf& Placement() const { return placement; }
|
||||
bool hasStyle() const { return style != 0; }
|
||||
const SurfaceStyle& Style() const { return *style; }
|
||||
void setStyle(const SurfaceStyle* newStyle) { style = newStyle; }
|
||||
};
|
||||
typedef std::vector<IfcRepresentationShapeItem> IfcRepresentationShapeItems;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef IFC_GEOM_API_H
|
||||
#define IFC_GEOM_API_H
|
||||
|
||||
#ifdef IFC_SHARED_BUILD
|
||||
#ifdef _WIN32
|
||||
#ifdef IFC_GEOM_EXPORTS
|
||||
#define IFC_GEOM_API __declspec(dllexport)
|
||||
#else
|
||||
#define IFC_GEOM_API __declspec(dllimport)
|
||||
#endif
|
||||
#else // simply assume *nix + GCC-like compiler
|
||||
#define IFC_GEOM_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
#else
|
||||
#define IFC_GEOM_API
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -27,9 +27,12 @@
|
||||
#include <iostream>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
// NB: Streams are only re-opened as binary when compiled with MSVC currently.
|
||||
// It is unclear what the correct behaviour would be compiled with e.g MinGW
|
||||
#if defined(_MSC_VER)
|
||||
#define SET_BINARY_STREAMS
|
||||
#endif
|
||||
|
||||
#ifdef SET_BINARY_STREAMS
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
@@ -37,13 +40,27 @@
|
||||
|
||||
#include "../ifcgeom/IfcGeomIterator.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
#endif
|
||||
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
|
||||
using namespace boost;
|
||||
|
||||
template <typename T>
|
||||
union data_field {
|
||||
char buffer[sizeof(T)];
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T sread(std::istream& s) {
|
||||
char buf[sizeof(T)];
|
||||
s.read(buf, sizeof(T));
|
||||
return *((T*)buf);
|
||||
data_field<T> data;
|
||||
s.read(data.buffer, sizeof(T));
|
||||
return data.value;
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -54,10 +71,21 @@ std::string sread(std::istream& s) {
|
||||
buf[len] = 0;
|
||||
while (len++ % 4) s.get();
|
||||
std::string str(buf);
|
||||
delete buf;
|
||||
delete[] buf;
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string format_json(const T& t) {
|
||||
return boost::lexical_cast<std::string>(t);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string format_json(const std::string& s) {
|
||||
// NB: No escaping whatsoever. Only use alphanumeric values.
|
||||
return "\"" + s + "\"";
|
||||
}
|
||||
|
||||
static std::streambuf *stdout_orig, *stdout_redir;
|
||||
|
||||
template <typename T>
|
||||
@@ -69,7 +97,7 @@ void swrite(std::ostream& s, T t) {
|
||||
|
||||
template <>
|
||||
void swrite(std::ostream& s, std::string t) {
|
||||
int32_t len = t.size();
|
||||
int32_t len = (int32_t)t.size();
|
||||
swrite(s, len);
|
||||
s.write(t.c_str(), len);
|
||||
while (len++ % 4) s.put(0);
|
||||
@@ -109,6 +137,8 @@ const int32_t NEXT = MORE + 1;
|
||||
const int32_t BYE = NEXT + 1;
|
||||
const int32_t GET_LOG = BYE + 1;
|
||||
const int32_t LOG = GET_LOG + 1;
|
||||
const int32_t DEFLECTION = LOG + 1;
|
||||
const int32_t SETTING = DEFLECTION + 1;
|
||||
|
||||
class Hello : public Command {
|
||||
private:
|
||||
@@ -122,7 +152,7 @@ protected:
|
||||
}
|
||||
public:
|
||||
const std::string& string() { return str; }
|
||||
Hello() : Command(HELLO), str("IfcOpenShell-" IFCOPENSHELL_VERSION) {}
|
||||
Hello() : Command(HELLO), str("IfcOpenShell-" IFCOPENSHELL_VERSION "-2") {}
|
||||
};
|
||||
|
||||
class More : public Command {
|
||||
@@ -156,16 +186,16 @@ public:
|
||||
|
||||
class Get : public Command {
|
||||
protected:
|
||||
void read_content(std::istream& s) {}
|
||||
void write_content(std::ostream& s) {}
|
||||
void read_content(std::istream& /*s*/) {}
|
||||
void write_content(std::ostream& /*s*/) {}
|
||||
public:
|
||||
Get() : Command(GET) {};
|
||||
};
|
||||
|
||||
class GetLog : public Command {
|
||||
protected:
|
||||
void read_content(std::istream& s) {}
|
||||
void write_content(std::ostream& s) {}
|
||||
void read_content(std::istream& /*s*/) {}
|
||||
void write_content(std::ostream& /*s*/) {}
|
||||
public:
|
||||
GetLog() : Command(GET_LOG) {};
|
||||
};
|
||||
@@ -184,11 +214,18 @@ public:
|
||||
WriteLog(const std::string& str) : Command(LOG), str(str) {};
|
||||
};
|
||||
|
||||
class EntityExtension {
|
||||
public:
|
||||
virtual void write_contents(std::ostream& s) = 0;
|
||||
};
|
||||
|
||||
class Entity : public Command {
|
||||
private:
|
||||
const IfcGeom::TriangulationElement<float>* geom;
|
||||
bool append_line_data;
|
||||
EntityExtension* eext_;
|
||||
protected:
|
||||
void read_content(std::istream& s) {}
|
||||
void read_content(std::istream& /*s*/) {}
|
||||
void write_content(std::ostream& s) {
|
||||
swrite<int32_t>(s, geom->id());
|
||||
swrite(s, geom->guid());
|
||||
@@ -203,19 +240,48 @@ protected:
|
||||
0, 0, 0, 1
|
||||
};
|
||||
swrite(s, std::string((char*)matrix_array, 16 * sizeof(float)));
|
||||
swrite<int32_t>(s, geom->geometry().id());
|
||||
|
||||
// The first bit of the string is always the instance name of the representation.
|
||||
const std::string& representation_id = geom->geometry().id();
|
||||
const int integer_representation_id = atoi(representation_id.c_str());
|
||||
swrite<int32_t>(s, (int32_t)integer_representation_id);
|
||||
|
||||
swrite(s, std::string((char*)geom->geometry().verts().data(), geom->geometry().verts().size() * sizeof(float)));
|
||||
swrite(s, std::string((char*)geom->geometry().normals().data(), geom->geometry().normals().size() * sizeof(float)));
|
||||
{ std::vector<int32_t> indices;
|
||||
for (std::vector<int>::const_iterator it = geom->geometry().faces().begin(); it != geom->geometry().faces().end(); ++it) {
|
||||
indices.push_back(*it);
|
||||
}
|
||||
swrite(s, std::string((char*) indices.data(), indices.size() * sizeof(int32_t))); }
|
||||
{
|
||||
std::vector<int32_t> indices;
|
||||
const std::vector<int>& faces = geom->geometry().faces();
|
||||
indices.reserve(faces.size());
|
||||
for (std::vector<int>::const_iterator it = faces.begin(); it != faces.end(); ++it) {
|
||||
indices.push_back(*it);
|
||||
}
|
||||
swrite(s, std::string((char*) indices.data(), indices.size() * sizeof(int32_t)));
|
||||
|
||||
if (append_line_data) {
|
||||
std::vector<int32_t> lines;
|
||||
std::set<int32_t> faces_set (indices.begin(), indices.end());
|
||||
|
||||
const std::vector<int>& edges = geom->geometry().edges();
|
||||
for ( std::vector<int>::const_iterator it = edges.begin(); it != edges.end(); ) {
|
||||
const int32_t i1 = *(it++);
|
||||
const int32_t i2 = *(it++);
|
||||
|
||||
if (faces_set.find(i1) != faces_set.end() || faces_set.find(i2) != faces_set.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lines.push_back(i1);
|
||||
lines.push_back(i2);
|
||||
}
|
||||
|
||||
swrite(s, std::string((char*) lines.data(), lines.size() * sizeof(int32_t)));
|
||||
}
|
||||
}
|
||||
{ std::vector<float> diffuse_color_array;
|
||||
for (std::vector<IfcGeom::Material>::const_iterator it = geom->geometry().materials().begin(); it != geom->geometry().materials().end(); ++it) {
|
||||
const IfcGeom::Material& m = *it;
|
||||
if (m.hasDiffuse()) {
|
||||
const double* color = m.diffuse();
|
||||
const IfcGeom::Material& mat = *it;
|
||||
if (mat.hasDiffuse()) {
|
||||
const double* color = mat.diffuse();
|
||||
diffuse_color_array.push_back(static_cast<float>(color[0]));
|
||||
diffuse_color_array.push_back(static_cast<float>(color[1]));
|
||||
diffuse_color_array.push_back(static_cast<float>(color[2]));
|
||||
@@ -224,8 +290,8 @@ protected:
|
||||
diffuse_color_array.push_back(0.f);
|
||||
diffuse_color_array.push_back(0.f);
|
||||
}
|
||||
if (m.hasTransparency()) {
|
||||
diffuse_color_array.push_back(static_cast<float>(1. - m.transparency()));
|
||||
if (mat.hasTransparency()) {
|
||||
diffuse_color_array.push_back(static_cast<float>(1. - mat.transparency()));
|
||||
} else {
|
||||
diffuse_color_array.push_back(1.f);
|
||||
}
|
||||
@@ -236,32 +302,161 @@ protected:
|
||||
material_indices.push_back(*it);
|
||||
}
|
||||
swrite(s, std::string((char*) material_indices.data(), material_indices.size() * sizeof(int32_t))); }
|
||||
if (eext_) {
|
||||
eext_->write_contents(s);
|
||||
}
|
||||
}
|
||||
public:
|
||||
Entity(const IfcGeom::TriangulationElement<float>* geom) : Command(ENTITY), geom(geom) {};
|
||||
Entity(const IfcGeom::TriangulationElement<float>* geom, EntityExtension* eext = 0) : Command(ENTITY), geom(geom), append_line_data(false), eext_(eext) {};
|
||||
};
|
||||
|
||||
class Next : public Command {
|
||||
protected:
|
||||
void read_content(std::istream& s) {}
|
||||
void write_content(std::ostream& s) {}
|
||||
void read_content(std::istream& /*s*/) {}
|
||||
void write_content(std::ostream& /*s*/) {}
|
||||
public:
|
||||
Next() : Command(NEXT) {};
|
||||
};
|
||||
|
||||
class Bye : public Command {
|
||||
protected:
|
||||
void read_content(std::istream& s) {}
|
||||
void write_content(std::ostream& s) {}
|
||||
void read_content(std::istream& /*s*/) {}
|
||||
void write_content(std::ostream& /*s*/) {}
|
||||
public:
|
||||
Bye() : Command(BYE) {};
|
||||
};
|
||||
|
||||
int main (int argc, char** argv) {
|
||||
if (sizeof(float) != 4 || sizeof(int32_t) != 4) {
|
||||
return 1;
|
||||
class Deflection : public Command {
|
||||
private:
|
||||
double deflection_;
|
||||
protected:
|
||||
void read_content(std::istream& s) {
|
||||
deflection_ = sread<double>(s);
|
||||
}
|
||||
void write_content(std::ostream& s) {
|
||||
swrite(s, deflection_);
|
||||
}
|
||||
public:
|
||||
Deflection(double d = 0.) : Command(DEFLECTION), deflection_(d) {};
|
||||
double deflection() const { return deflection_; }
|
||||
};
|
||||
|
||||
class Setting : public Command {
|
||||
private:
|
||||
uint32_t id_;
|
||||
uint32_t value_;
|
||||
protected:
|
||||
void read_content(std::istream& s) {
|
||||
id_ = sread<uint32_t>(s);
|
||||
value_ = sread<uint32_t>(s);
|
||||
}
|
||||
void write_content(std::ostream& s) {
|
||||
swrite(s, id_);
|
||||
swrite(s, value_);
|
||||
}
|
||||
public:
|
||||
Setting(uint32_t k = 0, uint32_t v = 0) : Command(DEFLECTION), id_(k), value_(v) {};
|
||||
uint32_t id() const { return id_; }
|
||||
uint32_t value() const { return value_; }
|
||||
};
|
||||
|
||||
static const std::string TOTAL_SURFACE_AREA = "TOTAL_SURFACE_AREA";
|
||||
static const std::string TOTAL_SHAPE_VOLUME = "TOTAL_SHAPE_VOLUME";
|
||||
static const std::string WALKABLE_SURFACE_AREA = "WALKABLE_SURFACE_AREA";
|
||||
static const double MAX_WALKABLE_SURFACE_ANGLE_DEGREES = 15.;
|
||||
|
||||
class QuantityWriter : public EntityExtension {
|
||||
private:
|
||||
const IfcGeom::BRepElement<float>* elem_;
|
||||
public:
|
||||
QuantityWriter(const IfcGeom::BRepElement<float>* elem) :
|
||||
elem_(elem)
|
||||
{}
|
||||
void write_contents(std::ostream& s) {
|
||||
|
||||
double total_surface_area = 0.;
|
||||
double total_shape_volume = 0.;
|
||||
double walkable_surface_area = 0.;
|
||||
|
||||
for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = elem_->geometry().begin(); it != elem_->geometry().end(); ++it) {
|
||||
gp_GTrsf gtrsf = it->Placement();
|
||||
const gp_Trsf& o_trsf = elem_->transformation().data();
|
||||
gtrsf.PreMultiply(o_trsf);
|
||||
const TopoDS_Shape& shp = it->Shape();
|
||||
const TopoDS_Shape moved_shape = IfcGeom::Kernel::apply_transformation(shp, gtrsf);
|
||||
|
||||
{
|
||||
GProp_GProps prop_area;
|
||||
BRepGProp::SurfaceProperties(moved_shape, prop_area);
|
||||
total_surface_area += prop_area.Mass();
|
||||
}
|
||||
|
||||
{
|
||||
GProp_GProps prop_volume;
|
||||
BRepGProp::VolumeProperties(moved_shape, prop_volume);
|
||||
total_shape_volume += prop_volume.Mass();
|
||||
}
|
||||
|
||||
if (elem_->type() == "IfcSpace") {
|
||||
TopExp_Explorer exp(moved_shape, TopAbs_FACE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Face& face = TopoDS::Face(exp.Current());
|
||||
Handle(Geom_Surface) surf = BRep_Tool::Surface(face);
|
||||
|
||||
// Assume we can only walk on planar surfaces
|
||||
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BRepGProp_Face prop(face);
|
||||
double u0, u1, v0, v1;
|
||||
BRepTools::UVBounds(face, u0, u1, v0, v1);
|
||||
gp_Pnt p;
|
||||
gp_Vec normal_direction;
|
||||
prop.Normal((u0 + u1) / 2., (v0 + v1) / 2., p, normal_direction);
|
||||
|
||||
gp_Vec normal(0., 0., 0.);
|
||||
if (normal_direction.Magnitude() > ALMOST_ZERO) {
|
||||
normal = gp_Dir(normal_direction.XYZ());
|
||||
}
|
||||
|
||||
if (normal.Angle(gp::DZ()) < (MAX_WALKABLE_SURFACE_ANGLE_DEGREES * M_PI / 180.0)) {
|
||||
GProp_GProps prop_face;
|
||||
BRepGProp::SurfaceProperties(face, prop_face);
|
||||
walkable_surface_area += prop_face.Mass();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Manual JSON formatting is always a bad idea
|
||||
std::ostringstream ss;
|
||||
ss.write("{", 1);
|
||||
ss << format_json(TOTAL_SURFACE_AREA);
|
||||
ss.write(":", 1);
|
||||
ss << format_json(total_surface_area);
|
||||
ss.write(",", 1);
|
||||
ss << format_json(TOTAL_SHAPE_VOLUME);
|
||||
ss.write(":", 1);
|
||||
ss << format_json(total_shape_volume);
|
||||
if (elem_->type() == "IfcSpace") {
|
||||
ss.write(",", 1);
|
||||
ss << format_json(WALKABLE_SURFACE_AREA);
|
||||
ss.write(":", 1);
|
||||
ss << format_json(walkable_surface_area);
|
||||
}
|
||||
ss.write("}", 1);
|
||||
|
||||
// We do a 4-byte manual alignment
|
||||
std::string payload = ss.str();
|
||||
s << payload;
|
||||
if (payload.size() % 4) {
|
||||
s << std::string(4 - (payload.size() % 4), ' ');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main () {
|
||||
// Redirect stdout to this stream, so that involuntary
|
||||
// writes to stdout do not interfere with our protocol.
|
||||
std::ostringstream oss;
|
||||
@@ -276,14 +471,16 @@ int main (int argc, char** argv) {
|
||||
std::cin.setf(std::ios_base::binary);
|
||||
#endif
|
||||
|
||||
double deflection = 1.e-3;
|
||||
bool has_more = false;
|
||||
|
||||
IfcGeom::Iterator<float>* iterator = 0;
|
||||
std::vector< std::pair<uint32_t, uint32_t> > setting_pairs;
|
||||
|
||||
Hello().write(std::cout);
|
||||
|
||||
int exit_code = 0;
|
||||
while (1) {
|
||||
for (;;) {
|
||||
const int32_t msg_type = sread<int32_t>(std::cin);
|
||||
switch (msg_type) {
|
||||
case IFC_MODEL: {
|
||||
@@ -293,13 +490,20 @@ int main (int argc, char** argv) {
|
||||
memcpy(data, m.string().c_str(), len);
|
||||
|
||||
IfcGeom::IteratorSettings settings;
|
||||
settings.use_world_coords() = false;
|
||||
settings.weld_vertices() = false;
|
||||
settings.convert_back_units() = true;
|
||||
settings.force_ccw_face_orientation() = true;
|
||||
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
|
||||
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
|
||||
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true);
|
||||
// settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, true);
|
||||
|
||||
iterator = new IfcGeom::Iterator<float>(settings, data, len);
|
||||
has_more = iterator->findContext();
|
||||
std::vector< std::pair<uint32_t, uint32_t> >::const_iterator it = setting_pairs.begin();
|
||||
for (; it != setting_pairs.end(); ++it) {
|
||||
settings.set(it->first, it->second != 0);
|
||||
}
|
||||
|
||||
settings.set_deflection_tolerance(deflection);
|
||||
|
||||
iterator = new IfcGeom::Iterator<float>(settings, data, (int)len);
|
||||
has_more = iterator->initialize();
|
||||
|
||||
More(has_more).write(std::cout);
|
||||
continue;
|
||||
@@ -311,12 +515,13 @@ int main (int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
const IfcGeom::TriangulationElement<float>* geom = static_cast<const IfcGeom::TriangulationElement<float>*>(iterator->get());
|
||||
Entity(geom).write(std::cout);
|
||||
QuantityWriter eext(iterator->get_native());
|
||||
Entity(geom, &eext).write(std::cout);
|
||||
continue;
|
||||
}
|
||||
case NEXT: {
|
||||
Next n; n.read(std::cin);
|
||||
has_more = iterator->next();
|
||||
has_more = iterator->next() != 0;
|
||||
if (!has_more) {
|
||||
delete iterator;
|
||||
iterator = 0;
|
||||
@@ -334,7 +539,27 @@ int main (int argc, char** argv) {
|
||||
exit_code = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case DEFLECTION: {
|
||||
Deflection d; d.read(std::cin);
|
||||
if (!iterator) {
|
||||
deflection = d.deflection();
|
||||
continue;
|
||||
} else {
|
||||
exit_code = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SETTING: {
|
||||
Setting s; s.read(std::cin);
|
||||
if (!iterator) {
|
||||
setting_pairs.push_back(std::make_pair(s.id(), s.value()));
|
||||
continue;
|
||||
} else {
|
||||
exit_code = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
exit_code = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
IfcGeomServer
|
||||
-------------
|
||||
|
||||
A command-line executable intented to be ran as a child process that receives an IFC model from stdin and will send binary geometry information of products found in the IFC file in separate messages on stdout. The advantage over conventional static or dynamic linking is that, in case the IfcOpenShell process would crash (either due to invalid input, heap overflow, bugs, ...), this does not affect the main process. Currently, the only implementation of a consumer for this process is the Java module over at: https://github.com/opensourceBIM/IfcOpenShell-BIMserver-plugin/blob/master/src/org/ifcopenshell/IfcGeomServerClient.java
|
||||
@@ -0,0 +1,40 @@
|
||||
################################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} ${ICU_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIRS} ${THREEDS_MAX_SDK_HOME}/include
|
||||
)
|
||||
|
||||
# All recent versions of 3ds Max (2014 and newer) are 64-bit only so assume lib/x64 directory
|
||||
LINK_DIRECTORIES(${LINK_DIRECTORIES} ${IfcOpenShell_BINARY_DIR} ${OCC_LIBRARY_DIR} ${OPENCOLLADA_LIBRARY_DIR}
|
||||
${ICU_LIBRARY_DIR} ${Boost_LIBRARY_DIRS} ${THREEDS_MAX_SDK_HOME}/lib/x64/Release
|
||||
)
|
||||
|
||||
ADD_LIBRARY(IfcMax SHARED IfcMax.h IfcMax.cpp)
|
||||
|
||||
# TODO: find the minimal subset of 3dsmax libraries to reference
|
||||
TARGET_LINK_LIBRARIES(IfcMax ${IFCOPENSHELL_LIBRARIES} Comctl32.lib zlibdll.lib bmm.lib core.lib CustDlg.lib edmodel.lib expr.lib
|
||||
flt.lib geom.lib gfx.lib gup.lib imageViewers.lib ManipSys.lib maxnet.lib Maxscrpt.lib
|
||||
maxutil.lib MenuMan.lib menus.lib mesh.lib MNMath.lib Paramblk2.lib particle.lib Poly.lib RenderUtil.lib
|
||||
tessint.lib viewfile.lib ${OPENCASCADE_LIBRARIES}
|
||||
)
|
||||
|
||||
SET_TARGET_PROPERTIES(IfcMax PROPERTIES SUFFIX ".dli")
|
||||
|
||||
INSTALL(TARGETS IfcMax RUNTIME DESTINATION ${BINDIR})
|
||||
+90
-51
@@ -17,49 +17,57 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <Max.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <stdmat.h>
|
||||
#include <istdplug.h>
|
||||
|
||||
#include "../ifcmax/IfcMax.h"
|
||||
#include "IfcMax.h"
|
||||
#include "../ifcgeom/IfcGeomIterator.h"
|
||||
|
||||
static const int NUM_MATERIAL_SLOTS = 24;
|
||||
|
||||
int controlsInit = false;
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) {
|
||||
BOOL WINAPI DllMain(HINSTANCE /*hinstDLL*/, ULONG /*fdwReason*/, LPVOID /*lpvReserved*/) {
|
||||
static int controlsInit = false;
|
||||
if (!controlsInit) {
|
||||
controlsInit = true;
|
||||
InitCommonControls();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
__declspec( dllexport ) const TCHAR* LibDescription() {
|
||||
return _T("IfcOpenShell IFC Importer");
|
||||
}
|
||||
|
||||
__declspec( dllexport ) int LibNumberClasses() { return 1; }
|
||||
|
||||
static class IFCImpClassDesc:public ClassDesc {
|
||||
static class IFCImpClassDesc :public ClassDesc {
|
||||
public:
|
||||
int IsPublic() {return 1;}
|
||||
void * Create(BOOL loading = FALSE) {return new IFCImp;}
|
||||
const TCHAR * ClassName() {return _T("IFCImp");}
|
||||
SClass_ID SuperClassID() {return SCENE_IMPORT_CLASS_ID;}
|
||||
Class_ID ClassID() {return Class_ID(0x3f230dbf, 0x5b3015c2);}
|
||||
const TCHAR* Category() {return _T("Chrutilities");}
|
||||
int IsPublic() { return 1; }
|
||||
void * Create(BOOL /*loading = FALSE*/) { return new IFCImp; }
|
||||
// TODO Delete() function?
|
||||
const TCHAR * ClassName() { return _T("IFCImp"); }
|
||||
SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; }
|
||||
Class_ID ClassID() { return Class_ID(0x3f230dbf, 0x5b3015c2); }
|
||||
const TCHAR* Category() { return _T("Chrutilities"); }
|
||||
} IFCImpDesc;
|
||||
|
||||
__declspec( dllexport ) ClassDesc* LibClassDesc(int i) {
|
||||
return i == 0 ? &IFCImpDesc : 0;
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
|
||||
extern "C" {
|
||||
|
||||
DLLEXPORT const TCHAR* LibDescription() {
|
||||
return _T("IfcOpenShell IFC Importer");
|
||||
}
|
||||
|
||||
__declspec( dllexport ) ULONG LibVersion() {
|
||||
return VERSION_3DSMAX;
|
||||
DLLEXPORT int LibNumberClasses() { return 1; }
|
||||
|
||||
DLLEXPORT ClassDesc* LibClassDesc(int i) {
|
||||
return i == 0 ? &IFCImpDesc : 0;
|
||||
}
|
||||
|
||||
DLLEXPORT ULONG LibVersion() {
|
||||
return VERSION_3DSMAX;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
int IFCImp::ExtCount() { return 1; }
|
||||
|
||||
const TCHAR * IFCImp::Ext(int n) {
|
||||
@@ -79,7 +87,7 @@ const TCHAR * IFCImp::AuthorName() {
|
||||
}
|
||||
|
||||
const TCHAR * IFCImp::CopyrightMessage() {
|
||||
return _T("Copyight (c) 2011 IfcOpenShell");
|
||||
return _T("Copyright (c) 2011-2016 IfcOpenShell");
|
||||
}
|
||||
|
||||
const TCHAR * IFCImp::OtherMessage1() {
|
||||
@@ -94,13 +102,14 @@ unsigned int IFCImp::Version() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK AboutBoxDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
return TRUE;
|
||||
}
|
||||
// TODO Use this in IFCImp::ShowAbout() if/when wanted
|
||||
//static BOOL CALLBACK AboutBoxDlgProc(HWND /*hWnd*/, UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lParam*/) {
|
||||
// return TRUE;
|
||||
//}
|
||||
|
||||
void IFCImp::ShowAbout(HWND hWnd) {}
|
||||
void IFCImp::ShowAbout(HWND /*hWnd*/) {}
|
||||
|
||||
DWORD WINAPI fn(LPVOID arg) { return 0; }
|
||||
DWORD WINAPI fn(LPVOID /*arg*/) { return 0; }
|
||||
|
||||
#if MAX_RELEASE > 14000
|
||||
# define S(x) (TSTR::FromCStr(x.c_str()))
|
||||
@@ -110,8 +119,9 @@ DWORD WINAPI fn(LPVOID arg) { return 0; }
|
||||
# define S(x) (CStr(x.c_str()))
|
||||
#endif
|
||||
|
||||
Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_name) {
|
||||
const int mat_index = library->FindMtlByName(S(material_name));
|
||||
static Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_name) {
|
||||
TSTR mat_name = S(material_name);
|
||||
const int mat_index = library->FindMtlByName(mat_name);
|
||||
Mtl* m = 0;
|
||||
if (mat_index != -1) {
|
||||
m = static_cast<Mtl*>((*library)[mat_index]);
|
||||
@@ -119,7 +129,7 @@ Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_name) {
|
||||
return m;
|
||||
}
|
||||
|
||||
Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& slot, const IfcGeom::Material& material) {
|
||||
static Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& slot, const IfcGeom::Material& material) {
|
||||
Mtl* m = FindMaterialByName(library, material.name());
|
||||
if (m == 0) {
|
||||
StdMat2* stdm = NewDefaultStdMat();
|
||||
@@ -133,10 +143,10 @@ Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& sl
|
||||
stdm->SetSpecular(Color(specular[0], specular[1], specular[2]),t);
|
||||
}
|
||||
if (material.hasSpecularity()) {
|
||||
stdm->SetShininess(material.specularity(), t);
|
||||
stdm->SetShininess((float)material.specularity(), t);
|
||||
}
|
||||
if (material.hasTransparency()) {
|
||||
stdm->SetOpacity(1.0 - material.transparency(), t);
|
||||
stdm->SetOpacity(1.0f - (float)material.transparency(), t);
|
||||
}
|
||||
m = stdm;
|
||||
m->SetName(S(material.name()));
|
||||
@@ -148,7 +158,10 @@ Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& sl
|
||||
return m;
|
||||
}
|
||||
|
||||
Mtl* ComposeMultiMaterial(std::map<std::vector<std::string>, Mtl*>& multi_mats, MtlBaseLib* library, Interface* max_interface, int& slot, const std::vector<IfcGeom::Material>& materials, const std::string& object_type, const std::vector<int>& material_ids) {
|
||||
static Mtl* ComposeMultiMaterial(std::map<std::vector<std::string>, Mtl*>& multi_mats, MtlBaseLib* library,
|
||||
Interface* max_interface, int& slot, const std::vector<IfcGeom::Material>& materials,
|
||||
const std::string& object_type, const std::vector<int>& material_ids)
|
||||
{
|
||||
std::vector<std::string> material_names;
|
||||
bool needs_default = std::find(material_ids.begin(), material_ids.end(), -1) != material_ids.end();
|
||||
if (needs_default) {
|
||||
@@ -181,7 +194,7 @@ Mtl* ComposeMultiMaterial(std::map<std::vector<std::string>, Mtl*>& multi_mats,
|
||||
return i->second;
|
||||
}
|
||||
MultiMtl* multi_mat = NewDefaultMultiMtl();
|
||||
multi_mat->SetNumSubMtls(material_names.size());
|
||||
multi_mat->SetNumSubMtls((int)material_names.size());
|
||||
int mtl_id = 0;
|
||||
if (needs_default) {
|
||||
multi_mat->SetSubMtlAndName(mtl_id ++, default_material, default_material->GetName());
|
||||
@@ -198,12 +211,12 @@ Mtl* ComposeMultiMaterial(std::map<std::vector<std::string>, Mtl*>& multi_mats,
|
||||
return multi_mat;
|
||||
}
|
||||
|
||||
int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc, BOOL suppressPrompts) {
|
||||
int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc, BOOL /*suppressPrompts*/) {
|
||||
|
||||
IfcGeom::IteratorSettings settings;
|
||||
settings.use_world_coords() = false;
|
||||
settings.weld_vertices() = true;
|
||||
settings.sew_shells() = true;
|
||||
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
|
||||
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, true);
|
||||
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
|
||||
|
||||
#ifdef _UNICODE
|
||||
int fn_buffer_size = WideCharToMultiByte(CP_UTF8, 0, name, -1, 0, 0, 0, 0);
|
||||
@@ -214,8 +227,8 @@ int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc,
|
||||
#endif
|
||||
|
||||
IfcGeom::Iterator<float> iterator(settings, fn_mb);
|
||||
|
||||
if (!iterator.findContext()) return false;
|
||||
delete fn_mb;
|
||||
if (!iterator.initialize()) return false;
|
||||
|
||||
itfc->ProgressStart(_T("Importing file..."), TRUE, fn, NULL);
|
||||
|
||||
@@ -234,28 +247,54 @@ int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc,
|
||||
|
||||
TriObject* tri = CreateNewTriObject();
|
||||
|
||||
const int numVerts = o->geometry().verts().size()/3;
|
||||
const int numVerts = (int)o->geometry().verts().size()/3;
|
||||
tri->mesh.setNumVerts(numVerts);
|
||||
for( int i = 0; i < numVerts; i ++ ) {
|
||||
tri->mesh.setVert(i,o->geometry().verts()[3*i+0],o->geometry().verts()[3*i+1],o->geometry().verts()[3*i+2]);
|
||||
}
|
||||
const int numFaces = o->geometry().faces().size()/3;
|
||||
const int numFaces = (int)o->geometry().faces().size()/3;
|
||||
tri->mesh.setNumFaces(numFaces);
|
||||
|
||||
bool needs_default = std::find(o->geometry().material_ids().begin(), o->geometry().material_ids().end(), -1) != o->geometry().material_ids().end();
|
||||
|
||||
typedef std::pair<int, int> edge_t;
|
||||
|
||||
std::set<edge_t> face_boundaries;
|
||||
for(std::vector<int>::const_iterator it = o->geometry().edges().begin(); it != o->geometry().edges().end();) {
|
||||
const int v1 = *it++;
|
||||
const int v2 = *it++;
|
||||
|
||||
const edge_t e((std::min)(v1, v2), (std::max)(v1, v2));
|
||||
face_boundaries.insert(e);
|
||||
}
|
||||
|
||||
for( int i = 0; i < numFaces; i ++ ) {
|
||||
tri->mesh.faces[i].setVerts(o->geometry().faces()[3*i+0],o->geometry().faces()[3*i+1],o->geometry().faces()[3*i+2]);
|
||||
tri->mesh.faces[i].setEdgeVisFlags(o->geometry().edges()[3*i+0],o->geometry().edges()[3*i+1],o->geometry().edges()[3*i+2]);
|
||||
MtlID mtlid = o->geometry().material_ids()[i];
|
||||
if (needs_default) mtlid ++;
|
||||
const int v1 = o->geometry().faces()[3*i+0];
|
||||
const int v2 = o->geometry().faces()[3*i+1];
|
||||
const int v3 = o->geometry().faces()[3*i+2];
|
||||
|
||||
const edge_t e1((std::min)(v1, v2), (std::max)(v1, v2));
|
||||
const edge_t e2((std::min)(v2, v3), (std::max)(v2, v3));
|
||||
const edge_t e3((std::min)(v3, v1), (std::max)(v3, v1));
|
||||
|
||||
const bool b1 = face_boundaries.find(e1) != face_boundaries.end();
|
||||
const bool b2 = face_boundaries.find(e2) != face_boundaries.end();
|
||||
const bool b3 = face_boundaries.find(e3) != face_boundaries.end();
|
||||
|
||||
tri->mesh.faces[i].setVerts(v1, v2, v3);
|
||||
tri->mesh.faces[i].setEdgeVisFlags(b1, b2, b3);
|
||||
|
||||
MtlID mtlid = (MtlID)o->geometry().material_ids()[i];
|
||||
if (needs_default) {
|
||||
mtlid ++;
|
||||
}
|
||||
tri->mesh.faces[i].setMatID(mtlid);
|
||||
}
|
||||
|
||||
tri->mesh.buildNormals();
|
||||
// Either use this or undefine the FACESETS_AS_COMPOUND option in IfcGeom.h to have
|
||||
// properly oriented normals. Using only the line below will result in a consistent
|
||||
// orientation of normals accross shells, but not always oriented towards the
|
||||
// orientation of normals across shells, but not always oriented towards the
|
||||
// outside.
|
||||
// tri->mesh.UnifyNormals(false);
|
||||
tri->mesh.BuildStripsAndEdges();
|
||||
@@ -281,4 +320,4 @@ int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc,
|
||||
itfc->ProgressEnd();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
LIBRARY ifcmax.dli
|
||||
EXPORTS
|
||||
LibDescription @1
|
||||
LibNumberClasses @2
|
||||
LibClassDesc @3
|
||||
LibVersion @4
|
||||
SECTIONS
|
||||
.data READ WRITE
|
||||
+1
-7
@@ -21,12 +21,6 @@
|
||||
#define IFCMAX_H
|
||||
|
||||
#include "Max.h"
|
||||
#include "istdplug.h"
|
||||
#include "stdmat.h"
|
||||
#include "decomp.h"
|
||||
#include "shape.h"
|
||||
#include "splshape.h"
|
||||
#include "dummy.h"
|
||||
|
||||
extern ClassDesc* GetIFCImpDesc();
|
||||
|
||||
@@ -38,7 +32,7 @@ public:
|
||||
const TCHAR * LongDesc(); // = "IfcOpenShell IFC Importer for 3ds Max"
|
||||
const TCHAR * ShortDesc(); // = "Industry Foundation Classes"
|
||||
const TCHAR * AuthorName(); // = "Thomas Krijnen"
|
||||
const TCHAR * CopyrightMessage(); // = "Copyight (c) 2011 IfcOpenShell"
|
||||
const TCHAR * CopyrightMessage(); // = "Copyright (c) 2011-2016 IfcOpenShell"
|
||||
const TCHAR * OtherMessage1(); // = ""
|
||||
const TCHAR * OtherMessage2(); // = ""
|
||||
unsigned int Version(); // = 12
|
||||
|
||||
@@ -17,117 +17,59 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import functools
|
||||
import itertools
|
||||
|
||||
from functools import reduce
|
||||
if hasattr(os, 'uname'):
|
||||
platform_system = os.uname()[0].lower()
|
||||
else:
|
||||
platform_system = 'windows'
|
||||
|
||||
from . import guid
|
||||
if sys.maxsize == (1 << 31) - 1:
|
||||
platform_architecture = '32bit'
|
||||
else:
|
||||
platform_architecture = '64bit'
|
||||
|
||||
python_distribution = os.path.join(platform.system().lower(),
|
||||
platform.architecture()[0],
|
||||
'python%s.%s' % platform.python_version_tuple()[:2])
|
||||
python_version_tuple = tuple(sys.version.split(' ')[0].split('.'))
|
||||
|
||||
python_distribution = os.path.join(platform_system,
|
||||
platform_architecture,
|
||||
'python%s.%s' % python_version_tuple[:2])
|
||||
sys.path.append(os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'lib', python_distribution)))
|
||||
os.path.dirname(__file__),
|
||||
'lib', python_distribution)))
|
||||
|
||||
try:
|
||||
from . import ifcopenshell_wrapper
|
||||
except:
|
||||
raise ImportError("IfcOpenShell not built for '%s'" % python_distribution)
|
||||
from . import ifcopenshell_wrapper
|
||||
except Exception as e:
|
||||
if int(python_version_tuple[0]) == 2:
|
||||
# Only for py2, as py3 has exception chaining
|
||||
import traceback
|
||||
|
||||
class entity_instance(object):
|
||||
def __init__(self, e):
|
||||
super(entity_instance, self).__setattr__('wrapped_data', e)
|
||||
def __getattr__(self, name):
|
||||
try: return entity_instance.wrap_value(self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name)))
|
||||
except:
|
||||
try: return entity_instance.wrap_value(self.wrapped_data.get_inverse(name))
|
||||
except: raise AttributeError("entity instance of type '%s' has no attribute '%s'"%(self.wrapped_data.is_a(), name))
|
||||
@staticmethod
|
||||
def map_value(v):
|
||||
if isinstance(v, entity_instance): return v.wrapped_data
|
||||
elif isinstance(v, (tuple, list)) and len(v):
|
||||
classes = list(map(type, v))
|
||||
if float in classes: return ifcopenshell_wrapper.double_vector(v)
|
||||
elif int in classes: return ifcopenshell_wrapper.int_vector(v)
|
||||
elif str in classes: return ifcopenshell_wrapper.string_vector(v)
|
||||
elif entity_instance in classes: return list(map(lambda e: e.wrapped_data, v))
|
||||
return v
|
||||
@staticmethod
|
||||
def wrap_value(v):
|
||||
wrap = lambda e: entity_instance(e)
|
||||
if isinstance(v, ifcopenshell_wrapper.entity_instance): return wrap(v)
|
||||
elif isinstance(v, (tuple, list)) and len(v):
|
||||
classes = list(map(type, v))
|
||||
if ifcopenshell_wrapper.entity_instance in classes: return list(map(wrap, v))
|
||||
return v
|
||||
def attribute_type(self, attr):
|
||||
attr_idx = attr if isinstance(attr, int) else self.wrapped_data.get_argument_index(attr)
|
||||
return self.wrapped_data.get_argument_type(attr_idx)
|
||||
def attribute_name(self, attr_idx):
|
||||
return self.wrapped_data.get_argument_name(attr_idx)
|
||||
def __setattr__(self, key, value):
|
||||
self[self.wrapped_data.get_argument_index(key)] = value
|
||||
def __getitem__(self, key):
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_argument(key))
|
||||
def __setitem__(self, idx, value):
|
||||
self.wrapped_data.set_argument(idx, entity_instance.map_value(value))
|
||||
def __len__(self): return len(self.wrapped_data)
|
||||
def __repr__(self): return repr(self.wrapped_data)
|
||||
def is_a(self, *args): return self.wrapped_data.is_a(*args)
|
||||
def id(self): return self.wrapped_data.id()
|
||||
def __dir__(self):
|
||||
return sorted(set(itertools.chain(
|
||||
dir(type(self)),
|
||||
self.wrapped_data.get_attribute_names(),
|
||||
self.wrapped_data.get_inverse_attribute_names()
|
||||
)))
|
||||
traceback.print_exc()
|
||||
print('-' * 64)
|
||||
raise ImportError("IfcOpenShell not built for '%s'" % python_distribution)
|
||||
|
||||
|
||||
class file(object):
|
||||
def __init__(self, f=None):
|
||||
self.wrapped_data = f or ifcopenshell_wrapper.file(True)
|
||||
def create_entity(self,type,*args,**kwargs):
|
||||
e = entity_instance(ifcopenshell_wrapper.entity_instance(type))
|
||||
attrs = list(enumerate(args)) + \
|
||||
[(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
for idx, arg in attrs: e[idx] = arg
|
||||
self.wrapped_data.add(e.wrapped_data)
|
||||
e.wrapped_data.this.disown()
|
||||
return e
|
||||
def __getattr__(self, attr):
|
||||
if attr[0:6] == 'create': return functools.partial(self.create_entity,attr[6:])
|
||||
else: return getattr(self.wrapped_data, attr)
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, int):
|
||||
return entity_instance(self.wrapped_data.by_id(key))
|
||||
elif isinstance(key, str):
|
||||
return entity_instance(self.wrapped_data.by_guid(key))
|
||||
def add(self, inst):
|
||||
inst.wrapped_data.this.disown()
|
||||
return entity_instance(self.wrapped_data.add(inst.wrapped_data))
|
||||
def by_type(self, type):
|
||||
return [entity_instance(e) for e in self.wrapped_data.by_type(type)]
|
||||
def traverse(self, inst):
|
||||
return [entity_instance(e) for e in self.wrapped_data.traverse(inst.wrapped_data)]
|
||||
def remove(self, inst):
|
||||
return self.wrapped_data.remove(inst.wrapped_data)
|
||||
def __iter__(self):
|
||||
return iter(self[id] for id in self.wrapped_data.entity_names())
|
||||
from . import guid
|
||||
from .file import file
|
||||
from .entity_instance import entity_instance
|
||||
|
||||
|
||||
def open(fn=None):
|
||||
return file(ifcopenshell_wrapper.open(os.path.abspath(fn))) if fn else file()
|
||||
return file(ifcopenshell_wrapper.open(os.path.abspath(fn))) if fn else file()
|
||||
|
||||
|
||||
def create_entity(type,*args,**kwargs):
|
||||
e = entity_instance(ifcopenshell_wrapper.entity_instance(type))
|
||||
attrs = list(enumerate(args)) + \
|
||||
[(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
for idx, arg in attrs: e[idx] = arg
|
||||
return e
|
||||
def create_entity(type, *args, **kwargs):
|
||||
e = entity_instance(type)
|
||||
attrs = list(enumerate(args)) + \
|
||||
[(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
for idx, arg in attrs:
|
||||
e[idx] = arg
|
||||
return e
|
||||
|
||||
|
||||
from .main import *
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import functools
|
||||
import numbers
|
||||
import itertools
|
||||
|
||||
from . import ifcopenshell_wrapper
|
||||
|
||||
try:
|
||||
import logging
|
||||
except ImportError as e:
|
||||
logging = type('logger', (object,), {'exception': staticmethod(lambda s: print(s))})
|
||||
|
||||
|
||||
class entity_instance(object):
|
||||
"""
|
||||
This is the base python class for all IFC objects.
|
||||
|
||||
An instantiated entity_instance will have methods of Python and the IFC class itself.
|
||||
|
||||
example:
|
||||
|
||||
ifc_file = ifcopenshell.open(file_path)
|
||||
products = ifc_file.by_type("IfcProduct")
|
||||
|
||||
print(products[0].__class__)
|
||||
>>> <class 'ifcopenshell.entity_instance.entity_instance'>
|
||||
|
||||
print(products[0].Representation)
|
||||
>>> #423=IfcProductDefinitionShape($,$,(#409,#421))
|
||||
"""
|
||||
def __init__(self, e):
|
||||
if isinstance(e, str):
|
||||
e = ifcopenshell_wrapper.new_IfcBaseClass(e)
|
||||
super(entity_instance, self).__setattr__('wrapped_data', e)
|
||||
|
||||
def __getattr__(self, name):
|
||||
INVALID, FORWARD, INVERSE = range(3)
|
||||
attr_cat = self.wrapped_data.get_attribute_category(name)
|
||||
if attr_cat == FORWARD:
|
||||
return entity_instance.wrap_value(
|
||||
self.wrapped_data.get_argument(self.wrapped_data.get_argument_index(name)))
|
||||
elif attr_cat == INVERSE:
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_inverse(name))
|
||||
else:
|
||||
raise AttributeError(
|
||||
"entity instance of type '%s' has no attribute '%s'" % (self.wrapped_data.is_a(), name))
|
||||
|
||||
@staticmethod
|
||||
def walk(f, g, value):
|
||||
if isinstance(value, (tuple, list)):
|
||||
return tuple(map(functools.partial(entity_instance.walk, f, g), value))
|
||||
elif f(value):
|
||||
return g(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def wrap_value(v):
|
||||
def wrap(e): return entity_instance(e)
|
||||
|
||||
def is_instance(e): return isinstance(e, ifcopenshell_wrapper.entity_instance)
|
||||
|
||||
return entity_instance.walk(is_instance, wrap, v)
|
||||
|
||||
@staticmethod
|
||||
def unwrap_value(v):
|
||||
def unwrap(e): return e.wrapped_data
|
||||
|
||||
def is_instance(e): return isinstance(e, entity_instance)
|
||||
|
||||
return entity_instance.walk(is_instance, unwrap, v)
|
||||
|
||||
def attribute_type(self, attr):
|
||||
attr_idx = attr if isinstance(attr, numbers.Integral) else self.wrapped_data.get_argument_index(attr)
|
||||
return self.wrapped_data.get_argument_type(attr_idx)
|
||||
|
||||
def attribute_name(self, attr_idx):
|
||||
return self.wrapped_data.get_argument_name(attr_idx)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self[self.wrapped_data.get_argument_index(key)] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key < 0 or key >= len(self):
|
||||
raise IndexError("Attribute index {} out of range for instance of type {}".format(key, self.is_a()))
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_argument(key))
|
||||
|
||||
def __setitem__(self, idx, value):
|
||||
attr_type = real_attr_type = self.attribute_type(idx).title().replace(' ', '')
|
||||
real_attr_type = real_attr_type.replace('Derived', 'None')
|
||||
attr_type = attr_type.replace('Binary', 'String')
|
||||
attr_type = attr_type.replace('Enumeration', 'String')
|
||||
|
||||
if value is None:
|
||||
if attr_type != "Derived":
|
||||
self.wrapped_data.setArgumentAsNull(idx)
|
||||
else:
|
||||
valid = attr_type != "Derived"
|
||||
if valid:
|
||||
try:
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode("utf-8")
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
try:
|
||||
if attr_type != "Derived":
|
||||
getattr(self.wrapped_data, "setArgumentAs%s" % attr_type)(idx, entity_instance.unwrap_value(value))
|
||||
except BaseException as e:
|
||||
valid = False
|
||||
|
||||
if not valid:
|
||||
raise ValueError("Expected %s for attribute %s.%s, got %r" % (
|
||||
real_attr_type, self.is_a(), self.attribute_name(idx), value))
|
||||
|
||||
return value
|
||||
|
||||
def __len__(self):
|
||||
return len(self.wrapped_data)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.wrapped_data)
|
||||
|
||||
def is_a(self, *args):
|
||||
return self.wrapped_data.is_a(*args)
|
||||
|
||||
def id(self):
|
||||
return self.wrapped_data.id()
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(self, type(other)):
|
||||
return False
|
||||
return self.wrapped_data == other.wrapped_data
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.id(), self.wrapped_data.file_pointer()))
|
||||
|
||||
def __dir__(self):
|
||||
return sorted(set(itertools.chain(
|
||||
dir(type(self)),
|
||||
map(str, self.wrapped_data.get_attribute_names()),
|
||||
map(str, self.wrapped_data.get_inverse_attribute_names())
|
||||
)))
|
||||
|
||||
def get_info(self, include_identifier=True, recursive=False, return_type=dict, ignore=()):
|
||||
"""
|
||||
Return a dictionary of the entity_instance's properties (Python and IFC) and their values.
|
||||
|
||||
example:
|
||||
|
||||
ifc_file = ifcopenshell.open(file_path)
|
||||
products = ifc_file.by_type("IfcProduct")
|
||||
obj_info = products[0].get_info()
|
||||
print(obj_info.keys())
|
||||
|
||||
>>> dict_keys(['Description', 'Name', 'BuildingAddress', 'LongName', 'GlobalId', 'ObjectPlacement', 'OwnerHistory', 'ObjectType',
|
||||
>>> ...'ElevationOfTerrain', 'CompositionType', 'id', 'Representation', 'type', 'ElevationOfRefHeight'])
|
||||
"""
|
||||
def _():
|
||||
try:
|
||||
if include_identifier:
|
||||
yield "id", self.id()
|
||||
yield "type", self.is_a()
|
||||
except BaseException:
|
||||
logging.exception("unhandled exception while getting id / type info on {}".format(self))
|
||||
for i in range(len(self)):
|
||||
try:
|
||||
if self.wrapped_data.get_attribute_names()[i] in ignore:
|
||||
continue
|
||||
attr_value = self[i]
|
||||
if recursive:
|
||||
def is_instance(e): return isinstance(e, entity_instance)
|
||||
|
||||
def get_info_(inst):
|
||||
# for ty in ignore:
|
||||
# if inst.is_a(ty):
|
||||
# return None
|
||||
return entity_instance.get_info(inst,
|
||||
include_identifier=include_identifier,
|
||||
recursive=recursive,
|
||||
return_type=return_type,
|
||||
ignore=ignore
|
||||
)
|
||||
|
||||
attr_value = entity_instance.walk(is_instance, get_info_, attr_value)
|
||||
yield self.attribute_name(i), attr_value
|
||||
except BaseException:
|
||||
logging.exception("unhandled exception occurred setting attribute name for {}".format(self))
|
||||
|
||||
return return_type(_())
|
||||
|
||||
__dict__ = property(get_info)
|
||||
@@ -0,0 +1,127 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import numbers
|
||||
import functools
|
||||
|
||||
from . import ifcopenshell_wrapper
|
||||
from .entity_instance import entity_instance
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
basestring
|
||||
except NameError:
|
||||
# Python 3 or newer
|
||||
basestring = (str, bytes)
|
||||
|
||||
|
||||
class file(object):
|
||||
"""
|
||||
Base class for containing IFC files.
|
||||
|
||||
Class has instance methods for filtering by element Id, Type, etc.
|
||||
|
||||
Instantiated objects can be subscripted by Id or Guid
|
||||
|
||||
example:
|
||||
|
||||
ifc_file = ifcopenshell.open(file_path)
|
||||
products = ifc_file.by_type("IfcProduct")
|
||||
|
||||
print(products[0].id(), products[0].GlobalId)
|
||||
>>> 122 2XQ$n5SLP5MBLyL442paFx
|
||||
|
||||
# Subscripting
|
||||
print(products[0] == ifc_file[122] == ifc_file['2XQ$n5SLP5MBLyL442paFx'])
|
||||
>>> True
|
||||
"""
|
||||
def __init__(self, f=None):
|
||||
self.wrapped_data = f or ifcopenshell_wrapper.file()
|
||||
|
||||
def create_entity(self, type, *args, **kwargs):
|
||||
e = entity_instance(type)
|
||||
self.wrapped_data.add(e.wrapped_data)
|
||||
e.wrapped_data.this.disown()
|
||||
attrs = list(enumerate(args)) + \
|
||||
[(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
|
||||
for idx, arg in attrs:
|
||||
e[idx] = arg
|
||||
return e
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr[0:6] == 'create':
|
||||
return functools.partial(self.create_entity, attr[6:])
|
||||
else:
|
||||
return getattr(self.wrapped_data, attr)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, numbers.Integral):
|
||||
return entity_instance(self.wrapped_data.by_id(key))
|
||||
elif isinstance(key, basestring):
|
||||
return entity_instance(self.wrapped_data.by_guid(str(key)))
|
||||
|
||||
def by_id(self, id):
|
||||
"""
|
||||
Return IFC objects filtered by IFC ID.
|
||||
|
||||
Returned objects are unwrapped IFC objects.
|
||||
"""
|
||||
return self[id]
|
||||
|
||||
def by_guid(self, guid):
|
||||
"""
|
||||
Return IFC objects filtered by IFC GUID.
|
||||
|
||||
Returned objects are unwrapped IFC objects.
|
||||
"""
|
||||
return self[guid]
|
||||
|
||||
def add(self, inst):
|
||||
inst.wrapped_data.this.disown()
|
||||
return entity_instance(self.wrapped_data.add(inst.wrapped_data))
|
||||
|
||||
def by_type(self, type):
|
||||
"""
|
||||
Return IFC objects filtered by IFC Type and wrapped with the entity_instance class.
|
||||
|
||||
See ifcopenshell.entity_instance for class methods and properties
|
||||
"""
|
||||
return [entity_instance(e) for e in self.wrapped_data.by_type(type)]
|
||||
|
||||
def traverse(self, inst, max_levels=None):
|
||||
if max_levels is None:
|
||||
max_levels = -1
|
||||
return [entity_instance(e) for e in self.wrapped_data.traverse(inst.wrapped_data, max_levels)]
|
||||
|
||||
def get_inverse(self, inst):
|
||||
return [entity_instance(e) for e in self.wrapped_data.get_inverse(inst.wrapped_data)]
|
||||
|
||||
def remove(self, inst):
|
||||
return self.wrapped_data.remove(inst.wrapped_data)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self[id] for id in self.wrapped_data.entity_names())
|
||||
|
||||
@staticmethod
|
||||
def from_string(s):
|
||||
return file(ifcopenshell_wrapper.read(s))
|
||||
@@ -17,66 +17,9 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .. import ifcopenshell_wrapper
|
||||
|
||||
def has_occ():
|
||||
try: import OCC.BRepTools
|
||||
except: return False
|
||||
return True
|
||||
|
||||
|
||||
has_occ = has_occ()
|
||||
wrap_shape_creation = lambda settings, shape: shape
|
||||
if has_occ:
|
||||
import occ_utils as utils
|
||||
wrap_shape_creation = lambda settings, shape: utils.create_shape_from_serialization(shape) if getattr(settings, 'use_python_opencascade', False) else shape
|
||||
|
||||
|
||||
# Subclass the settings module to provide an additional
|
||||
# setting to enable pythonOCC when available
|
||||
class settings(ifcopenshell_wrapper.settings):
|
||||
if has_occ:
|
||||
USE_PYTHON_OPENCASCADE = -1
|
||||
def set(self, *args):
|
||||
setting, value = args
|
||||
if setting == settings.USE_PYTHON_OPENCASCADE:
|
||||
self.set(settings.USE_BREP_DATA, value)
|
||||
self.set(settings.USE_WORLD_COORDS, value)
|
||||
self.set(settings.DISABLE_TRIANGULATION, value)
|
||||
self.use_python_opencascade = value
|
||||
else:
|
||||
ifcopenshell_wrapper.settings.set(self, *args)
|
||||
|
||||
|
||||
# Hide templating precision to the user by choosing based on Python's
|
||||
# internal float type. This is probably always going to be a double.
|
||||
for ty in (ifcopenshell_wrapper.iterator_single_precision, ifcopenshell_wrapper.iterator_double_precision):
|
||||
if ty.mantissa_size() == sys.float_info.mant_dig:
|
||||
_iterator = ty
|
||||
|
||||
|
||||
# Make sure people are able to use python's platform agnostic paths
|
||||
class iterator(_iterator):
|
||||
def __init__(self, settings, filename):
|
||||
self.settings = settings
|
||||
_iterator.__init__(self, settings, os.path.abspath(filename))
|
||||
if has_occ:
|
||||
def get(self):
|
||||
return wrap_shape_creation(self.settings, _iterator.get(self))
|
||||
|
||||
|
||||
def create_shape(settings, inst):
|
||||
return wrap_shape_creation(settings, ifcopenshell_wrapper.create_shape(settings, inst.wrapped_data))
|
||||
|
||||
|
||||
def iterate(settings, filename):
|
||||
it = iterator(settings, filename)
|
||||
if it.findContext():
|
||||
while True:
|
||||
yield it.get()
|
||||
if not it.next(): break
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
from . import occ_utils as utils
|
||||
from .main import *
|
||||
|
||||
@@ -0,0 +1,611 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import operator
|
||||
import functools
|
||||
|
||||
import OCC.AIS
|
||||
|
||||
from collections import defaultdict, Iterable, OrderedDict
|
||||
|
||||
try:
|
||||
QString = unicode
|
||||
except NameError:
|
||||
# Python 3
|
||||
QString = str
|
||||
|
||||
os.environ['QT_API'] = 'pyqt5'
|
||||
try:
|
||||
from pyqode.qt import QtCore
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from .code_editor_pane import code_edit
|
||||
|
||||
try:
|
||||
from OCC.Display.pyqt5Display import qtViewer3d
|
||||
except BaseException:
|
||||
import OCC.Display
|
||||
|
||||
try:
|
||||
import OCC.Display.backend
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
try:
|
||||
OCC.Display.backend.get_backend("qt-pyqt5")
|
||||
except BaseException:
|
||||
OCC.Display.backend.load_backend("qt-pyqt5")
|
||||
|
||||
from OCC.Display.qtDisplay import qtViewer3d
|
||||
|
||||
from .main import settings, iterator
|
||||
from .occ_utils import display_shape
|
||||
|
||||
from .. import open as open_ifc_file
|
||||
from .. import version as ifcopenshell_version
|
||||
|
||||
if ifcopenshell_version < "0.6":
|
||||
# not yet ported
|
||||
from .. import get_supertype
|
||||
|
||||
class configuration(object):
|
||||
def __init__(self):
|
||||
try:
|
||||
import ConfigParser
|
||||
Cfg = ConfigParser.RawConfigParser
|
||||
except BaseException:
|
||||
import configparser
|
||||
|
||||
def Cfg():
|
||||
return configparser.ConfigParser(interpolation=None)
|
||||
|
||||
conf_file = os.path.expanduser(os.path.join("~", ".ifcopenshell", "app", "snippets.conf"))
|
||||
if conf_file.startswith("~"):
|
||||
conf_file = None
|
||||
return
|
||||
|
||||
self.config_encode = lambda s: s.replace("\\", "\\\\").replace("\n", "\n|")
|
||||
self.config_decode = lambda s: s.replace("\n|", "\n").replace("\\\\", "\\")
|
||||
|
||||
if not os.path.exists(os.path.dirname(conf_file)):
|
||||
os.makedirs(os.path.dirname(conf_file))
|
||||
|
||||
if not os.path.exists(conf_file):
|
||||
config = Cfg()
|
||||
config.add_section("snippets")
|
||||
config.set("snippets", "print all wall ids", self.config_encode("""
|
||||
###########################################################################
|
||||
# A simple script that iterates over all walls in the current model #
|
||||
# and prints their Globally unique IDs (GUIDS) to the console window #
|
||||
###########################################################################
|
||||
|
||||
for wall in model.by_type("IfcWall"):
|
||||
print ("wall with global id: "+str(wall.GlobalId))
|
||||
""".lstrip()))
|
||||
|
||||
config.set("snippets", "print properties of current selection", self.config_encode("""
|
||||
###########################################################################
|
||||
# A simple script that iterates over all IfcPropertySets of the currently #
|
||||
# selected object and prints them to the console #
|
||||
###########################################################################
|
||||
|
||||
# check if something is selected
|
||||
if selection:
|
||||
#get the IfcProduct that is stored in the global variable 'selection'
|
||||
obj = selection
|
||||
for relDefinesByProperties in obj.IsDefinedBy:
|
||||
print("[{0}]".format(relDefinesByProperties.RelatingPropertyDefinition.Name))
|
||||
for prop in relDefinesByProperties.RelatingPropertyDefinition.HasProperties:
|
||||
print ("{:<20} :{}".format(prop.Name,prop.NominalValue.wrappedValue))
|
||||
print ("\\n")
|
||||
""".lstrip()))
|
||||
with open(conf_file, 'w') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
self.config = Cfg()
|
||||
self.config.read(conf_file)
|
||||
|
||||
def options(self, s):
|
||||
return OrderedDict([(k, self.config_decode(self.config.get(s, k))) for k in self.config.options(s)])
|
||||
|
||||
|
||||
class application(QtWidgets.QApplication):
|
||||
"""A pythonOCC, PyQt based IfcOpenShell application
|
||||
with two tree views and a graphical 3d view"""
|
||||
|
||||
class abstract_treeview(QtWidgets.QTreeWidget):
|
||||
|
||||
"""Base class for the two treeview controls"""
|
||||
|
||||
instanceSelected = QtCore.pyqtSignal([object])
|
||||
instanceVisibilityChanged = QtCore.pyqtSignal([object, int])
|
||||
instanceDisplayModeChanged = QtCore.pyqtSignal([object, int])
|
||||
|
||||
def __init__(self):
|
||||
QtWidgets.QTreeView.__init__(self)
|
||||
self.setColumnCount(len(self.ATTRIBUTES))
|
||||
self.setHeaderLabels(self.ATTRIBUTES)
|
||||
self.children = defaultdict(list)
|
||||
|
||||
def get_children(self, inst):
|
||||
c = [inst]
|
||||
i = 0
|
||||
while i < len(c):
|
||||
c.extend(self.children[c[i]])
|
||||
i += 1
|
||||
return c
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
menu = QtWidgets.QMenu(self)
|
||||
visibility = [menu.addAction("Show"), menu.addAction("Hide")]
|
||||
displaymode = [menu.addAction("Solid"), menu.addAction("Wireframe")]
|
||||
action = menu.exec_(self.mapToGlobal(event.pos()))
|
||||
index = self.selectionModel().currentIndex()
|
||||
inst = index.data(QtCore.Qt.UserRole)
|
||||
if hasattr(inst, 'toPyObject'):
|
||||
inst = inst
|
||||
if action in visibility:
|
||||
self.instanceVisibilityChanged.emit(inst, visibility.index(action))
|
||||
elif action in displaymode:
|
||||
self.instanceDisplayModeChanged.emit(inst, displaymode.index(action))
|
||||
|
||||
def clicked_(self, index):
|
||||
inst = index.data(QtCore.Qt.UserRole)
|
||||
if hasattr(inst, 'toPyObject'):
|
||||
inst = inst
|
||||
if inst:
|
||||
self.instanceSelected.emit(inst)
|
||||
|
||||
def select(self, product):
|
||||
itm = self.product_to_item.get(product)
|
||||
if itm is None:
|
||||
return
|
||||
self.selectionModel().setCurrentIndex(itm,
|
||||
QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.Rows)
|
||||
|
||||
class decomposition_treeview(abstract_treeview):
|
||||
|
||||
"""Treeview with typical IFC decomposition relationships"""
|
||||
|
||||
ATTRIBUTES = ['Entity', 'GlobalId', 'Name']
|
||||
|
||||
def parent(self, instance):
|
||||
if instance.is_a("IfcOpeningElement"):
|
||||
return instance.VoidsElements[0].RelatingBuildingElement
|
||||
if instance.is_a("IfcElement"):
|
||||
fills = instance.FillsVoids
|
||||
if len(fills):
|
||||
return fills[0].RelatingOpeningElement
|
||||
containments = instance.ContainedInStructure
|
||||
if len(containments):
|
||||
return containments[0].RelatingStructure
|
||||
if instance.is_a("IfcObjectDefinition"):
|
||||
decompositions = instance.Decomposes
|
||||
if len(decompositions):
|
||||
return decompositions[0].RelatingObject
|
||||
|
||||
def load_file(self, f, **kwargs):
|
||||
products = list(f.by_type("IfcProduct")) + list(f.by_type("IfcProject"))
|
||||
parents = list(map(self.parent, products))
|
||||
items = {}
|
||||
skipped = 0
|
||||
ATTRS = self.ATTRIBUTES
|
||||
while len(items) + skipped < len(products):
|
||||
for product, parent in zip(products, parents):
|
||||
if parent is None and not product.is_a("IfcProject"):
|
||||
skipped += 1
|
||||
continue
|
||||
if (parent is None or parent in items) and product not in items:
|
||||
sl = []
|
||||
for attr in ATTRS:
|
||||
if attr == 'Entity':
|
||||
sl.append(product.is_a())
|
||||
else:
|
||||
sl.append(getattr(product, attr) or '')
|
||||
itm = items[product] = QtWidgets.QTreeWidgetItem(items.get(parent, self), sl)
|
||||
itm.setData(0, QtCore.Qt.UserRole, product)
|
||||
self.children[parent].append(product)
|
||||
self.product_to_item = dict(zip(items.keys(), map(self.indexFromItem, items.values())))
|
||||
self.clicked.connect(self.clicked_)
|
||||
self.expandAll()
|
||||
|
||||
class type_treeview(abstract_treeview):
|
||||
|
||||
"""Treeview with typical IFC decomposition relationships"""
|
||||
|
||||
ATTRIBUTES = ['Name']
|
||||
|
||||
def load_file(self, f, **kwargs):
|
||||
products = list(f.by_type("IfcProduct"))
|
||||
types = set(map(lambda i: i.is_a(), products))
|
||||
items = {}
|
||||
for t in types:
|
||||
def add(t):
|
||||
s = get_supertype(t)
|
||||
if s:
|
||||
add(s)
|
||||
s2, t2 = map(QString, (s, t))
|
||||
if t2 not in items:
|
||||
itm = items[t2] = QtWidgets.QTreeWidgetItem(items.get(s2, self), [t2])
|
||||
itm.setData(0, QtCore.Qt.UserRole, t2)
|
||||
self.children[s2].append(t2)
|
||||
|
||||
if ifcopenshell_version < "0.6":
|
||||
add(t)
|
||||
|
||||
for p in products:
|
||||
t = QString(p.is_a())
|
||||
itm = items[p] = QtWidgets.QTreeWidgetItem(items.get(t, self), [p.Name or '<no name>'])
|
||||
itm.setData(0, QtCore.Qt.UserRole, t)
|
||||
self.children[t].append(p)
|
||||
|
||||
self.product_to_item = dict(zip(items.keys(), map(self.indexFromItem, items.values())))
|
||||
self.clicked.connect(self.clicked)
|
||||
self.expandAll()
|
||||
|
||||
class property_table(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self):
|
||||
QtWidgets.QWidget.__init__(self)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.setLayout(self.layout)
|
||||
self.scroll = QtWidgets.QScrollArea(self)
|
||||
self.layout.addWidget(self.scroll)
|
||||
self.scroll.setWidgetResizable(True)
|
||||
self.scrollContent = QtWidgets.QWidget(self.scroll)
|
||||
self.scrollLayout = QtWidgets.QVBoxLayout(self.scrollContent)
|
||||
self.scrollContent.setLayout(self.scrollLayout)
|
||||
self.scroll.setWidget(self.scrollContent)
|
||||
self.prop_dict = {}
|
||||
|
||||
# triggered by selection event in either component of parent
|
||||
def select(self, product):
|
||||
|
||||
# Clear the old contents if any
|
||||
while self.scrollLayout.count():
|
||||
child = self.scrollLayout.takeAt(0)
|
||||
if child is not None:
|
||||
if child.widget() is not None:
|
||||
child.widget().deleteLater()
|
||||
|
||||
self.scroll = QtWidgets.QScrollArea()
|
||||
self.scroll.setWidgetResizable(True)
|
||||
|
||||
prop_sets = self.prop_dict.get(str(product))
|
||||
|
||||
if prop_sets is not None:
|
||||
for k, v in prop_sets:
|
||||
group_box = QtWidgets.QGroupBox()
|
||||
|
||||
group_box.setTitle(k)
|
||||
group_layout = QtWidgets.QVBoxLayout()
|
||||
group_box.setLayout(group_layout)
|
||||
|
||||
for name, value in v.items():
|
||||
prop_name = str(name)
|
||||
|
||||
value_str = value
|
||||
if hasattr(value_str, "wrappedValue"):
|
||||
value_str = value_str.wrappedValue
|
||||
|
||||
if isinstance(value_str, unicode):
|
||||
value_str = value_str.encode('utf-8')
|
||||
else:
|
||||
value_str = str(value_str)
|
||||
|
||||
if hasattr(value, "is_a"):
|
||||
type_str = " <i>(%s)</i>" % value.is_a()
|
||||
else:
|
||||
type_str = ""
|
||||
label = QtWidgets.QLabel("<b>%s</b>: %s%s" % (prop_name, value_str, type_str))
|
||||
group_layout.addWidget(label)
|
||||
|
||||
group_layout.addStretch()
|
||||
self.scrollLayout.addWidget(group_box)
|
||||
|
||||
self.scrollLayout.addStretch()
|
||||
else:
|
||||
label = QtWidgets.QLabel("No IfcPropertySets asscociated with selected entity instance")
|
||||
self.scrollLayout.addWidget(label)
|
||||
|
||||
def load_file(self, f, **kwargs):
|
||||
for p in f.by_type("IfcProduct"):
|
||||
propsets = []
|
||||
|
||||
def process_pset(prop_def):
|
||||
if prop_def is not None:
|
||||
prop_set_name = prop_def.Name
|
||||
props = {}
|
||||
if prop_def.is_a("IfcElementQuantity"):
|
||||
for q in prop_def.Quantities:
|
||||
if q.is_a("IfcPhysicalSimpleQuantity"):
|
||||
props[q.Name] = q[3]
|
||||
elif prop_def.is_a("IfcPropertySet"):
|
||||
for prop in prop_def.HasProperties:
|
||||
if prop.is_a("IfcPropertySingleValue"):
|
||||
props[prop.Name] = prop.NominalValue
|
||||
else:
|
||||
# Entity introduced in IFC4
|
||||
# prop_def.is_a("IfcPreDefinedPropertySet"):
|
||||
for prop in range(4, len(prop_def)):
|
||||
props[prop_def.attribute_name(prop)] = prop_def[prop]
|
||||
return prop_set_name, props
|
||||
|
||||
try:
|
||||
for is_def_by in p.IsDefinedBy:
|
||||
if is_def_by.is_a("IfcRelDefinesByProperties"):
|
||||
propsets.append(process_pset(is_def_by.RelatingPropertyDefinition))
|
||||
elif is_def_by.is_a("IfcRelDefinesByType"):
|
||||
type_psets = is_def_by.RelatingType.HasPropertySets
|
||||
if type_psets is None:
|
||||
continue
|
||||
for propset in type_psets:
|
||||
propsets.append(process_pset(propset))
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print("failed to load properties: {}".format(e))
|
||||
traceback.print_exc()
|
||||
|
||||
if len(propsets):
|
||||
self.prop_dict[str(p)] = propsets
|
||||
|
||||
print("property set dictionary has {} entries".format(len(self.prop_dict)))
|
||||
|
||||
class viewer(qtViewer3d):
|
||||
|
||||
instanceSelected = QtCore.pyqtSignal([object])
|
||||
|
||||
@staticmethod
|
||||
def ais_to_key(ais_handle):
|
||||
def yield_shapes():
|
||||
ais = ais_handle.GetObject()
|
||||
if hasattr(ais, 'Shape'):
|
||||
yield ais.Shape()
|
||||
return
|
||||
shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle)
|
||||
if not shp.IsNull():
|
||||
yield shp.Shape()
|
||||
return
|
||||
mult = ais_handle
|
||||
if mult.IsNull():
|
||||
shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle)
|
||||
if not shp.IsNull():
|
||||
yield shp
|
||||
else:
|
||||
li = mult.GetObject().ConnectedTo()
|
||||
for i in range(li.Length()):
|
||||
shp = OCC.AIS.Handle_AIS_Shape.DownCast(li.Value(i + 1))
|
||||
if not shp.IsNull():
|
||||
yield shp
|
||||
|
||||
return tuple(shp.HashCode(1 << 24) for shp in yield_shapes())
|
||||
|
||||
def __init__(self, widget):
|
||||
qtViewer3d.__init__(self, widget)
|
||||
self.ais_to_product = {}
|
||||
self.product_to_ais = {}
|
||||
self.counter = 0
|
||||
self.window = widget
|
||||
|
||||
def initialize(self):
|
||||
self.InitDriver()
|
||||
self._display.Select = self.HandleSelection
|
||||
|
||||
def load_file(self, f, setting=None):
|
||||
|
||||
if setting is None:
|
||||
setting = settings()
|
||||
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
|
||||
|
||||
v = self._display
|
||||
|
||||
t = {0: time.time()}
|
||||
|
||||
def update(dt=None):
|
||||
t1 = time.time()
|
||||
if t1 - t[0] > (dt or -1):
|
||||
v.FitAll()
|
||||
v.Repaint()
|
||||
t[0] = t1
|
||||
|
||||
terminate = [False]
|
||||
self.window.window_closed.connect(lambda *args: operator.setitem(terminate, 0, True))
|
||||
|
||||
t0 = time.time()
|
||||
|
||||
it = iterator(setting, f)
|
||||
if not it.initialize():
|
||||
return
|
||||
|
||||
old_progress = -1
|
||||
while True:
|
||||
if terminate[0]:
|
||||
break
|
||||
shape = it.get()
|
||||
product = f[shape.data.id]
|
||||
ais = display_shape(shape, viewer_handle=v)
|
||||
ais.GetObject().SetSelectionPriority(self.counter)
|
||||
self.ais_to_product[self.counter] = product
|
||||
self.product_to_ais[product] = ais
|
||||
self.counter += 1
|
||||
QtWidgets.QApplication.processEvents()
|
||||
if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
|
||||
v.Context.Erase(ais, True)
|
||||
progress = it.progress() // 2
|
||||
if progress > old_progress:
|
||||
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
|
||||
old_progress = progress
|
||||
if not it.next():
|
||||
break
|
||||
update(0.2)
|
||||
|
||||
print("\rOpened file in %.2f seconds%s" % (time.time() - t0, " " * 25))
|
||||
|
||||
update()
|
||||
|
||||
def select(self, product):
|
||||
ais = self.product_to_ais.get(product)
|
||||
if ais is None:
|
||||
return
|
||||
v = self._display.Context
|
||||
v.ClearSelected(False)
|
||||
v.SetSelected(ais, True)
|
||||
|
||||
def toggle(self, product_or_products, fn):
|
||||
if not isinstance(product_or_products, Iterable):
|
||||
product_or_products = [product_or_products]
|
||||
aiss = list(filter(None, map(self.product_to_ais.get, product_or_products)))
|
||||
last = len(aiss) - 1
|
||||
for i, ais in enumerate(aiss):
|
||||
fn(ais, i == last)
|
||||
|
||||
def toggle_visibility(self, product_or_products, flag):
|
||||
v = self._display.Context
|
||||
if flag:
|
||||
def visibility(ais, last):
|
||||
v.Erase(ais, last)
|
||||
else:
|
||||
def visibility(ais, last):
|
||||
v.Display(ais, last)
|
||||
self.toggle(product_or_products, visibility)
|
||||
|
||||
def toggle_wireframe(self, product_or_products, flag):
|
||||
v = self._display.Context
|
||||
if flag:
|
||||
def wireframe(ais, last):
|
||||
if v.IsDisplayed(ais):
|
||||
v.SetDisplayMode(ais, 0, last)
|
||||
else:
|
||||
def wireframe(ais, last):
|
||||
if v.IsDisplayed(ais):
|
||||
v.SetDisplayMode(ais, 1, last)
|
||||
self.toggle(product_or_products, wireframe)
|
||||
|
||||
def HandleSelection(self, X, Y):
|
||||
v = self._display.Context
|
||||
v.Select()
|
||||
v.InitSelected()
|
||||
if v.MoreSelected():
|
||||
ais = v.SelectedInteractive()
|
||||
inst = self.ais_to_product[ais.GetObject().SelectionPriority()]
|
||||
self.instanceSelected.emit(inst)
|
||||
|
||||
class window(QtWidgets.QMainWindow):
|
||||
|
||||
TITLE = "IfcOpenShell IFC viewer"
|
||||
|
||||
window_closed = QtCore.pyqtSignal([])
|
||||
|
||||
def __init__(self):
|
||||
QtWidgets.QMainWindow.__init__(self)
|
||||
self.setWindowTitle(self.TITLE)
|
||||
self.menu = self.menuBar()
|
||||
self.menus = {}
|
||||
|
||||
def closeEvent(self, *args):
|
||||
self.window_closed.emit()
|
||||
|
||||
def add_menu_item(self, menu, label, callback, icon=None, shortcut=None):
|
||||
m = self.menus.get(menu)
|
||||
if m is None:
|
||||
m = self.menu.addMenu(menu)
|
||||
self.menus[menu] = m
|
||||
|
||||
if icon:
|
||||
a = QtWidgets.QAction(QtGui.QIcon(icon), label, self)
|
||||
else:
|
||||
a = QtWidgets.QAction(label, self)
|
||||
|
||||
if shortcut:
|
||||
a.setShortcut(shortcut)
|
||||
|
||||
a.triggered.connect(callback)
|
||||
m.addAction(a)
|
||||
|
||||
def makeSelectionHandler(self, component):
|
||||
def handler(inst):
|
||||
for c in self.components:
|
||||
if c != component:
|
||||
c.select(inst)
|
||||
|
||||
return handler
|
||||
|
||||
def __init__(self, settings=None):
|
||||
QtWidgets.QApplication.__init__(self, sys.argv)
|
||||
self.window = application.window()
|
||||
self.tree = application.decomposition_treeview()
|
||||
self.tree2 = application.type_treeview()
|
||||
self.propview = self.property_table()
|
||||
self.canvas = application.viewer(self.window)
|
||||
self.tabs = QtWidgets.QTabWidget()
|
||||
self.window.resize(800, 600)
|
||||
splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
|
||||
splitter.addWidget(self.tabs)
|
||||
self.tabs.addTab(self.tree, 'Decomposition')
|
||||
self.tabs.addTab(self.tree2, 'Types')
|
||||
self.tabs.addTab(self.propview, "Properties")
|
||||
splitter2 = QtWidgets.QSplitter(QtCore.Qt.Vertical)
|
||||
splitter2.addWidget(self.canvas)
|
||||
self.editor = code_edit(self.canvas, configuration().options('snippets'))
|
||||
splitter2.addWidget(self.editor)
|
||||
splitter.addWidget(splitter2)
|
||||
splitter.setSizes([200, 600])
|
||||
splitter2.setSizes([400, 200])
|
||||
self.window.setCentralWidget(splitter)
|
||||
self.canvas.initialize()
|
||||
self.components = [self.tree, self.tree2, self.canvas, self.propview, self.editor]
|
||||
self.files = {}
|
||||
|
||||
self.window.add_menu_item('File', '&Open', self.browse, shortcut='CTRL+O')
|
||||
self.window.add_menu_item('File', '&Close', self.clear, shortcut='CTRL+W')
|
||||
self.window.add_menu_item('File', '&Exit', self.window.close, shortcut='ALT+F4')
|
||||
|
||||
self.tree.instanceSelected.connect(self.makeSelectionHandler(self.tree))
|
||||
self.tree2.instanceSelected.connect(self.makeSelectionHandler(self.tree2))
|
||||
self.canvas.instanceSelected.connect(self.makeSelectionHandler(self.canvas))
|
||||
for t in [self.tree, self.tree2]:
|
||||
t.instanceVisibilityChanged.connect(functools.partial(self.change_visibility, t))
|
||||
t.instanceDisplayModeChanged.connect(functools.partial(self.change_displaymode, t))
|
||||
|
||||
self.settings = settings
|
||||
|
||||
def change_visibility(self, tree, inst, flag):
|
||||
insts = tree.get_children(inst)
|
||||
self.canvas.toggle_visibility(insts, flag)
|
||||
|
||||
def change_displaymode(self, tree, inst, flag):
|
||||
insts = tree.get_children(inst)
|
||||
self.canvas.toggle_wireframe(insts, flag)
|
||||
|
||||
def start(self):
|
||||
self.window.show()
|
||||
sys.exit(self.exec_())
|
||||
|
||||
def browse(self):
|
||||
filename = QtWidgets.QFileDialog.getOpenFileName(self.window, 'Open file', ".",
|
||||
"Industry Foundation Classes (*.ifc)")[0]
|
||||
self.load(filename)
|
||||
|
||||
def clear(self):
|
||||
self.canvas._display.Context.RemoveAll()
|
||||
self.tree.clear()
|
||||
self.files.clear()
|
||||
|
||||
def load(self, fn):
|
||||
if fn in self.files:
|
||||
return
|
||||
f = open_ifc_file(str(fn))
|
||||
self.files[fn] = f
|
||||
for c in self.components:
|
||||
c.load_file(f, setting=self.settings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
application().start()
|
||||
@@ -0,0 +1,149 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from code import InteractiveConsole
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
try:
|
||||
from PyQt5 import QtWidgets
|
||||
except BaseException:
|
||||
QtWidgets = QtGui
|
||||
|
||||
try:
|
||||
from pyqode.core.panels import CheckerPanel
|
||||
from pyqode.core import api
|
||||
from pyqode.core import modes
|
||||
from pyqode.core import panels
|
||||
from pyqode.core.api import CodeEdit, ColorScheme
|
||||
from pyqode.python.modes import PyAutoIndentMode, PythonSH
|
||||
from pyqode.python.backend import server
|
||||
from pyqode.python import modes as pymodes, panels as pypanels, widgets
|
||||
from pyqode.python.widgets import PyInteractiveConsole
|
||||
|
||||
has_pyqode = True
|
||||
except BaseException:
|
||||
has_pyqode = False
|
||||
CodeEdit = QtWidgets.QPlainTextEdit
|
||||
|
||||
|
||||
class StdoutRedirector(object):
|
||||
"""A class for redirecting stdout to this Text widget."""
|
||||
|
||||
def __init__(self, widget):
|
||||
self.widget = widget
|
||||
self.isError = False
|
||||
|
||||
def write(self, myStr):
|
||||
self.widget.moveCursor(QtGui.QTextCursor.End)
|
||||
if self.isError:
|
||||
self.widget.setTextColor(QtCore.Qt.red)
|
||||
else:
|
||||
self.widget.setTextColor(QtCore.Qt.white)
|
||||
self.widget.insertPlainText(myStr)
|
||||
self.widget.moveCursor(QtGui.QTextCursor.End)
|
||||
|
||||
|
||||
class code_edit(QtWidgets.QWidget):
|
||||
class Console(InteractiveConsole):
|
||||
def __init__(*args):
|
||||
InteractiveConsole.__init__(*args)
|
||||
|
||||
def enter(self, source):
|
||||
self.runcode(source)
|
||||
|
||||
def runCode(self):
|
||||
sys.stdout = StdoutRedirector(self.output)
|
||||
sys.stderr = StdoutRedirector(self.output)
|
||||
sys.stderr.isError = True
|
||||
|
||||
if not self.model:
|
||||
print("please load a model first", file=sys.stderr)
|
||||
else:
|
||||
self.c.enter(str(self.editor.toPlainText()))
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
|
||||
def select(self, product):
|
||||
self.c = self.Console({'model': self.model, 'viewer': self.viewer, 'selection': product})
|
||||
|
||||
def __init__(self, viewer, snippets=None):
|
||||
self.model = None
|
||||
self.viewer = viewer
|
||||
QtWidgets.QWidget.__init__(self)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.setLayout(self.layout)
|
||||
self.c = None
|
||||
self.tools = QtWidgets.QHBoxLayout(self)
|
||||
self.layout.addLayout(self.tools)
|
||||
self.runbutton = QtWidgets.QPushButton("Run")
|
||||
width = self.runbutton.fontMetrics().boundingRect("Run").width() + 20
|
||||
self.runbutton.setMaximumWidth(width)
|
||||
self.tools.addWidget(self.runbutton)
|
||||
self.runbutton.clicked.connect(self.runCode)
|
||||
|
||||
editor = CodeEdit()
|
||||
if has_pyqode:
|
||||
editor.backend.start(server.__file__)
|
||||
editor.panels.append(panels.FoldingPanel())
|
||||
editor.panels.append(panels.LineNumberPanel())
|
||||
editor.panels.append(panels.SearchAndReplacePanel(),
|
||||
panels.SearchAndReplacePanel.Position.BOTTOM)
|
||||
editor.panels.append(panels.EncodingPanel(), api.Panel.Position.TOP)
|
||||
editor.add_separator()
|
||||
editor.panels.append(pypanels.QuickDocPanel(), api.Panel.Position.BOTTOM)
|
||||
sh = editor.modes.append(PythonSH(editor.document()))
|
||||
editor.modes.append(modes.CaretLineHighlighterMode())
|
||||
editor.modes.append(modes.CodeCompletionMode())
|
||||
editor.modes.append(modes.ExtendedSelectionMode())
|
||||
editor.modes.append(modes.FileWatcherMode())
|
||||
editor.modes.append(modes.OccurrencesHighlighterMode())
|
||||
editor.modes.append(modes.RightMarginMode())
|
||||
editor.modes.append(modes.SmartBackSpaceMode())
|
||||
editor.modes.append(modes.SymbolMatcherMode())
|
||||
editor.modes.append(modes.ZoomMode())
|
||||
editor.modes.append(pymodes.CommentsMode())
|
||||
editor.modes.append(pymodes.CalltipsMode())
|
||||
auto = pymodes.PyAutoCompleteMode()
|
||||
auto.logger.setLevel(logging.CRITICAL)
|
||||
editor.modes.append(auto)
|
||||
editor.modes.append(pymodes.PyAutoIndentMode())
|
||||
editor.modes.append(pymodes.PyIndenterMode())
|
||||
editor.show()
|
||||
else:
|
||||
editor.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier;')
|
||||
|
||||
self.editor = editor
|
||||
self.snippets = snippets
|
||||
if self.snippets:
|
||||
self.list = QtWidgets.QComboBox(self)
|
||||
self.replace_snippet(0)
|
||||
for snip_name in self.snippets.keys():
|
||||
self.list.addItem(snip_name)
|
||||
self.tools.addWidget(self.list)
|
||||
self.list.currentIndexChanged[int].connect(self.replace_snippet)
|
||||
|
||||
self.layout.addWidget(self.editor)
|
||||
self.output = QtWidgets.QTextEdit()
|
||||
self.output.setReadOnly(True)
|
||||
self.output.setStyleSheet('font-size: 10pt; font-family: Consolas, Courier; background-color: #444;')
|
||||
self.layout.addWidget(self.output)
|
||||
|
||||
def replace_snippet(self, number=None):
|
||||
snip = list(self.snippets.values())[number]
|
||||
if has_pyqode:
|
||||
self.editor.setPlainText(snip, "", "")
|
||||
else:
|
||||
self.editor.setPlainText(snip)
|
||||
|
||||
def load_file(self, f, **kwargs):
|
||||
output = []
|
||||
sys.stdout = StdoutRedirector(self.output)
|
||||
self.model = f
|
||||
self.c = self.Console({'model': self.model, 'selection': None, 'viewer': self.viewer})
|
||||
sys.stdout = sys.__stdout__
|
||||
@@ -0,0 +1,199 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .. import ifcopenshell_wrapper
|
||||
from ..file import file
|
||||
from ..entity_instance import entity_instance
|
||||
|
||||
|
||||
def has_occ():
|
||||
try:
|
||||
import OCC.BRepTools
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
has_occ = has_occ()
|
||||
|
||||
|
||||
def wrap_shape_creation(settings, shape):
|
||||
return shape
|
||||
|
||||
|
||||
if has_occ:
|
||||
from . import occ_utils as utils
|
||||
|
||||
def wrap_shape_creation(settings, shape): return utils.create_shape_from_serialization(shape) if getattr(settings,
|
||||
'use_python_opencascade',
|
||||
False) else shape
|
||||
|
||||
|
||||
# Subclass the settings module to provide an additional
|
||||
# setting to enable pythonOCC when available
|
||||
|
||||
|
||||
class settings(ifcopenshell_wrapper.settings):
|
||||
if has_occ:
|
||||
USE_PYTHON_OPENCASCADE = -1
|
||||
|
||||
def set(self, *args):
|
||||
setting, value = args
|
||||
if setting == settings.USE_PYTHON_OPENCASCADE:
|
||||
self.set(settings.USE_BREP_DATA, value)
|
||||
self.set(settings.USE_WORLD_COORDS, value)
|
||||
self.set(settings.DISABLE_TRIANGULATION, value)
|
||||
self.use_python_opencascade = value
|
||||
else:
|
||||
ifcopenshell_wrapper.settings.set(self, *args)
|
||||
|
||||
|
||||
# Hide templating precision to the user by choosing based on Python's
|
||||
# internal float type. This is probably always going to be a double.
|
||||
for ty in (ifcopenshell_wrapper.iterator_single_precision, ifcopenshell_wrapper.iterator_double_precision):
|
||||
if ty.mantissa_size() == sys.float_info.mant_dig:
|
||||
_iterator = ty
|
||||
|
||||
|
||||
# Make sure people are able to use python's platform agnostic paths
|
||||
class iterator(_iterator):
|
||||
def __init__(self, settings, file_or_filename):
|
||||
self.settings = settings
|
||||
if isinstance(file_or_filename, file):
|
||||
file_or_filename = file_or_filename.wrapped_data
|
||||
else:
|
||||
file_or_filename = os.path.abspath(file_or_filename)
|
||||
_iterator.__init__(self, settings, file_or_filename)
|
||||
|
||||
if has_occ:
|
||||
def get(self):
|
||||
return wrap_shape_creation(self.settings, _iterator.get(self))
|
||||
|
||||
|
||||
class tree(ifcopenshell_wrapper.tree):
|
||||
|
||||
def __init__(self, file=None, settings=None):
|
||||
args = [self]
|
||||
if file is not None:
|
||||
args.append(file.wrapped_data)
|
||||
if settings is not None:
|
||||
args.append(settings)
|
||||
ifcopenshell_wrapper.tree.__init__(*args)
|
||||
|
||||
def add_file(self, file, settings):
|
||||
ifcopenshell_wrapper.tree.add_file(self, file.wrapped_data, settings)
|
||||
|
||||
def select(self, value, **kwargs):
|
||||
def unwrap(value):
|
||||
if isinstance(value, entity_instance):
|
||||
return value.wrapped_data
|
||||
elif all(map(lambda v: hasattr(value, v), "XYZ")):
|
||||
return value.X(), value.Y(), value.Z()
|
||||
return value
|
||||
|
||||
args = [self, unwrap(value)]
|
||||
if isinstance(value, entity_instance):
|
||||
args.append(kwargs.get("completely_within", False))
|
||||
elif has_occ:
|
||||
import OCC.TopoDS
|
||||
if isinstance(value, OCC.TopoDS.TopoDS_Shape):
|
||||
args[1] = utils.serialize_shape(value)
|
||||
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select(*args)]
|
||||
|
||||
def select_box(self, value, **kwargs):
|
||||
def unwrap(value):
|
||||
if isinstance(value, entity_instance):
|
||||
return value.wrapped_data
|
||||
elif hasattr(value, "Get"):
|
||||
return value.Get()[:3], value.Get()[3:]
|
||||
return value
|
||||
|
||||
args = [self, unwrap(value)]
|
||||
if "extend" in kwargs or "completely_within" in kwargs:
|
||||
args.append(kwargs.get("completely_within", False))
|
||||
if "extend" in kwargs:
|
||||
args.append(kwargs.get("extend", -1.e-5))
|
||||
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select_box(*args)]
|
||||
|
||||
|
||||
def create_shape(settings, inst, repr=None):
|
||||
"""
|
||||
Return a geometric representation from STEP-based IFCREPRESENTATIONSHAPE
|
||||
or
|
||||
Return an OpenCASCADE BRep if settings.USE_PYTHON_OPENCASCADE == True
|
||||
|
||||
example:
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
|
||||
|
||||
ifc_file = ifcopenshell.open(file_path)
|
||||
products = ifc_file.by_type("IfcProduct")
|
||||
|
||||
for i, product in enumerate(products):
|
||||
if product.Representation is not None:
|
||||
try:
|
||||
shape = geom.create_shape(settings, inst=product).geometry
|
||||
shape_gpXYZ = shape.Location().Transformation().TranslationPart() # These are methods of the TopoDS_Shape class from pythonOCC
|
||||
print(shape_gpXYZ.X(), shape_gpXYZ.Y(), shape_gpXYZ.Z()) # These are methods of the gpXYZ class from pythonOCC
|
||||
"""
|
||||
return wrap_shape_creation(
|
||||
settings,
|
||||
ifcopenshell_wrapper.create_shape(
|
||||
settings,
|
||||
inst.wrapped_data,
|
||||
repr.wrapped_data if repr is not None else None
|
||||
))
|
||||
|
||||
|
||||
def iterate(settings, filename):
|
||||
it = iterator(settings, filename)
|
||||
if it.initialize():
|
||||
while True:
|
||||
yield it.get()
|
||||
if not it.next():
|
||||
break
|
||||
|
||||
|
||||
def make_shape_function(fn):
|
||||
def entity_instance_or_none(e):
|
||||
return None if e is None else entity_instance(e)
|
||||
|
||||
if has_occ:
|
||||
import OCC.TopoDS
|
||||
|
||||
def _(string_or_shape, *args):
|
||||
if isinstance(string_or_shape, OCC.TopoDS.TopoDS_Shape):
|
||||
string_or_shape = utils.serialize_shape(string_or_shape)
|
||||
return entity_instance_or_none(fn(string_or_shape, *args))
|
||||
else:
|
||||
def _(string, *args):
|
||||
return entity_instance_or_none(fn(string, *args))
|
||||
return _
|
||||
|
||||
|
||||
serialise = make_shape_function(ifcopenshell_wrapper.serialise)
|
||||
tesselate = make_shape_function(ifcopenshell_wrapper.tesselate)
|
||||
@@ -1,42 +1,188 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import random
|
||||
from collections import namedtuple
|
||||
import operator
|
||||
from collections import namedtuple, Iterable
|
||||
|
||||
import OCC.gp
|
||||
import OCC.V3d
|
||||
import OCC.Quantity
|
||||
import OCC.BRepTools
|
||||
import OCC.Display.SimpleGui
|
||||
|
||||
tuple = namedtuple('shape', ('data', 'geometry'))
|
||||
shape_tuple = namedtuple('shape_tuple', ('data', 'geometry', 'styles'))
|
||||
|
||||
handle, main_loop, add_menu, add_function_to_menu = None, None, None, None
|
||||
|
||||
DEFAULT_STYLES = {
|
||||
"DEFAULT": (.7, .7, .7),
|
||||
"IfcWall": (.8, .8, .8),
|
||||
"IfcSite": (.75, .8, .65),
|
||||
"IfcSlab": (.4, .4, .4),
|
||||
"IfcWallStandardCase": (.9, .9, .9),
|
||||
"IfcWall": (.9, .9, .9),
|
||||
"IfcWindow": (.75, .8, .75, .3),
|
||||
"IfcDoor": (.55, .3, .15),
|
||||
"IfcBeam": (.75, .7, .7),
|
||||
"IfcRailing": (.65, .6, .6),
|
||||
"IfcMember": (.65, .6, .6),
|
||||
"IfcPlate": (.8, .8, .8)
|
||||
}
|
||||
|
||||
|
||||
def initialize_display():
|
||||
import OCC.V3d
|
||||
import OCC.Display.SimpleGui
|
||||
|
||||
global handle, main_loop, add_menu, add_function_to_menu
|
||||
handle, main_loop, add_menu, add_function_to_menu = OCC.Display.SimpleGui.init_display()
|
||||
|
||||
def setup():
|
||||
viewer_handle = handle.GetViewer()
|
||||
viewer = viewer_handle.GetObject()
|
||||
while True:
|
||||
|
||||
def lights():
|
||||
viewer.InitActiveLights()
|
||||
try: active_light = viewer.ActiveLight()
|
||||
except: break
|
||||
viewer.DelLight(active_light)
|
||||
viewer.NextActiveLights()
|
||||
for dir in [(1,2,-3), (-2,-1,1)]:
|
||||
while True:
|
||||
try:
|
||||
active_light = viewer.ActiveLight()
|
||||
except BaseException:
|
||||
break
|
||||
yield active_light
|
||||
viewer.NextActiveLights()
|
||||
|
||||
lights = list(lights())
|
||||
for l in lights:
|
||||
viewer.DelLight(l)
|
||||
|
||||
for dir in [(3, 2, 1), (-1, -2, -3)]:
|
||||
light = OCC.V3d.V3d_DirectionalLight(viewer_handle)
|
||||
light.SetDirection(*dir)
|
||||
viewer.SetLightOn(light.GetHandle())
|
||||
|
||||
setup()
|
||||
return handle
|
||||
|
||||
|
||||
def display_shape(shape, clr=None):
|
||||
if not clr:
|
||||
r = lambda: random.random() * 0.3 + 0.7
|
||||
def yield_subshapes(shape):
|
||||
import OCC.TopoDS
|
||||
|
||||
it = OCC.TopoDS.TopoDS_Iterator(shape)
|
||||
while it.More():
|
||||
yield it.Value()
|
||||
it.Next()
|
||||
|
||||
|
||||
def display_shape(shape, clr=None, viewer_handle=None):
|
||||
import OCC.gp
|
||||
import OCC.AIS
|
||||
import OCC.Quantity
|
||||
|
||||
if viewer_handle is None:
|
||||
viewer_handle = handle
|
||||
|
||||
if isinstance(shape, shape_tuple):
|
||||
shape, representation = shape.geometry, shape
|
||||
else:
|
||||
representation = None
|
||||
|
||||
material = OCC.Graphic3d.Graphic3d_MaterialAspect(OCC.Graphic3d.Graphic3d_NOM_PLASTER)
|
||||
material.SetDiffuse(1)
|
||||
|
||||
if representation and not clr:
|
||||
if len(set(representation.styles)) == 1:
|
||||
clr = representation.styles[0]
|
||||
if min(clr) < 0. or max(clr) > 1.:
|
||||
clr = DEFAULT_STYLES.get(representation.data.type, DEFAULT_STYLES["DEFAULT"])
|
||||
|
||||
if clr:
|
||||
ais = OCC.AIS.AIS_Shape(shape)
|
||||
ais.SetMaterial(material)
|
||||
|
||||
if isinstance(clr, str):
|
||||
qclr = getattr(OCC.Quantity, "Quantity_NOC_%s" % clr.upper(),
|
||||
getattr(OCC.Quantity, "Quantity_NOC_%s1" % clr.upper(), None))
|
||||
if qclr is None:
|
||||
raise Exception("No color named '%s'" % clr.upper())
|
||||
elif isinstance(clr, Iterable):
|
||||
clr = tuple(clr)
|
||||
if len(clr) < 3 and len(clr) > 4:
|
||||
raise Exception("Need 3 or 4 colour components. Got '%r'." % clr)
|
||||
qclr = OCC.Quantity.Quantity_Color(clr[0], clr[1], clr[2], OCC.Quantity.Quantity_TOC_RGB)
|
||||
elif isinstance(clr, OCC.Quantity.Quantity_Color):
|
||||
qclr = clr
|
||||
else:
|
||||
raise Exception("Object of type %r cannot be used as a color." % type(clr))
|
||||
|
||||
ais.SetColor(qclr)
|
||||
if isinstance(clr, tuple) and len(clr) == 4 and clr[3] < 1.:
|
||||
ais.SetTransparency(1. - clr[3])
|
||||
|
||||
elif representation and hasattr(OCC.AIS, "AIS_MultipleConnectedShape"):
|
||||
default_style_applied = None
|
||||
|
||||
ais = OCC.AIS.AIS_MultipleConnectedShape(shape)
|
||||
|
||||
subshapes = list(yield_subshapes(shape))
|
||||
lens = len(representation.styles), len(subshapes)
|
||||
if lens[0] != lens[1]:
|
||||
import warnings
|
||||
warnings.warn("Unable to assign styles to subshapes. Encountered %d styles for %d shapes." % lens)
|
||||
else:
|
||||
for shp, stl in zip(subshapes, representation.styles):
|
||||
subshape = OCC.AIS.AIS_Shape(shp)
|
||||
if min(stl) < 0. or max(stl) > 1.:
|
||||
default_style_applied = stl = DEFAULT_STYLES.get(representation.data.type,
|
||||
DEFAULT_STYLES["DEFAULT"])
|
||||
subshape.SetColor(OCC.Quantity.Quantity_Color(stl[0], stl[1], stl[2], OCC.Quantity.Quantity_TOC_RGB))
|
||||
subshape.SetMaterial(material)
|
||||
if len(stl) == 4 and stl[3] < 1.:
|
||||
subshape.SetTransparency(1. - stl[3])
|
||||
ais.Connect(subshape.GetHandle())
|
||||
|
||||
# For some reason it is necessary to set transparency here again
|
||||
# in order for transparency to be rendered on the subshape.
|
||||
applied_styles = representation.styles
|
||||
if default_style_applied:
|
||||
if len(default_style_applied) == 3:
|
||||
default_style_applied += (1.,)
|
||||
applied_styles += (default_style_applied,)
|
||||
|
||||
if len(applied_styles):
|
||||
# The only way for this not to be true if is the entire shape is NULL
|
||||
min_transp = min(map(operator.itemgetter(3), applied_styles))
|
||||
if min_transp < 1.:
|
||||
ais.SetTransparency(1.)
|
||||
|
||||
else:
|
||||
ais = OCC.AIS.AIS_Shape(shape)
|
||||
ais.SetMaterial(material)
|
||||
|
||||
def r():
|
||||
return random.random() * 0.3 + 0.7
|
||||
|
||||
clr = OCC.Quantity.Quantity_Color(r(), r(), r(), OCC.Quantity.Quantity_TOC_RGB)
|
||||
return handle.DisplayShape(shape, color=clr, update=True)
|
||||
ais.SetColor(clr)
|
||||
|
||||
ais_handle = ais.GetHandle()
|
||||
viewer_handle.Context.Display(ais_handle, False)
|
||||
|
||||
return ais_handle
|
||||
|
||||
|
||||
def set_shape_transparency(ais, t):
|
||||
@@ -44,23 +190,52 @@ def set_shape_transparency(ais, t):
|
||||
|
||||
|
||||
def get_bounding_box_center(bbox):
|
||||
bbmin = [0.]*3; bbmax = [0.]*3
|
||||
import OCC.gp
|
||||
|
||||
bbmin = [0.] * 3
|
||||
bbmax = [0.] * 3
|
||||
bbmin[0], bbmin[1], bbmin[2], bbmax[0], bbmax[1], bbmax[2] = bbox.Get()
|
||||
return OCC.gp.gp_Pnt(*map(lambda xy: (xy[0]+xy[1])/2., zip(bbmin, bbmax)))
|
||||
return OCC.gp.gp_Pnt(*map(lambda xy: (xy[0] + xy[1]) / 2., zip(bbmin, bbmax)))
|
||||
|
||||
|
||||
def serialize_shape(shape):
|
||||
import OCC.BRepTools
|
||||
|
||||
shapes = OCC.BRepTools.BRepTools_ShapeSet()
|
||||
shapes.Add(shape)
|
||||
return shapes.WriteToString()
|
||||
|
||||
|
||||
def create_shape_from_serialization(brep_object):
|
||||
brep_data, occ_shape = None, None
|
||||
|
||||
try: brep_data = brep_object.geometry.brep_data
|
||||
except: pass
|
||||
if not brep_data: return tuple(brep_object, None)
|
||||
|
||||
import OCC.BRepTools
|
||||
|
||||
brep_data, occ_shape, styles = None, None, ()
|
||||
|
||||
is_product_shape = True
|
||||
try:
|
||||
brep_data = brep_object.geometry.brep_data
|
||||
styles = brep_object.geometry.surface_styles
|
||||
except BaseException:
|
||||
try:
|
||||
brep_data = brep_object.brep_data
|
||||
styles = brep_object.surface_styles
|
||||
is_product_shape = False
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
styles = tuple(styles[i:i + 4] for i in range(0, len(styles), 4))
|
||||
|
||||
if not brep_data:
|
||||
return shape_tuple(brep_object, None, styles)
|
||||
|
||||
try:
|
||||
ss = OCC.BRepTools.BRepTools_ShapeSet()
|
||||
ss.ReadFromString(brep_data)
|
||||
occ_shape = ss.Shape(ss.NbShapes())
|
||||
except: pass
|
||||
|
||||
return tuple(brep_object, occ_shape)
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
if is_product_shape:
|
||||
return shape_tuple(brep_object, occ_shape, styles)
|
||||
else:
|
||||
return occ_shape
|
||||
|
||||
@@ -17,27 +17,41 @@
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
import string
|
||||
|
||||
from functools import reduce
|
||||
|
||||
chars = string.digits + string.ascii_uppercase + string.ascii_lowercase + '_$'
|
||||
|
||||
|
||||
def compress(g):
|
||||
bs = [int(g[i:i+2], 16) for i in range(0, len(g), 2)]
|
||||
bs = [int(g[i:i + 2], 16) for i in range(0, len(g), 2)]
|
||||
|
||||
def b64(v, l=4):
|
||||
return ''.join([chars[(v // (64**i))%64] for i in range(l)][::-1])
|
||||
return ''.join([b64(bs[0], 2)] + [b64((bs[i] << 16) + (bs[i+1] << 8) + bs[i+2]) for i in range(1,16,3)])
|
||||
return ''.join([chars[(v // (64 ** i)) % 64] for i in range(l)][::-1])
|
||||
|
||||
return ''.join([b64(bs[0], 2)] + [b64((bs[i] << 16) + (bs[i + 1] << 8) + bs[i + 2]) for i in range(1, 16, 3)])
|
||||
|
||||
|
||||
def expand(g):
|
||||
def b64(v):
|
||||
return reduce(lambda a, b: a * 64 + b, map(lambda c: chars.index(c), v))
|
||||
|
||||
bs = [b64(g[0:2])]
|
||||
for i in range(5):
|
||||
d = b64(g[2+4*i:6+4*i])
|
||||
bs += [(d >> (8*(2-j)))%256 for j in range(3)]
|
||||
return ''.join(['%02x'%b for b in bs])
|
||||
d = b64(g[2 + 4 * i:6 + 4 * i])
|
||||
bs += [(d >> (8 * (2 - j))) % 256 for j in range(3)]
|
||||
return ''.join(['%02x' % b for b in bs])
|
||||
|
||||
|
||||
def split(g):
|
||||
return '{%s-%s-%s-%s-%s}'%(g[:8], g[8:12], g[12:16], g[16:20], g[20:])
|
||||
return '{%s-%s-%s-%s-%s}' % (g[:8], g[8:12], g[12:16], g[16:20], g[20:])
|
||||
|
||||
|
||||
def new():
|
||||
return compress(uuid.uuid4().hex)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
from . import ifcopenshell_wrapper
|
||||
|
||||
version = ifcopenshell_wrapper.version()
|
||||
schema_identifier = ifcopenshell_wrapper.schema_identifier()
|
||||
get_supertype = ifcopenshell_wrapper.get_supertype
|
||||
get_log = ifcopenshell_wrapper.get_log
|
||||
@@ -0,0 +1,86 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# 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/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from .file import file
|
||||
from .guid import compress
|
||||
from . import main
|
||||
|
||||
# A quick way to setup an 'empty' IFC file, taken from:
|
||||
# http://academy.ifcopenshell.org/creating-a-simple-wall-with-property-set-and-quantity-information/
|
||||
TEMPLATE = """ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
|
||||
FILE_NAME('%(filename)s','%(timestring)s',('%(creator)s'),('%(organization)s'),'%(application)s','%(application)s','');
|
||||
FILE_SCHEMA(('%(schema_identifier)s'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1=IFCPERSON($,$,'%(creator)s',$,$,$,$,$);
|
||||
#2=IFCORGANIZATION($,'%(organization)s',$,$,$);
|
||||
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
|
||||
#4=IFCAPPLICATION(#2,'%(application_version)s','%(application)s','');
|
||||
#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,$,#3,#4,%(timestamp)s);
|
||||
#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.));
|
||||
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
|
||||
#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=IFCPROJECT('%(project_globalid)s',#5,'%(project_name)s',$,$,$,$,(#11),#19);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
"""
|
||||
|
||||
DEFAULTS = {
|
||||
"application": lambda d: 'IfcOpenShell-%s' % main.version,
|
||||
"application_version": lambda d: main.version,
|
||||
"project_globalid": lambda d: compress(uuid.uuid4().hex),
|
||||
"schema_identifier": lambda d: main.schema_identifier,
|
||||
"timestamp": lambda d: time.time(),
|
||||
"timestring": lambda d: time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(d.get('timestamp') or time.time()))
|
||||
}
|
||||
|
||||
|
||||
def create(filename=None, timestring=None, organization=None, creator=None,
|
||||
schema_identifier=None, application_version=None, timestamp=None,
|
||||
application=None, project_globalid=None, project_name=None):
|
||||
d = dict(locals())
|
||||
|
||||
def _():
|
||||
for var, value in d.items():
|
||||
if value is None:
|
||||
yield var, DEFAULTS.get(var, lambda *args: '')(d)
|
||||
|
||||
d.update(dict(_()))
|
||||
|
||||
return file.from_string(TEMPLATE % d)
|
||||
@@ -0,0 +1,86 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef ARGUMENT_H
|
||||
#define ARGUMENT_H
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "ifc_parse_api.h"
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#include "../ifcparse/Ifc4enum.h"
|
||||
#else
|
||||
#include "../ifcparse/Ifc2x3enum.h"
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/IfcEntityList.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
class Argument;
|
||||
class IfcEntityList;
|
||||
class IfcEntityListList;
|
||||
class IfcEntityInstanceData;
|
||||
namespace IfcParse {
|
||||
class IfcFile;
|
||||
}
|
||||
namespace IfcUtil {
|
||||
class IfcBaseClass;
|
||||
|
||||
IFC_PARSE_API const char* ArgumentTypeToString(ArgumentType argument_type);
|
||||
|
||||
IFC_PARSE_API bool valid_binary_string(const std::string& s);
|
||||
}
|
||||
|
||||
class IFC_PARSE_API Argument {
|
||||
public:
|
||||
virtual operator int() const;
|
||||
virtual operator bool() const;
|
||||
virtual operator double() const;
|
||||
virtual operator std::string() const;
|
||||
virtual operator boost::dynamic_bitset<>() const;
|
||||
virtual operator IfcUtil::IfcBaseClass*() const;
|
||||
|
||||
virtual operator std::vector<int>() const;
|
||||
virtual operator std::vector<double>() const;
|
||||
virtual operator std::vector<std::string>() const;
|
||||
virtual operator std::vector<boost::dynamic_bitset<> >() const;
|
||||
virtual operator IfcEntityList::ptr() const;
|
||||
|
||||
virtual operator std::vector< std::vector<int> >() const;
|
||||
virtual operator std::vector< std::vector<double> >() const;
|
||||
virtual operator IfcEntityListList::ptr() const;
|
||||
|
||||
virtual bool isNull() const = 0;
|
||||
virtual unsigned int size() const = 0;
|
||||
|
||||
virtual IfcUtil::ArgumentType type() const = 0;
|
||||
virtual Argument* operator [] (unsigned int i) const = 0;
|
||||
virtual std::string toString(bool upper=false) const = 0;
|
||||
|
||||
virtual ~Argument() {};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/********************************************************************************
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef ARGUMENTTYPE_H
|
||||
#define ARGUMENTTYPE_H
|
||||
|
||||
namespace IfcUtil {
|
||||
|
||||
enum ArgumentType {
|
||||
Argument_NULL,
|
||||
Argument_DERIVED,
|
||||
Argument_INT,
|
||||
Argument_BOOL,
|
||||
Argument_DOUBLE,
|
||||
Argument_STRING,
|
||||
Argument_BINARY,
|
||||
Argument_ENUMERATION,
|
||||
Argument_ENTITY_INSTANCE,
|
||||
|
||||
Argument_EMPTY_AGGREGATE,
|
||||
Argument_AGGREGATE_OF_INT,
|
||||
Argument_AGGREGATE_OF_DOUBLE,
|
||||
Argument_AGGREGATE_OF_STRING,
|
||||
Argument_AGGREGATE_OF_BINARY,
|
||||
Argument_AGGREGATE_OF_ENTITY_INSTANCE,
|
||||
|
||||
Argument_AGGREGATE_OF_EMPTY_AGGREGATE,
|
||||
Argument_AGGREGATE_OF_AGGREGATE_OF_INT,
|
||||
Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE,
|
||||
Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE,
|
||||
|
||||
Argument_UNKNOWN
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+706
-684
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user