Comparar commits
1738 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ecd832744c | |||
| 03b654af3e | |||
| c79a7f1819 | |||
| e01f18c7d4 | |||
| 42891874f4 | |||
| 257d6a6635 | |||
| bec8bef455 | |||
| 4efeaa7083 | |||
| 95fd9d6685 | |||
| 2db37c6018 | |||
| 41b0966d26 | |||
| 93485cd0df | |||
| 2d3382874a | |||
| 1e78397e18 | |||
| 6bcdd94f7e | |||
| accd28db64 | |||
| 1c0f4c42d9 | |||
| 5da1632d9f | |||
| cbf1152f56 | |||
| 2273c5aefe | |||
| 46c793e73d | |||
| fd4969887b | |||
| 6620a050a5 | |||
| 6325197fce | |||
| f828d44365 | |||
| 29564a23e0 | |||
| 1fdda3319f | |||
| be113eceb4 | |||
| 0c42ff9bfa | |||
| 168e041c42 | |||
| 670ae6dd8c | |||
| 20adfa751f | |||
| bf8776b112 | |||
| 1d0f3f519a | |||
| 07fef8668c | |||
| 1c06fc49fc | |||
| beb9422d9b | |||
| a8359b9a68 | |||
| 5ccfe2d1aa | |||
| 74577759b4 | |||
| 45eb026777 | |||
| aabb07fd81 | |||
| 6377ec63e1 | |||
| eab2d8e667 | |||
| 8d41003c67 | |||
| ecfae4d75c | |||
| a2208f6b69 | |||
| bacacb01dc | |||
| 94c9399f2c | |||
| 168dc3c12b | |||
| 235af65b00 | |||
| f3f3be74cc | |||
| dca0bc80b5 | |||
| c0e57622d0 | |||
| b7a8fbe798 | |||
| 4353980e78 | |||
| 18ae9cf41d | |||
| 04edf35331 | |||
| 6718a8b2f3 | |||
| 08a5ec7dee | |||
| 3de01763c1 | |||
| bc7ee74d19 | |||
| fc20a6661b | |||
| 81d521a58b | |||
| c4eb1d9f27 | |||
| 32d1bb6d75 | |||
| afdb552f63 | |||
| a400ba28f2 | |||
| e0a7818d9e | |||
| 277b5dce75 | |||
| 462a6e8c16 | |||
| c20d35e293 | |||
| 9e56841cd2 | |||
| c39e43c161 | |||
| 421a6d4095 | |||
| 8497e46542 | |||
| 6510ebbd92 | |||
| c1b4a5398b | |||
| 11c4b9339a | |||
| 75efbac68e | |||
| cd199dc43e | |||
| 2372a78c9b | |||
| 142f4d519f | |||
| 9ea43e7fc1 | |||
| 9b92de9b8b | |||
| 6826581497 | |||
| 4fead4612b | |||
| f763edfb05 | |||
| 0e662d7d67 | |||
| 94d8c3ff32 | |||
| 8e91753afc | |||
| 21e59cc1e2 | |||
| f0f14b0be3 | |||
| a82c00bead | |||
| 14b1c5333a | |||
| bbe809dabc | |||
| ed581a92b2 | |||
| 9b68305851 | |||
| 940d3a86bd | |||
| 55047120d8 | |||
| e50b00fd73 | |||
| 7377f30fd0 | |||
| c9245e0926 | |||
| 7149b7dbf1 | |||
| daa3927a99 | |||
| 074d1125e6 | |||
| a92274678d | |||
| 081393d7f0 | |||
| 30da3097d0 | |||
| d830fc515b | |||
| 5f2d924771 | |||
| b1ab1cae5a | |||
| 1fb6b20d90 | |||
| dfb4c98e35 | |||
| e455d63ad2 | |||
| cb024c73b1 | |||
| d2d25058cb | |||
| e85362370b | |||
| 00c6f5c092 | |||
| 818c7da951 | |||
| 41723c8b38 | |||
| c8d75dd913 | |||
| faa60c408f | |||
| 7e322bc57c | |||
| 888fc54660 | |||
| fd660fcc11 | |||
| e8080422a4 | |||
| 5fecf2cd17 | |||
| b4bf17c6be | |||
| fabd658b53 | |||
| a2050de513 | |||
| 9bfb5b17e9 | |||
| e93d39d6d5 | |||
| 175a65415a | |||
| 74986dd95c | |||
| 55dd35ae5e | |||
| 8caf810642 | |||
| 76efc41407 | |||
| b0a0fe8f30 | |||
| 653b631ffd | |||
| b9a01ed031 | |||
| de9d3bb447 | |||
| 8f5a46f77d | |||
| c22793b5d7 | |||
| 1e684f23a7 | |||
| 7d0025975b | |||
| 5c892b564b | |||
| d5327ec3f1 | |||
| c48104bc86 | |||
| f58f1daec3 | |||
| 669bb523a3 | |||
| fa74f672ac | |||
| e224d9c853 | |||
| 338d80a7a9 | |||
| 87123dac31 | |||
| 73de288f44 | |||
| 9f5b5f405d | |||
| 2850b34227 | |||
| 072d320f1d | |||
| 8e4b61d2a5 | |||
| ba4e26f801 | |||
| cea0a0a98f | |||
| 2b51a11739 | |||
| d32443ca9b | |||
| 792aa9e8e8 | |||
| a63abb4e88 | |||
| 9f82f58f03 | |||
| df04467ab1 | |||
| bbd9412b5f | |||
| c476acd522 | |||
| 278efdeb39 | |||
| bb8a9dacba | |||
| 42420edeed | |||
| be2e96f3fd | |||
| 18adc0bbc9 | |||
| e7824faefe | |||
| 7199be1b4f | |||
| 727c863f1a | |||
| d732c49dd7 | |||
| 781fc5ed7b | |||
| 7243cf6da4 | |||
| 1cdcce4205 | |||
| acf381c008 | |||
| 4b7df9ddc0 | |||
| 7788a4a234 | |||
| 6224713998 | |||
| db8b30d77a | |||
| 050b9c88f2 | |||
| 8ac63fca6e | |||
| 1383882bc5 | |||
| 3a773d3c0f | |||
| 734ddce3c8 | |||
| 0b19936299 | |||
| 691d5de591 | |||
| 490cef1a5c | |||
| b777b55935 | |||
| 0428c08152 | |||
| 20c74dd22d | |||
| 188b190d21 | |||
| 1cd128decd | |||
| 925247a54d | |||
| 94d9a948ae | |||
| 176bf2c887 | |||
| 3cf208f8ed | |||
| 08eec2ad1f | |||
| 41e012be5f | |||
| 8102f7a246 | |||
| d527358a82 | |||
| 6115bdec2a | |||
| 9f29cbd3fb | |||
| 5c3727d532 | |||
| 73152d84c7 | |||
| c7ce43eb6c | |||
| d9e2b39baf | |||
| 34e86195e9 | |||
| bad780c54b | |||
| 3ddbc1d53b | |||
| 51757353b4 | |||
| d931baa788 | |||
| 321a9c11e0 | |||
| 389950ed90 | |||
| 61e5187267 | |||
| 3859d58571 | |||
| 345c599062 | |||
| 73625b949f | |||
| bf1cf334e6 | |||
| 13ec686608 | |||
| 8b244c1738 | |||
| 22c1ff4efe | |||
| 811be991c1 | |||
| 684da831b2 | |||
| a9ff4a3826 | |||
| 6d2ecc7811 | |||
| e77ae7362b | |||
| 571d592136 | |||
| 4d1ebb33e6 | |||
| 995f9786a4 | |||
| 3467593aa2 | |||
| ef4b8abaf5 | |||
| f460eeef57 | |||
| 488f82ccbc | |||
| db3e9ac5e6 | |||
| 8529924bc4 | |||
| 03ff861904 | |||
| b3752a43b4 | |||
| 7e47664a51 | |||
| 1b6b5cb843 | |||
| 0d98c6eaef | |||
| 518a528596 | |||
| c9ea663a1b | |||
| 0a7355d25c | |||
| fc6cad48af | |||
| d4c7abf7ef | |||
| c04274401a | |||
| ee8dd68046 | |||
| afcacd00a7 | |||
| a5b31de404 | |||
| c8f1922966 | |||
| ac24325715 | |||
| 4807e13de6 | |||
| b389b24d9b | |||
| 121d13b57e | |||
| b1dc67be71 | |||
| 2584c7b38c | |||
| ac8b0e149a | |||
| 03141f505d | |||
| e18659669e | |||
| 903f3b11d1 | |||
| 51f27fd0ca | |||
| b7d1e53992 | |||
| 461a90a319 | |||
| b19c6751c5 | |||
| e2479e12b0 | |||
| c67f691237 | |||
| 9c8e32f37b | |||
| 124b116b0c | |||
| 0b8fd8209c | |||
| 1534ff0361 | |||
| b6268d8dc9 | |||
| 3413f3abaa | |||
| cb5c69d064 | |||
| afa7c5a0c0 | |||
| 7cbfd42b8f | |||
| 515bf4592e | |||
| 667644eee4 | |||
| 17e00733a4 | |||
| 5142f8c1ba | |||
| 432abf11d5 | |||
| 77361417a4 | |||
| 83da6b2072 | |||
| e567f8265a | |||
| c20b07fe0d | |||
| 10815b4f93 | |||
| a119be9021 | |||
| 002692fe18 | |||
| 99e9054f54 | |||
| eedd4adee3 | |||
| e110b864ce | |||
| 1109f97ae6 | |||
| 469ab32b5c | |||
| e1390582ed | |||
| 6b1b11b47b | |||
| 3526edd04a | |||
| a101be9d2f | |||
| 52aedafc13 | |||
| afd1e2659e | |||
| 93896a6960 | |||
| 954a396d96 | |||
| 610880737f | |||
| 36ed9904b5 | |||
| 996fc2cbc1 | |||
| 7af12e5526 | |||
| 0323179b4a | |||
| 91956ad0e6 | |||
| ff9dbf9a51 | |||
| bbff888e23 | |||
| 4460b9a1d0 | |||
| dd161ef770 | |||
| 30050349fa | |||
| bafcb804f4 | |||
| a7320a2d14 | |||
| a7e72ea917 | |||
| 61c9cb5a07 | |||
| d029d7294f | |||
| 4c2aaed215 | |||
| 68602df6ee | |||
| 017fb03391 | |||
| 92f5311aaf | |||
| 00abb3ab4c | |||
| 4f9f55ab06 | |||
| dfc91d811f | |||
| 2bfdb2d612 | |||
| 026f82540f | |||
| 835424121f | |||
| d87072f761 | |||
| f89a747836 | |||
| 767803d2bf | |||
| a808e9e8ed | |||
| 087efefc16 | |||
| 09afe2264a | |||
| 6866925a05 | |||
| 5ede92fb1b | |||
| 321ef643a4 | |||
| c43c876cbc | |||
| 3d596f1765 | |||
| ff747fa88f | |||
| 950441493c | |||
| 0c7cca6f17 | |||
| 41a1db1e55 | |||
| c058a2b12b | |||
| adc8341e49 | |||
| 0717594a15 | |||
| d6ed95ed81 | |||
| c72b05463a | |||
| eb1acd02a2 | |||
| 102b9f9c75 | |||
| abb0b9a763 | |||
| 63eda27a50 | |||
| 5b1eeecf76 | |||
| 41ede0727b | |||
| 7ffec2e617 | |||
| 57ebcf0163 | |||
| 71ae42a949 | |||
| 49a1ed7f3b | |||
| 190b5b789b | |||
| ad29876617 | |||
| 07481a548c | |||
| a6be90d3b4 | |||
| 3231d75d94 | |||
| 5e4080e0bb | |||
| 6432215ad9 | |||
| 3a60d0cb01 | |||
| 7d70768178 | |||
| 9c201e3396 | |||
| f00c091ed1 | |||
| 4574e0917e | |||
| 4ae1f5e6bf | |||
| ae02a91a5d | |||
| 0781a8f4ef | |||
| f1902d88bf | |||
| 4cc5c35564 | |||
| f124a7e528 | |||
| 1cd8e1c500 | |||
| 6dc89bbe7f | |||
| 16250c54c9 | |||
| c6e201e307 | |||
| c5be07212e | |||
| 7035e91ac6 | |||
| 4f972da7ae | |||
| 47718a1ba4 | |||
| 87db7232a1 | |||
| b9f9c12d6c | |||
| 046fa3ba8e | |||
| db039033ce | |||
| c0fa1b4a1a | |||
| 1d073600e4 | |||
| 8f6cb82421 | |||
| a455913165 | |||
| 9b0e4be570 | |||
| 1023102e3a | |||
| 0ac334c849 | |||
| 696e0132f4 | |||
| f9a0e39a6a | |||
| ff9e7f11ae | |||
| fb284397c2 | |||
| f5e2c5a070 | |||
| 85cfd422ba | |||
| c5334e355e | |||
| 086c38ac0b | |||
| 4cb64bab67 | |||
| 87eaa97f21 | |||
| 59fc004f09 | |||
| 5dbfab6ffb | |||
| 5494ce12a5 | |||
| 1ed1de147a | |||
| 1c0a616ec7 | |||
| 82a4bff953 | |||
| f7bbda7599 | |||
| 273f1f46f3 | |||
| e81abe405d | |||
| d0dd97ab04 | |||
| d0e6f5c07e | |||
| f4eb33e14e | |||
| 3d3ddc238f | |||
| 704edd25af | |||
| e33560beb6 | |||
| 71e486d007 | |||
| 4ca66b6916 | |||
| 7e6c4661b9 | |||
| f96eaddf6c | |||
| eb2ae0d699 | |||
| bcee6c7cc2 | |||
| 3a28f5d547 | |||
| 5c41425859 | |||
| 85959b0aa7 | |||
| 6bd1d9fe8c | |||
| 3e43df9e60 | |||
| 4b11b5946e | |||
| d558e24b43 | |||
| 5d6f45a71d | |||
| d827cc0f92 | |||
| faa580ffe6 | |||
| 16679d68d0 | |||
| 9983ebac7b | |||
| ee8ef2bfa5 | |||
| 7646426f9d | |||
| 263eb370e9 | |||
| c0d22dcf7b | |||
| 01d0a3b8d6 | |||
| 1dd7313332 | |||
| 7a7d1ebd77 | |||
| 2976b1276c | |||
| b84f41d9b0 | |||
| f142df9176 | |||
| 94f90419dd | |||
| 90547bb69c | |||
| 3611d53222 | |||
| ab7ca73596 | |||
| c42568c059 | |||
| 43eb784b6c | |||
| 68f1eb1fd2 | |||
| 3dfb84b9a1 | |||
| 80896e3f4d | |||
| 6518e14e18 | |||
| 4c95e43048 | |||
| 4c66b7fd01 | |||
| 97ebfe8b12 | |||
| 0cb8b64f70 | |||
| bfc2d458ae | |||
| 365de0578c | |||
| d9c30de796 | |||
| e8cb1028e4 | |||
| bc41b415c2 | |||
| d4adbb97bf | |||
| c8488f6d37 | |||
| 7524df2868 | |||
| 28ac5dc2c9 | |||
| f5db2ee8b4 | |||
| 8e736f32aa | |||
| 9b4d93e236 | |||
| 2f4316e755 | |||
| 52e5b33e7e | |||
| 01c124774c | |||
| b4afc8bb4f | |||
| d4e2f774db | |||
| eb388c7d80 | |||
| ab5b7f432e | |||
| e0a09dbe7c | |||
| d83863ce4c | |||
| fe8def059e | |||
| b91abb4c22 | |||
| 4179e46a9d | |||
| 692e875256 | |||
| 9d513291ba | |||
| 5b0370864b | |||
| 4dcb8563f5 | |||
| 21b6be76fe | |||
| f06fa4af1a | |||
| 951eac4c08 | |||
| e4e2dc172e | |||
| 70b0800f2e | |||
| aaaf2a6c92 | |||
| bde136e7ed | |||
| 75859623d7 | |||
| 388a65ef11 | |||
| 1168c2b120 | |||
| dfd31844ba | |||
| f457f2590c | |||
| fb9bcccbbe | |||
| cfe216ae44 | |||
| a41a1e556a | |||
| 8081237298 | |||
| 1a7137a6c8 | |||
| 4c967ec6a3 | |||
| b455661a75 | |||
| 46b7d1ccab | |||
| fb95022776 | |||
| 62ce0b5513 | |||
| 12c25b2214 | |||
| 61915fc322 | |||
| aa1998dbd8 | |||
| 859a106ec2 | |||
| ff38534644 | |||
| 2eb81d216f | |||
| 2785659fe6 | |||
| 4bc86ad192 | |||
| a7b5e05505 | |||
| 0fd130cfd3 | |||
| ccae7d611e | |||
| 5dcce74efe | |||
| 988af0f6ed | |||
| 53714319b4 | |||
| fe44537924 | |||
| a285b22804 | |||
| e588fe100b | |||
| ad81cc8a12 | |||
| a3db17442c | |||
| e0cf567bf7 | |||
| 904ad24eec | |||
| f49803b8a9 | |||
| 2b422ce6c0 | |||
| b2cc46a4cb | |||
| 98d105d73c | |||
| ecd32700f5 | |||
| 44c77d2a31 | |||
| f78c3ed4da | |||
| 79b16e406a | |||
| 8219f32d96 | |||
| 05cfe62927 | |||
| 38e832c172 | |||
| 64ef6e5698 | |||
| 769dc252df | |||
| 7093231707 | |||
| ad93a8adde | |||
| 1bb8bde1de | |||
| d6822cdaf8 | |||
| 2817528c51 | |||
| a52f101c14 | |||
| 22b1d427ec | |||
| b89733320d | |||
| f662844d78 | |||
| 38a4e45771 | |||
| dc87dda346 | |||
| f249796f3d | |||
| 9602616a6b | |||
| 0823f8faf5 | |||
| 33d63e8359 | |||
| 3a8a5844d4 | |||
| 7b5bb1e367 | |||
| 017dc5be4c | |||
| 60c9e83da9 | |||
| 51927938b0 | |||
| d4b9f7c0bd | |||
| 0b78099587 | |||
| a61d5385ea | |||
| 24c836f87a | |||
| 8a2ca62305 | |||
| b0ae307006 | |||
| 29dd62345a | |||
| b3f555c78f | |||
| 0b2f64c6a7 | |||
| d501fe67d5 | |||
| 7180e77699 | |||
| 9c36fdf513 | |||
| bc8a167e7b | |||
| a2881b83f7 | |||
| bd90dd060f | |||
| 1070d367e1 | |||
| 5ac62a96a8 | |||
| 7010689448 | |||
| 869975be6b | |||
| da3bf8da32 | |||
| e861fba11b | |||
| 226c974b5f | |||
| b25c72a9e3 | |||
| 6caf2b2db9 | |||
| b6138cb951 | |||
| e9b0cfc939 | |||
| f6e44af43d | |||
| 487f35abd4 | |||
| 2a163528f9 | |||
| 02e6c87396 | |||
| b367af6ad1 | |||
| eef8b77405 | |||
| 97a4c00022 | |||
| 6516ae0da9 | |||
| 360e3d7e24 | |||
| c267492a48 | |||
| 52258e2d3a | |||
| 92c6edca86 | |||
| 9c26c820a9 | |||
| b7993441ff | |||
| 10ebc96323 | |||
| 13af50f34d | |||
| 0563c6eacc | |||
| ab313b8992 | |||
| 7779bb9604 | |||
| 1576d1d19e | |||
| 615ed0b384 | |||
| 6f95a78f3c | |||
| ae79d232f3 | |||
| 11786ada66 | |||
| 648bc1f3f3 | |||
| 84be436eb9 | |||
| e04965187a | |||
| ee09c6da92 | |||
| 325345a2ce | |||
| 29e17d8bdf | |||
| 1f1bfadf9e | |||
| 5aca2bde45 | |||
| ae6426cb6e | |||
| 7271693169 | |||
| d5bbe57571 | |||
| 8423d3ed4a | |||
| b1f717ad9b | |||
| 00eba89494 | |||
| 76e5b8b94b | |||
| 80e09a05ef | |||
| e96bd68324 | |||
| 9ba6239219 | |||
| af099e6896 | |||
| bcdcc25a51 | |||
| 6b9c98914d | |||
| e1d508ade0 | |||
| 919c0ea16d | |||
| 865b22063b | |||
| 4bfd48efa7 | |||
| 9e2b269c8b | |||
| 2e78d60c13 | |||
| caf1fd5582 | |||
| 8f99483d2c | |||
| fc31b13a5e | |||
| 2ea1bc0e59 | |||
| 102fb24d74 | |||
| 381bf1c3d2 | |||
| 03b188aca5 | |||
| d584b19078 | |||
| df1b0372ff | |||
| 73de8ac75b | |||
| 48464e9d13 | |||
| 51a9d1b44f | |||
| 481142ef4a | |||
| 2eb3d3438a | |||
| eedda865bc | |||
| d9195c276f | |||
| 5accc6adf8 | |||
| f7fde50e73 | |||
| 765b8be00b | |||
| 756af8601c | |||
| 21661f2f4b | |||
| 1ffa57f740 | |||
| 158b944b76 | |||
| cce2efd419 | |||
| 1853a41117 | |||
| 69412eda52 | |||
| 567c385e69 | |||
| e35f21ef7b | |||
| ee8b2ed39c | |||
| 7d93688b9b | |||
| 8daa32f757 | |||
| cf05eadfaa | |||
| 70093fdb5a | |||
| 1a7dec654e | |||
| 24f41fe419 | |||
| 97e89e1a94 | |||
| 913f955de8 | |||
| a2fa27603b | |||
| 9d313910f4 | |||
| eacc87d435 | |||
| 57f276d07e | |||
| dd638c32e5 | |||
| e02ed7c928 | |||
| c0007b156f | |||
| 2c1f4cf50e | |||
| 28612e060c | |||
| 094370db73 | |||
| ecb6b7c914 | |||
| 51b2d390c5 | |||
| be157338ad | |||
| 2c54394cd0 | |||
| 141f29bc18 | |||
| 28b27c971e | |||
| 7efc41e218 | |||
| 7145023b6f | |||
| 8797157351 | |||
| f999e1b75f | |||
| 5876964d41 | |||
| 61ff30772c | |||
| e6453082bf | |||
| c2038f7408 | |||
| ff5a1d82f8 | |||
| 1b13dfca97 | |||
| fed536b079 | |||
| 3cf74b5624 | |||
| 52a57b1499 | |||
| 831266db31 | |||
| 5fdb56966f | |||
| eae4c507a4 | |||
| 92b68c5745 | |||
| 9ade99a9c7 | |||
| 097c390fab | |||
| f982202aa4 | |||
| c3f474e229 | |||
| 125af2915f | |||
| 7b26d08d25 | |||
| a2b631b61b | |||
| e3df22dab9 | |||
| 7ad1b32ef3 | |||
| 98a7c65d66 | |||
| b60ad09882 | |||
| 0f35450440 | |||
| 0adc1b44e7 | |||
| 8d084e727a | |||
| 916005b6ad | |||
| c7ecc23053 | |||
| 9d36987124 | |||
| 0fa3cab356 | |||
| 31226c0b68 | |||
| ca23a77ad0 | |||
| b052cd431c | |||
| f00cb00f52 | |||
| f236969909 | |||
| 70dde36d85 | |||
| 52f8a7500e | |||
| 710138f487 | |||
| f7e3682c35 | |||
| 4fca97352d | |||
| 10773b688e | |||
| aae2aad42c | |||
| 79d36d9a49 | |||
| 96d64b325a | |||
| a316302314 | |||
| 93b0aad9f0 | |||
| 1df22b3cab | |||
| 0a3da66539 | |||
| 45674d0b1c | |||
| 834a778f93 | |||
| 160612f0dc | |||
| 0cd463820f | |||
| 73f3995443 | |||
| 72762d9db5 | |||
| 22cbeb7c0a | |||
| 819206afff | |||
| 9121c5440d | |||
| 587121c58d | |||
| 5d5912f8c2 | |||
| efd88d4c84 | |||
| c994d830fa | |||
| 186bdb50da | |||
| 2b418030bb | |||
| 874465b450 | |||
| 7089a2d72a | |||
| 3d7309bb3d | |||
| 2f355d1263 | |||
| 478c667655 | |||
| 0d07f3b9b0 | |||
| d7d3fd47f7 | |||
| 244a90e4eb | |||
| ec5680e2ea | |||
| f9e562e020 | |||
| e7379efaf3 | |||
| 576d90806d | |||
| dcedf97493 | |||
| 76af340433 | |||
| 24fdcd0355 | |||
| 3f6f35bd95 | |||
| b41288c63d | |||
| 75b13046cf | |||
| 2433c8521e | |||
| 613e65ecba | |||
| 6a7d9a7c18 | |||
| 6e52f81840 | |||
| 7984932307 | |||
| 871fdc3a01 | |||
| 807e734fa4 | |||
| b59ebe074d | |||
| 81a3b14999 | |||
| a1a3c079bb | |||
| 33d5c91227 | |||
| d43774c664 | |||
| 6feb5bbe8c | |||
| ed15bdcc25 | |||
| 95bbf211ba | |||
| bb2138b060 | |||
| debe1c5129 | |||
| 157f0c37f8 | |||
| cac5ee612c | |||
| a931f2f964 | |||
| 6450379a1c | |||
| 24ece43700 | |||
| ff59738246 | |||
| b3de3b7643 | |||
| 1523350501 | |||
| 1a5fd99d7e | |||
| 55ee8f687b | |||
| 265adb1a5d | |||
| 235c561bb5 | |||
| 1753bd566e | |||
| 5e8c73a0a2 | |||
| 5752b358f2 | |||
| 10d4785659 | |||
| 9beaee7161 | |||
| c79e052c17 | |||
| 5b88fda464 | |||
| 52cd83bac3 | |||
| a47adb2114 | |||
| e0001ffa5b | |||
| 58734899d7 | |||
| 60b0f43838 | |||
| 4bed0e3ad1 | |||
| 0e27cbfd4f | |||
| 94c29cf0af | |||
| 7d1d60b422 | |||
| 7fca86e27a | |||
| faeaf2816f | |||
| 9943279361 | |||
| 58b2d42596 | |||
| c42df2e53e | |||
| 3607a88f0a | |||
| 8625365cf0 | |||
| 941354c740 | |||
| 13f5ebe9d7 | |||
| 9ba97a7cd7 | |||
| f9bf42f610 | |||
| ce4a375780 | |||
| f0675c88df | |||
| db787c7cab | |||
| 21a04beb25 | |||
| 0d0dd7292a | |||
| 735f50875e | |||
| f0fd1a654e | |||
| 1565e979ec | |||
| 82532a3ddf | |||
| 7075ca34d4 | |||
| 927395630b | |||
| 8c9625f578 | |||
| 7c4064aa5f | |||
| 6d82284545 | |||
| fe512222e5 | |||
| 79d164b2d4 | |||
| a18cfd9af1 | |||
| 7469a48449 | |||
| 9c8bac7a99 | |||
| 3cfbc20d75 | |||
| 9baaa93159 | |||
| 9b92c42947 | |||
| fcaa7d71b7 | |||
| 423516ddd9 | |||
| 0a77aaa385 | |||
| a54e70e48f | |||
| d3e41f3ce2 | |||
| a9b4aa7640 | |||
| 95ff26940e | |||
| e5f87bcbc7 | |||
| 210636e205 | |||
| 028c8c6e42 | |||
| 73f5691651 | |||
| afc3088427 | |||
| f1ac6c05b1 | |||
| ddc2c1e574 | |||
| fcbcf380ae | |||
| eccda61b55 | |||
| 0f8b52d1a0 | |||
| 35373a5b1a | |||
| 094d0e84a2 | |||
| a5beedf4f6 | |||
| a77f0bf728 | |||
| 8a75b638b7 | |||
| 0815a834d8 | |||
| 93c9864f17 | |||
| 2444667a70 | |||
| 0ec956c653 | |||
| ccd1c44315 | |||
| bed85c7e68 | |||
| 4e40b2bc09 | |||
| 6fbe9c7de8 | |||
| d965f1b7e5 | |||
| 172722c63b | |||
| 702564f5c6 | |||
| 5b70a57700 | |||
| 1ce42b6b93 | |||
| e5b79ea847 | |||
| 70168cde07 | |||
| 7dad3d5018 | |||
| 87fbaf51f9 | |||
| 5cf401cde7 | |||
| 136aea0c15 | |||
| 8e43531e10 | |||
| 329d85a9aa | |||
| 7ac77ba24b | |||
| 1406c8f2ea | |||
| c0715fd490 | |||
| 267e0b1fa9 | |||
| dcf56c5a8e | |||
| bf23ca38ba | |||
| b851be6a06 | |||
| 84b3176ac3 | |||
| b9f8325534 | |||
| 9fa902ad6d | |||
| 68888fc0b7 | |||
| 136e199225 | |||
| 0a31584760 | |||
| 295e51309f | |||
| 5f09352a24 | |||
| 94faff843f | |||
| 2efce629f7 | |||
| 6964c7122b | |||
| 6b97779ac8 | |||
| 5d9c64a140 | |||
| 5ca046fec2 | |||
| 5657577938 | |||
| 224fec0b79 | |||
| ae2b67ef69 | |||
| 3b8ad5e55c | |||
| abd2c586db | |||
| 0a8b508b4c | |||
| df97f36315 | |||
| cc3ce8ae6b | |||
| ba2f47aa67 | |||
| e516682744 | |||
| f43f4285a8 | |||
| 99c756aa90 | |||
| a8a2d69a5f | |||
| f7ac889bb4 | |||
| a326e73e4a | |||
| 2796103a2b | |||
| 14c4ac55f1 | |||
| 831b9d94f8 | |||
| 5fa793ece7 | |||
| 4065e6f7e8 | |||
| 7ff00dfbb0 | |||
| 84909e2e77 | |||
| 9d75cdf0f7 | |||
| 01f1568dda | |||
| c0c842649f | |||
| e8d117d62f | |||
| 87ae0dc870 | |||
| e26ee1a113 | |||
| 9d31a525c9 | |||
| ffb08f9f8d | |||
| 294dec007a | |||
| 93a19422e8 | |||
| c44adab86d | |||
| 7bb5cbe98e | |||
| 883b0c2efc | |||
| 57c91c35b0 | |||
| 0b0db9f864 | |||
| 00a2a2a107 | |||
| 64ddaf2ff5 | |||
| a901e88aaa | |||
| fe03259704 | |||
| eb164c8a0e | |||
| cf209a157b | |||
| 0f547ae354 | |||
| 2ebfbe837d | |||
| af87a00eaa | |||
| 09543636c7 | |||
| f5a56a5048 | |||
| 16185bd88c | |||
| 47479a9fd5 | |||
| fce31b0c32 | |||
| d44c68a28b | |||
| 5f793943f2 | |||
| 3d81f84c0f | |||
| 9f59447a81 | |||
| 67c9b3dd23 | |||
| f1028b55a2 | |||
| 4cb11122dc | |||
| 58438bf56e | |||
| e93ba3d37f | |||
| 9566e60037 | |||
| b0b06635ac | |||
| 6dfacb7d2c | |||
| f1774665df | |||
| 45c8439446 | |||
| 1d0fded682 | |||
| 31a81c5476 | |||
| 229d3b1a2f | |||
| 61c4ffd982 | |||
| 796ced3508 | |||
| c91137cc03 | |||
| 6df992580a | |||
| d061420750 | |||
| 50ef87d41f | |||
| c4952dd28f | |||
| 6c5c5c2e34 | |||
| 1520e010e7 | |||
| 30bd8b5c67 | |||
| 8cc54c60de | |||
| a4a5e22b50 | |||
| 4529585acb | |||
| d1bc5f8002 | |||
| ab68ee508e | |||
| db7eb35e34 | |||
| 216ab79521 | |||
| 2a74c39642 | |||
| eeaa53c118 | |||
| effff44843 | |||
| 99ea16e251 | |||
| 574a89ad93 | |||
| f4af9ec780 | |||
| 81dc71a2de | |||
| 9f36330fe6 | |||
| f73094179e | |||
| a430281b46 | |||
| 57820afdfa | |||
| 8ed7d6e4d5 | |||
| c23701d7f1 | |||
| 8964454bf4 | |||
| 153370bbcb | |||
| ff28da5bde | |||
| 2ea7a8724a | |||
| d073dda5c4 | |||
| 93be1b8d63 | |||
| 0991624234 | |||
| bc28971f10 | |||
| c98b6ba8a8 | |||
| 600eed61fc | |||
| 0088c2e6ca | |||
| cdf7628287 | |||
| 7b7e9eec24 | |||
| 326ba11c87 | |||
| eee41a6aee | |||
| 20fa95def8 | |||
| 43c3b11253 | |||
| 7c708c1072 | |||
| 33b287000c | |||
| 97ed92d785 | |||
| 218a028dd6 | |||
| a53977e49c | |||
| 3c718af85e | |||
| ae3c113ca0 | |||
| b3f3135157 | |||
| 3d8e1d39d7 | |||
| a7335a5caa | |||
| a2f88584bf | |||
| 61c3de631e | |||
| 2aa72aa0c4 | |||
| c2c7e4dac3 | |||
| 626eed9ecb | |||
| e1420f661d | |||
| e8faa99a46 | |||
| 8f2fe55f54 | |||
| e587cf0bee | |||
| 1fb0ed5c5a | |||
| 5163caed25 | |||
| bd90859055 | |||
| 50cdb76249 | |||
| 36e7e2c4f4 | |||
| c66ca18e1f | |||
| f68a82eca4 | |||
| 24f74a733b | |||
| ae545a9ee9 | |||
| 45a4f2f28f | |||
| b533629835 | |||
| 282442c00c | |||
| 1a014c9b36 | |||
| 66385ed4bd | |||
| f6013a8683 | |||
| 3b798fb593 | |||
| ab36feefbe | |||
| a509e6af7e | |||
| 607989c28e | |||
| a31c855141 | |||
| 6a288763b1 | |||
| 16d960d5b7 | |||
| ed659ea663 | |||
| 71af3e1548 | |||
| 999f90cc00 | |||
| 51214f9dfd | |||
| aaa871157b | |||
| 488d74c950 | |||
| bf23980e57 | |||
| b175265385 | |||
| 8403514604 | |||
| 9e1a2c674e | |||
| cbe36a41c3 | |||
| 14e7397a53 | |||
| abbda6cd88 | |||
| 44411a45c7 | |||
| 79b2b36ecf | |||
| 7a05419def | |||
| 8423d2ab6c | |||
| 44ab7dd0cd | |||
| e6f4851d00 | |||
| e3d89e9fae | |||
| 4623962a11 | |||
| 3c37901918 | |||
| b763469114 | |||
| 33e68d7534 | |||
| 903d0b3260 | |||
| 52ca544328 | |||
| 80ada66e91 | |||
| 4e9bc9c955 | |||
| d406cd3fd1 | |||
| 0e375ca376 | |||
| 6c4d758cd9 | |||
| 1cd7c82c03 | |||
| 641dd33d74 | |||
| e48e4dea7b | |||
| 0aa163d84d | |||
| c2a233db51 | |||
| e90d3008c2 | |||
| 59879ba38f | |||
| 266e280860 | |||
| 6c489217f5 | |||
| d4bae3c377 | |||
| 15cf1bffc8 | |||
| bb7d6f4cdc | |||
| 1936cbb781 | |||
| 55f4ad68a9 | |||
| a45f936366 | |||
| dcef0205b2 | |||
| 61976be9c1 | |||
| fd5f33570d | |||
| ff568d0be2 | |||
| b29d71b2cc | |||
| 2ee21bf4d2 | |||
| cfd5178d54 | |||
| c1b6e5a8b3 | |||
| 02b32238d8 | |||
| 5f47023759 | |||
| 68e3966257 | |||
| 024fed3dc5 | |||
| b2e5a290ce | |||
| 8f9fc7a7b1 | |||
| 2bbd2a5cd8 | |||
| 18b476bc33 | |||
| 06aeff27df | |||
| 683a1ea99b | |||
| dac08fa727 | |||
| 22cffd2ac6 | |||
| b2e00b042c | |||
| d2240c7228 | |||
| 68d00d0d00 | |||
| b37e864cee | |||
| acb58d0ac0 | |||
| 2edfafb988 | |||
| 5ea46a44be | |||
| a6ec820c1e | |||
| 228b641b8d | |||
| 22bf8b290a | |||
| c851427848 | |||
| 8d6d61b3aa | |||
| 0787250aca | |||
| 2c2492b6ea | |||
| 2d8ce2173a | |||
| 35faa0c2ec | |||
| 7c5ea7cba4 | |||
| cace9448df | |||
| 03a13199b4 | |||
| 177d16015b | |||
| 54080f7119 | |||
| 66a0b0275d | |||
| 7602f3ccfb | |||
| dcadf6668a | |||
| 379fee78f4 | |||
| d4f3d0c37b | |||
| 0954fef276 | |||
| eb947ef52c | |||
| 0c08bf6ce0 | |||
| a9db2813a8 | |||
| 34682fc8a3 | |||
| 9b0b68c75e | |||
| f9854aead9 | |||
| 468abb641b | |||
| c62774c572 | |||
| 68f2824fd0 | |||
| 556dace457 | |||
| 6f80977b35 | |||
| ef8c076a99 | |||
| 31ea98a59d | |||
| 6c05436a0e | |||
| 859f37b96c | |||
| 05874bb9c4 | |||
| ca7353e4d4 | |||
| b9d48c9580 | |||
| f7edd64f9f | |||
| 6fafc0ab29 | |||
| 93a45c4ed1 | |||
| 05263813f8 | |||
| bb3098e0bc | |||
| 1288038edf | |||
| 33e0083cda | |||
| 7a01dbb08f | |||
| 2cd3bc330c | |||
| eeeadb8221 | |||
| 290c75669e | |||
| 0a01ba47e5 | |||
| 9b30d16a16 | |||
| 01250390de | |||
| c22b1bef74 | |||
| 4820fbc5ee | |||
| 6285f1d1fd | |||
| 1f77742a18 | |||
| f1a83c18db | |||
| 8a2956334e | |||
| ceacaa27b1 | |||
| 8032c7bfec | |||
| b3c83ce5e1 | |||
| f278248402 | |||
| 99f2950cb2 | |||
| 712acb9639 | |||
| 4b0189b1b6 | |||
| eda8800636 | |||
| 9b95c2f506 | |||
| 6241ec2f5b | |||
| 3d9f8bd740 | |||
| 3c6f6832a9 | |||
| e0e009368d | |||
| df29292d8f | |||
| 461b41b7e4 | |||
| f02b197322 | |||
| 2ef092a720 | |||
| e305fd4823 | |||
| d1e4518961 | |||
| 3948590ded | |||
| 7b2b9630c7 | |||
| a1b0ffc1a7 | |||
| fbbe908f35 | |||
| cdf3215ab3 | |||
| faaf5737f2 | |||
| e843d53ec0 | |||
| cbb080f327 | |||
| 4621d77a17 | |||
| 77935a845c | |||
| 62c7c113ca | |||
| 107aaf0c93 | |||
| b90263b481 | |||
| d2f6a86e54 | |||
| e0c294238f | |||
| 487327256a | |||
| e4e0dff046 | |||
| 8e5881d7e4 | |||
| 780fe88fe7 | |||
| 9f73f59d2e | |||
| 6e7cf77e3d | |||
| 6207419948 | |||
| 112cf55ea9 | |||
| b7587a1f1e | |||
| 01a430b052 | |||
| bdc12d0bd9 | |||
| 9b5b035e5e | |||
| d6f98f2db2 | |||
| c0013a4329 | |||
| 46774fb1eb | |||
| 327b38094a | |||
| ffe50c6cb0 | |||
| 6b7d61d7db | |||
| 6d8b5408bf | |||
| 408bf5ef12 | |||
| 0610c2193e | |||
| 866a1b4137 | |||
| 19c9c80c7d | |||
| 9aba19a738 | |||
| 616d2305cd | |||
| 7be326ba1b | |||
| f6ec4f3700 | |||
| ece2d4af18 | |||
| c22499bcc6 | |||
| c12198ac25 | |||
| 5a6ebd4156 | |||
| 0fdec5246b | |||
| bbed68e588 | |||
| 7ce7123dd9 | |||
| 3ceff11112 | |||
| 90c663c6cd | |||
| 51314b765d | |||
| 445cc29ef0 | |||
| ae5aba694f | |||
| 8e570b8fc2 | |||
| 14482d2cf5 | |||
| 49d5a81d02 | |||
| 3e980a84fd | |||
| af14f82a7f | |||
| ff4fa3581e | |||
| 2bef59e79b | |||
| 2e5b72cfef | |||
| 3aa638ea42 | |||
| edf2f897d1 | |||
| cacffadc61 | |||
| d7a26ab715 | |||
| 65dc9fe64b | |||
| 951f8f4e4b | |||
| f459c91631 | |||
| db6c9a05e0 | |||
| 93e2cda988 | |||
| b1bf1ccbdc | |||
| 47b75368af | |||
| aa7b1f5c76 | |||
| d49257d30c | |||
| e53408dc52 | |||
| ecb365f275 | |||
| 4be5999770 | |||
| f4e1a7b1f7 | |||
| c771da74d4 | |||
| 914417ee3e | |||
| a4f3656093 | |||
| 763b9e4827 | |||
| 1231bf6ce0 | |||
| ed8c6cd5ce | |||
| f76510ce26 | |||
| fa4eafa8e3 | |||
| 685991cd98 | |||
| 9a3d0c7099 | |||
| 67771564be | |||
| 5b67ca1a00 | |||
| 5d3d436105 | |||
| 40ff880ef0 | |||
| ead90e6862 | |||
| 92937617f5 | |||
| 4cf43fca20 | |||
| 525c8a6966 | |||
| 68473ad811 | |||
| 90ca7b8658 | |||
| 0871cd7492 | |||
| b3b79f42d5 | |||
| 42b6f365c5 | |||
| 7b042b2b7d | |||
| b5e4928780 | |||
| d1f9e80b5d | |||
| 175a3243a5 | |||
| e089c82781 | |||
| cf5008bec3 | |||
| ceae269ff1 | |||
| fac9b89f81 | |||
| 613c2af385 | |||
| 0c60a087f3 | |||
| 0f7f58aab4 | |||
| dd1612c763 | |||
| 4c42b16059 | |||
| 5a4548d426 | |||
| bb587d17d9 | |||
| c004124f85 | |||
| 9fd36d8820 | |||
| e72aee5558 | |||
| 63e81e33d3 | |||
| 8b8c385441 | |||
| cad96e24dd | |||
| c688fc67ef | |||
| 68c34fad86 | |||
| 96af51afce | |||
| f20fb1aec5 | |||
| f6d7b77e8e | |||
| ce6f78dd89 | |||
| 61ace00ab3 | |||
| 19800c66b8 | |||
| 69f931b298 | |||
| 2a15672b98 | |||
| 7e788e64b2 | |||
| 7e59e3b23f | |||
| 5f361846bb | |||
| c1c676d892 | |||
| 7240dc6ffe | |||
| 848a08f4b7 | |||
| 93c2ceae8c | |||
| 778c26e90c | |||
| 9e1d180e81 | |||
| 4c671418ee | |||
| 95bd012bbe | |||
| 4c1043f69a | |||
| 7df7da788b | |||
| beb781ac68 | |||
| b5e94f93b7 | |||
| d721ae7daf | |||
| 21494ffc6c | |||
| e1a11d0741 | |||
| 64faaca516 | |||
| 2451df3f1e | |||
| 54f9620fc5 | |||
| e4e2c0428d | |||
| 074d4cea00 | |||
| b17a3f2a92 | |||
| 5c327b7100 | |||
| e9e0457238 | |||
| 481df6ddcd | |||
| febbdcaf27 | |||
| b0e962d37f | |||
| d0af23d7c8 | |||
| 0f85c400d3 | |||
| 548d58cf8e | |||
| 35beec7104 | |||
| 63c6a6e4f4 | |||
| e5f42cb1f7 | |||
| 4a2f76503c | |||
| 0d65ef9ccc | |||
| 1a21409220 | |||
| 881db618ca | |||
| 08b16b3c4e | |||
| 8b94f8d886 | |||
| 1f64315092 | |||
| b27a51e031 | |||
| ae7f359f23 | |||
| 2cb777bfdc | |||
| 4b4c71c537 | |||
| dd3be7d4c9 | |||
| 3c3a24ff92 | |||
| b993e93a68 | |||
| 17fe09fff9 | |||
| cc4a65de19 | |||
| 47a996cf15 | |||
| e71ea1f3a9 | |||
| a4957c52fd | |||
| 848e395b72 | |||
| b3d79e675b | |||
| 8c6f42cbb8 | |||
| 40fb8896d8 | |||
| fe6bc0499e | |||
| b8a91bb06f | |||
| 1de9da4558 | |||
| aa46967d79 | |||
| 85fe231dcc | |||
| 06e9f576b2 | |||
| d0ce9523d3 | |||
| 05657bbe6c | |||
| 7b0e835168 | |||
| ad28c7707c | |||
| 4fc877afd0 | |||
| ea10ec7c44 | |||
| c4a127fcd6 | |||
| 129f502f28 | |||
| cd0ef93813 | |||
| 1ceac5ab0e | |||
| b5769093c4 | |||
| 73157f49d1 | |||
| 16a51edc2d | |||
| cd8cb7733a | |||
| 884b9100d1 | |||
| 4999c1d6ea | |||
| 981d956766 | |||
| 98c7fc1ee3 | |||
| 9434abfcf6 | |||
| f1acd11ffd | |||
| 3c252ff91e | |||
| 55cd07cf58 | |||
| 7051907cd5 | |||
| be02553c46 | |||
| 3a1f0ca2e1 | |||
| c5f3fe2b9c | |||
| 094ed5b4c7 | |||
| c7eb07468d | |||
| 7623c51e41 | |||
| 2c8ddc3295 | |||
| 7c2606287e | |||
| 82028756da | |||
| 8bd2bcacbc | |||
| dc74a09092 | |||
| 8f049641d7 | |||
| 2ee44238ec | |||
| 74b1b67b0d | |||
| 0e6f4abe27 | |||
| 10b6a4788f | |||
| 6430e67ddd | |||
| 6d38063e34 | |||
| ab08b89496 | |||
| 4f8caa85ec | |||
| 8b4d4743e9 | |||
| c2348ef17c | |||
| 78853237e0 | |||
| 87eacf161b | |||
| 24db283088 | |||
| d7d9b5b9a4 | |||
| d00682c56f | |||
| 0888e50219 | |||
| 4abd629e2f | |||
| c454b7881c | |||
| f0d7e830f5 | |||
| b539f0d2b0 | |||
| 6aa27e0271 | |||
| c4f0f354ae | |||
| 6cc0cca7a6 | |||
| e41ebcb3ae | |||
| 943ae5901f | |||
| de8661da31 | |||
| 93dfc8b2d6 | |||
| 1cea56cdd3 | |||
| 483f18ed96 | |||
| c4fa1ba55a | |||
| 0ef0ae3ca0 | |||
| a9376b50e6 | |||
| 8d57f871aa | |||
| bb4ff5a2ca | |||
| 62667ff2e7 | |||
| ea767b8615 | |||
| e2c33e576e | |||
| 80cea5e104 | |||
| 052c79b8eb | |||
| 218067b650 | |||
| a77a4549b8 | |||
| c7f18f21a4 | |||
| 8403b3d238 | |||
| e9ce954141 | |||
| 3467e1f1a5 | |||
| b67f18a64a | |||
| 9f46abc2b0 | |||
| 704492360d | |||
| 338b4d374d | |||
| 142739a24b | |||
| 4d0dd255b4 | |||
| 190bf34ac2 | |||
| 856e82dd95 | |||
| 3943b2863d | |||
| 4ab210cb4e | |||
| 8febcc40f8 | |||
| f05a75a15d | |||
| 1667e19910 | |||
| 1665c38e3a | |||
| a69de22d31 | |||
| b2f4c197ca | |||
| 8afaa83a4b | |||
| 3cbaae3695 | |||
| 1b20d83b82 | |||
| 4b85f8fdec | |||
| ed299916c5 | |||
| 0b2171d1dd | |||
| 46f376ba8b | |||
| bccf807b1c | |||
| fc000dae7a | |||
| f32e292764 | |||
| ca0de8162d | |||
| 11e2c98c64 | |||
| e8e5773005 | |||
| 74672f27da | |||
| 94c74ca22d | |||
| 445498eba1 | |||
| d790cfd3c0 | |||
| f94a77bf94 | |||
| bb8056c31a | |||
| 726b7c8055 | |||
| dd0bac4371 | |||
| b4cf57214f | |||
| 3d6552e3ce | |||
| 260128e2b8 | |||
| fa5776d4e3 | |||
| 9df845e5d7 | |||
| 4dec5b2b6e | |||
| b6ef32dbbc | |||
| 4501319c83 | |||
| bf712a8d1e | |||
| b881d552c0 | |||
| 8110bdd91e | |||
| 28703096cd | |||
| b5210f8c3f | |||
| 02e4264c0e | |||
| 35b8d124d0 | |||
| 1c559cf497 | |||
| 0547a83700 | |||
| c40d5d7203 | |||
| 95880f8d0a | |||
| 9f5e1de888 | |||
| 9c6f7eeca9 | |||
| 63be107768 | |||
| 1ebe4315c3 | |||
| a9e6bb012a | |||
| c6c8efd671 | |||
| fd59ee4b68 | |||
| 52ee77f6b5 | |||
| 0707629846 | |||
| da7636580c | |||
| 5c91e890ac | |||
| 2000edf105 | |||
| ba3d2f26ce | |||
| 0f8bc5de19 | |||
| ee622059d5 | |||
| f97236a5aa | |||
| 6ced5eb5d1 | |||
| 0debc14906 | |||
| a2daa9b918 | |||
| 70ec7e33ce | |||
| 323dda5a54 | |||
| 7ea5b221c5 | |||
| 4b4d9d2f2a | |||
| 2b3a7692e0 | |||
| 1bff66262a | |||
| 3a384291e5 | |||
| 613f430751 | |||
| dd41556d9f | |||
| f4f24eb41a | |||
| 2a78b0f9d8 | |||
| c11a3c0ffa | |||
| 58e3f08633 | |||
| c19fdd9871 | |||
| 71ceffd482 | |||
| f425e92309 | |||
| e427746fc0 | |||
| 90cfa481a1 | |||
| 7ede5c1d1e | |||
| 192a2e5a78 | |||
| 5dfc18d3f0 | |||
| f0eae492f7 | |||
| 1d171e71de | |||
| 16593d3a91 | |||
| 24c87e639e | |||
| 5619087429 | |||
| f333905fd0 | |||
| 339182611e | |||
| 73939c7567 | |||
| 8e77f212e5 | |||
| de7080619c | |||
| 5de1559110 | |||
| 1f19060719 | |||
| 3abf24a314 | |||
| 744f09eefb | |||
| 6a3963d903 | |||
| d873602182 | |||
| bca34c80d6 | |||
| 685d11d5d4 | |||
| 9b8ddbe29c | |||
| 3ca3e1fcfe | |||
| debfa35673 | |||
| 5e310d509c | |||
| 346815a3e0 | |||
| c92139b8a2 | |||
| f777218a6d | |||
| 4c3875e427 | |||
| 4f5b366af2 | |||
| c4eb84b6a3 | |||
| c338f4aca2 | |||
| ee2700872f | |||
| 3d3f093e3b | |||
| 25cf9c0f9e | |||
| de7620cea0 | |||
| 74486d27b4 | |||
| 3621aa88c8 | |||
| ef5a252337 | |||
| 5640378faa | |||
| 76689fa9b2 | |||
| 4094c42872 | |||
| 04fde2a98a | |||
| 47f04ffb4f | |||
| 7ddd198e03 | |||
| 11f489d06f | |||
| 02036f34e6 | |||
| 9ef3d792fe | |||
| 04780c2115 | |||
| 64e5ec3182 | |||
| 6ccca8343f | |||
| 538031171d | |||
| 316861f05c | |||
| 2ea0ab5c5f | |||
| 20c9519427 | |||
| 85acee47b1 | |||
| 08ddccdf51 | |||
| 3e50a1c5fb | |||
| 7e8c7814ed | |||
| c47fb9f48f | |||
| ca8ab015db | |||
| ff851e324c | |||
| 54a79b1433 | |||
| e2a2f69415 | |||
| 7a961d56cb | |||
| 009b23d1e3 | |||
| 7c10a490e0 | |||
| 0d7b413d8e | |||
| 31fb704c4c | |||
| bd1c7fd463 | |||
| 3681858d8d | |||
| b3a796ce26 | |||
| 7c775490d9 | |||
| 38f244ba7d | |||
| cbe6bcf747 | |||
| 14f28328f9 | |||
| fda9dd9b4c | |||
| 7f2fa9af9a | |||
| 50a01f1667 | |||
| e89106eacb | |||
| 74714ed2b5 | |||
| b9571c984f | |||
| 3d95856f6d | |||
| 6f0848b538 | |||
| 13316a61dc | |||
| 2eb079dfbf | |||
| 21ba0c3da3 | |||
| 68f5253bb9 | |||
| d48cc09ab2 | |||
| cc44d86cb4 | |||
| 8125cd8558 | |||
| 925f112863 | |||
| 756e11445b | |||
| 1b50b0e6d7 | |||
| e343e3adae | |||
| 338d049c5c | |||
| 64d7d5f82c | |||
| 6f46d74797 | |||
| 7b0665cb2c | |||
| 717b5eb95d | |||
| 236b9814f3 | |||
| df1d945339 | |||
| abbea47241 | |||
| 43d545c528 | |||
| 8a13a20cb1 | |||
| 93dbfd8d5d | |||
| 34505f9eca | |||
| 78b623fe0d | |||
| 5c6b21d79c | |||
| 7e40355d86 | |||
| 3acadbd656 | |||
| 74ccaecaee | |||
| 5851ef6866 | |||
| b70425e772 | |||
| bfed93353d | |||
| ec9d50414a | |||
| eeceb82bd1 | |||
| c5cd26a618 | |||
| 254b12503d | |||
| 1249a527c2 | |||
| 62919c8e40 | |||
| 944bdab5d8 | |||
| 3e402a50ba | |||
| 0e4ecbf06e | |||
| 2905cbf71a | |||
| e67fb65122 | |||
| ca4b5dae95 | |||
| d362bc8ba1 | |||
| 37ce611cef | |||
| d219960eea | |||
| 8fb03998c9 | |||
| 3534641faf |
+26
-4
@@ -1,12 +1,34 @@
|
||||
*.tar.bz2
|
||||
*.tar.gz
|
||||
*.7z
|
||||
*.pyc
|
||||
*.zip
|
||||
*.exe
|
||||
.idea
|
||||
.DS_Store
|
||||
*~
|
||||
NUL
|
||||
*.gcode
|
||||
|
||||
## Building result.
|
||||
build/*
|
||||
*.pyc
|
||||
*.exe
|
||||
*.o
|
||||
CuraEngine
|
||||
_bin
|
||||
_obj
|
||||
|
||||
## IDE project files.
|
||||
CuraEngine.layout
|
||||
CuraEngine.cbp
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
nbproject/*
|
||||
.idea
|
||||
*.depend
|
||||
*.gcode
|
||||
.*.swp
|
||||
|
||||
## Documentation.
|
||||
documentation/html/*
|
||||
documentation/latex/*
|
||||
|
||||
## Test results.
|
||||
tests/output.xml
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
project(CuraEngine)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
find_package(Arcus REQUIRED)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
endif()
|
||||
|
||||
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
|
||||
option(BUILD_TESTS OFF)
|
||||
|
||||
# Add a compiler flag to check the output for insane values if we are in debug mode.
|
||||
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Building debug release of CuraEngine.")
|
||||
add_definitions(-DASSERT_INSANE_OUTPUT)
|
||||
endif()
|
||||
|
||||
# Add warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
|
||||
|
||||
add_library(clipper STATIC libs/clipper/clipper.cpp)
|
||||
|
||||
set(engine_SRCS # Except main.cpp.
|
||||
src/bridge.cpp
|
||||
src/comb.cpp
|
||||
src/commandSocket.cpp
|
||||
src/FffGcodeWriter.cpp
|
||||
src/FffPolygonGenerator.cpp
|
||||
src/FffProcessor.cpp
|
||||
src/gcodeExport.cpp
|
||||
src/gcodePlanner.cpp
|
||||
src/infill.cpp
|
||||
src/inset.cpp
|
||||
src/layerPart.cpp
|
||||
src/LayerPlanBuffer.cpp
|
||||
src/MergeInfillLines.cpp
|
||||
src/mesh.cpp
|
||||
src/MeshGroup.cpp
|
||||
src/multiVolumes.cpp
|
||||
src/pathOrderOptimizer.cpp
|
||||
src/PrimeTower.cpp
|
||||
src/Progress.cpp
|
||||
src/raft.cpp
|
||||
src/settingRegistry.cpp
|
||||
src/settings.cpp
|
||||
src/skin.cpp
|
||||
src/skirt.cpp
|
||||
src/sliceDataStorage.cpp
|
||||
src/slicer.cpp
|
||||
src/support.cpp
|
||||
src/timeEstimate.cpp
|
||||
src/wallOverlap.cpp
|
||||
src/Weaver.cpp
|
||||
src/Wireframe2gcode.cpp
|
||||
|
||||
src/infill/NoZigZagConnectorProcessor.cpp
|
||||
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorEndPieces.cpp
|
||||
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
|
||||
|
||||
src/utils/gettime.cpp
|
||||
src/utils/LinearAlg2D.cpp
|
||||
src/utils/logoutput.cpp
|
||||
src/utils/polygonUtils.cpp
|
||||
src/utils/polygon.cpp
|
||||
)
|
||||
|
||||
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
|
||||
set(engine_TEST
|
||||
GCodePlannerTest
|
||||
LinearAlg2DTest
|
||||
)
|
||||
|
||||
# Generating ProtoBuf protocol.
|
||||
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
|
||||
|
||||
# Compiling CuraEngine itself.
|
||||
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
|
||||
target_link_libraries(_CuraEngine clipper Arcus)
|
||||
|
||||
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(_CuraEngine pthread)
|
||||
endif()
|
||||
add_executable(CuraEngine src/main.cpp) #Then compile main.cpp as separate executable, and link the library to it.
|
||||
target_link_libraries(CuraEngine _CuraEngine)
|
||||
|
||||
# Compiling the test environment.
|
||||
if (BUILD_TESTS)
|
||||
message(STATUS "Building tests...")
|
||||
enable_testing()
|
||||
foreach (test ${engine_TEST})
|
||||
add_executable(${test} tests/main.cpp tests/${test}.cpp)
|
||||
target_link_libraries(${test} _CuraEngine cppunit)
|
||||
add_test(${test} ${test})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Installing CuraEngine.
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
include(CPackConfig.cmake)
|
||||
@@ -0,0 +1,20 @@
|
||||
set(CPACK_PACKAGE_VENDOR "Ultimaker")
|
||||
set(CPACK_PACKAGE_CONTACT "Arjen Hiemstra <a.hiemstra@ultimaker.com>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cura Engine")
|
||||
set(CPACK_PACKAGE_VERSION "15.05.90")
|
||||
set(CPACK_GENERATOR "DEB")
|
||||
if(NOT DEFINED CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
|
||||
execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
set(DEB_DEPENDS
|
||||
"arcus (>= 15.05.90)"
|
||||
"protobuf (>= 3.0.0)"
|
||||
"libstdc++6 (>= 4.9.0)"
|
||||
"libgcc1 (>= 4.9.0)"
|
||||
)
|
||||
string(REPLACE ";" ", " DEB_DEPENDS "${DEB_DEPENDS}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_DEPENDS})
|
||||
|
||||
include(CPack)
|
||||
@@ -0,0 +1,14 @@
|
||||
Changelog CuraEngine
|
||||
====================
|
||||
|
||||
|
||||
|
||||
|
||||
- Feature: infill_wipe_dist. Add a travel move after an infill line in order to let it stick better to the walls.
|
||||
- Feature: Draft Protection Screen. A shell similar to the ooze shield providing protection from gusts of wind and acting similar to a heated chamber
|
||||
|
||||
|
||||
Release 15.06.01
|
||||
-----
|
||||
|
||||
- [Not documented]
|
||||
@@ -0,0 +1,81 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package cura.proto;
|
||||
|
||||
message ObjectList
|
||||
{
|
||||
repeated Object objects = 1;
|
||||
repeated Setting settings = 2;
|
||||
}
|
||||
|
||||
message Slice
|
||||
{
|
||||
repeated ObjectList object_lists = 1;
|
||||
}
|
||||
|
||||
message Object
|
||||
{
|
||||
int64 id = 1;
|
||||
bytes vertices = 2; //An array of 3 floats.
|
||||
bytes normals = 3; //An array of 3 floats.
|
||||
bytes indices = 4; //An array of ints.
|
||||
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
|
||||
}
|
||||
|
||||
message Progress
|
||||
{
|
||||
float amount = 1;
|
||||
}
|
||||
|
||||
message Layer {
|
||||
int32 id = 1;
|
||||
float height = 2;
|
||||
float thickness = 3;
|
||||
|
||||
repeated Polygon polygons = 4;
|
||||
}
|
||||
|
||||
message Polygon {
|
||||
enum Type {
|
||||
NoneType = 0;
|
||||
Inset0Type = 1;
|
||||
InsetXType = 2;
|
||||
SkinType = 3;
|
||||
SupportType = 4;
|
||||
SkirtType = 5;
|
||||
InfillType = 6;
|
||||
SupportInfillType = 7;
|
||||
MoveCombingType = 8;
|
||||
MoveRetractionType = 9;
|
||||
}
|
||||
Type type = 1;
|
||||
bytes points = 2;
|
||||
float line_width = 3;
|
||||
}
|
||||
|
||||
message GCodeLayer {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message ObjectPrintTime {
|
||||
int64 id = 1;
|
||||
float time = 2;
|
||||
float material_amount = 3;
|
||||
}
|
||||
|
||||
message SettingList {
|
||||
repeated Setting settings = 1;
|
||||
}
|
||||
|
||||
message Setting {
|
||||
string name = 1;
|
||||
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
message SlicingFinished {
|
||||
}
|
||||
+2304
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
-90
@@ -1,90 +0,0 @@
|
||||
#
|
||||
# Makefile for Cura_SteamEngine
|
||||
#
|
||||
|
||||
# simplest working invocation to compile it
|
||||
#g++ main.cpp modelFile/modelFile.cpp clipper/clipper.cpp -I. -o Cura_SteamEngine
|
||||
|
||||
CC=g++
|
||||
CFLAGS=-I. -c -Wall
|
||||
# also include debug symbols
|
||||
#CFLAGS+=-ggdb
|
||||
LDFLAGS=
|
||||
SOURCES=main.cpp modelFile/modelFile.cpp clipper/clipper.cpp
|
||||
OBJECTS=$(SOURCES:.cpp=.o)
|
||||
EXECUTABLE=Cura_SteamEngine
|
||||
UNAME := $(shell uname)
|
||||
|
||||
ifeq ($(UNAME), Linux)
|
||||
OPEN_HTML=firefox
|
||||
endif
|
||||
ifeq ($(UNAME), Darwin)
|
||||
OPEN_HTML=open
|
||||
#For MacOS force to build
|
||||
CFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
|
||||
LDFLAGS += -force_cpusubtype_ALL -mmacosx-version-min=10.6 -arch x86_64 -arch i386
|
||||
endif
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
|
||||
|
||||
.cpp.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
|
||||
## gcode: will run several tests and output the gCode and loads the first 100,000 lines into an HTML5 preview tool
|
||||
gcode: $(EXECUTABLE) httpServer
|
||||
./$(EXECUTABLE) --html
|
||||
rm -f html/gCodeViewer/output.gcode 2>/dev/null
|
||||
ln -f output.gcode html/gCodeViewer/
|
||||
$(OPEN_HTML) "http://localhost:8000/gCodeViewer/index.html?analyzeFile=output.gcode"
|
||||
|
||||
## layers: make 'layers' will output layer SVGs and loads it up in your browser.
|
||||
layers: $(EXECUTABLE)
|
||||
./$(EXECUTABLE) --html
|
||||
$(OPEN_HTML) output.html
|
||||
|
||||
## tests: will run several tests and output the gCode
|
||||
tests: $(EXECUTABLE) wolt testmodels klein
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/woltBaseline.stl
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/woltNotFlat.stl
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/wolt_scaled200Perc.stl
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/wolt_smoothingOn.stl
|
||||
#find ./testcase_models/*.stl -exec ./$(EXECUTABLE) {} \|\| echo ====== FAILED EXIT CODE $? ====== \;
|
||||
rm html/gCodeViewer/output.gcode 2>/dev/null || echo
|
||||
head -n 100000 output.gcode > html/gCodeViewer/output.gcode
|
||||
|
||||
## robot: will slice the robot
|
||||
robot: testmodels
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/ultirobot_Martijn.stl
|
||||
|
||||
## robot: will slice the klein bottle
|
||||
klein: testmodels
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/kleinbottle_Dizingof.stl
|
||||
|
||||
wolt:
|
||||
./Cura_SteamEngine --verbose -i ./testcase_models/wolt.stl
|
||||
|
||||
testmodels:
|
||||
@ls testcase_models/ultirobot_Martijn.stl 2>/dev/null || \
|
||||
( echo Fetching ultirobot_Martijn.stl... && \
|
||||
wget -O testcase_models/ultirobot_Martijn.stl "http://www.thingiverse.com/download:36866" )
|
||||
@ls testcase_models/kleinbottle_Dizingof.stl 2>/dev/null || \
|
||||
( echo Fetching klein bottle stl file... && \
|
||||
wget -O testcase_models/kleinbottle_Dizingof.stl http://www.thingiverse.com/download:69728 )
|
||||
|
||||
## clean stuff
|
||||
clean:
|
||||
rm -f $(EXECUTABLE) $(OBJECTS) output.html
|
||||
|
||||
clean_all: clean
|
||||
rm -f ./testcase_models/ultirobot_Martijn.stl kleinbottle_Dizingof.stl
|
||||
|
||||
httpServer:
|
||||
echo Starting simple webserver for online gCodeViewer...
|
||||
ps aux |grep SimpleHTTP 1>/dev/null || python -m SimpleHTTPServer 2>&1 1>/dev/null
|
||||
|
||||
help:
|
||||
@cat Makefile |grep \#\#| grep \: |cut -d\# -f3
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
CuraEngine
|
||||
==========
|
||||
The CuraEngine is a C++ console application for 3D printing GCode generation. It has been made as a better and faster alternative to the old Skeinforge engine.
|
||||
|
||||
The CuraEngine is pure C++ and uses Clipper from http://www.angusj.com/delphi/clipper.php
|
||||
Furthermore it depends on libArcus by Ultimaker, which can be found at http://github.com/Ultimaker/libArcus
|
||||
|
||||
This is just a console application for GCode generation. For a full graphical application look at https://github.com/Ultimaker/Cura which is the graphical frontend for CuraEngine.
|
||||
|
||||
The CuraEngine can be used seperately or in other applications. Feel free to add it to your application. But please take note of the License.
|
||||
|
||||
License
|
||||
=======
|
||||
CuraEngine is released under terms of the AGPLv3 License.
|
||||
Terms of the license can be found in the LICENSE file. Or at http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
But in general it boils down to: You need to share the source of any CuraEngine modifications if you make an application with the CuraEngine. (Even if you make a web-based slicer, you still need to share the source!)
|
||||
|
||||
How to Install
|
||||
==============
|
||||
1. Clone the repository from https://github.com/Ultimaker/CuraEngine.git (the URL at the right hand side of this page).
|
||||
2. Install Protobuf (see below)
|
||||
3. Install libArcus (see https://github.com/Ultimaker/libArcus)
|
||||
|
||||
In order to compile CuraEngine, either use CMake or start a project in your preferred IDE.
|
||||
CMake compilation:
|
||||
|
||||
1. Navigate to the CuraEngine directory and execute the following commands
|
||||
2. $ mkdir build && cd build
|
||||
3. $ cmake ..
|
||||
4. $ make
|
||||
|
||||
Project files generation:
|
||||
1. Navigate to the CuraEngine directory and execute the following commands
|
||||
2. cmake . -G "CodeBlocks - Unix Makefiles"
|
||||
3. (for a list of supported IDE's see http://www.cmake.org/Wiki/CMake_Generator_Specific_Information#Code::Blocks_Generator)
|
||||
|
||||
Installing Protobuf
|
||||
-------------------
|
||||
1. Be sure to have libtool installed.
|
||||
2. Download protobuf from https://github.com/google/protobuf/ (download ZIP and unZIP at desired location, or clone the repo) The protocol buffer is used for communication between the CuraEngine and the GUI.
|
||||
3. Before installing protobuf, change autogen.sh : comment line 18 to line 38 using '#'s. This removes the dependency on gtest-1.7.0.
|
||||
4. Run autogen.sh from the protobuf directory:
|
||||
$ ./autogen.sh
|
||||
5. $ ./configure
|
||||
6. $ make
|
||||
7. $ make install # Requires superused priviliges.
|
||||
8. (In case the shared library cannot be loaded, you can try "sudo ldconfig" on Linux systems)
|
||||
|
||||
Running
|
||||
=======
|
||||
Other than running CuraEngine from a frontend, such as Ultimaker/Cura, one can run CuraEngine from the command line.
|
||||
For that one needs a settings JSON file, which can be found in the Ultimaker/Cura repository.
|
||||
An example run for an UM2 machine looks as follows:
|
||||
* Navigate to the CuraEngine directory and execute the following
|
||||
```
|
||||
./build/CuraEngine slice -v -j ../Cura/resources/machines/dual_extrusion_printer.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
|
||||
```
|
||||
|
||||
Run `CuraEngine help` for a general description of how to use the CuraEngine tool.
|
||||
|
||||
Internals
|
||||
=========
|
||||
|
||||
The Cura Engine is structured as mainly .h files. This is not standard for a C++ project. However, using less cpp files makes the optimizer work harder and removes linking error issues. It's partialy a result of lazyness but comes in handy for optimalizations.
|
||||
|
||||
The .h files contain different steps called from the main.cpp file. The main.cpp file contains the global slicing logic.
|
||||
|
||||
The slicing process follows the following global steps:
|
||||
* Load 3D model
|
||||
* Analize and fix 3D model
|
||||
* Slice 3D model into 2D layers
|
||||
* Build LayerParts from sliced layers
|
||||
* Generate Insets
|
||||
* Generate up/down skins areas
|
||||
* Generate sparse infill areas
|
||||
* Generate GCode for each layer
|
||||
|
||||
Each step has more logic in it. But this is a general overview.
|
||||
All data for the engine is stored in the "SliceDataStorage". It's important to remember that only the data from the previous step is valid.
|
||||
|
||||
Coordinates are stored in 64bit integers as microns in the code. So if you see a value of 1000 then this mean 1mm of distance. This is because Clipper works on 64bit integers and microns give a high enough resolution without limiting the size too much. Note that there are some bits and pieces of code that need to be careful about 64bit overflows, especially calculating lengths sqrt(x*x+y*y) can cause overflows.
|
||||
|
||||
OptimizedModel
|
||||
==============
|
||||
The OptimizedModel is a 3D model stored with vertex<->face relations. This gives touching face relations which are used later on to slice into layers faster.
|
||||
|
||||
Slicer
|
||||
======
|
||||
While usually the whole GCode generation process is called 'slicing', the Slicer in the CuraEngine is the piece of code that generates layers. Each layer contains closed 2D polygons.
|
||||
These polygons are generated in a 2 step process. First all triangles are cut into lines per layer, for each layer a "line segment" is added to that layer.
|
||||
Next all these line-segments are connected to eachother to make Polygons. The vertex<->face relations of the OptimizedModel help to make this process fast, as there is a huge chance that 2 connecting faces also make 2 connecting line-segments.
|
||||
This code also patches up small holes in the 3D model, so your model doesn't need to be a perfect Manifold. It also deals with incorrect normals, so it can flip around line-segments to fit end-to-end.
|
||||
|
||||
After the Slicer we have closed Polygons which can be used in Clipper, as Clipper can only opperate on closed 2D polygons.
|
||||
|
||||
LayerParts
|
||||
==========
|
||||
An important concept to grasp is the idea of LayerParts. LayerParts are seperate parts inside a single layer. For example, in a solid cube each layer has a single LayerPart. However, in a table the layers which cover the legs have a LayerPart per leg, and thus there will be 4 LayerParts.
|
||||
A LayerPart is a seperated area inside a single layer which does not touch any other LayerParts. Most operations run on LayerParts as it reduces the amount of data to be processed. During GCode generation handling each LayerPart as a separate step makes sure you never travel between LayerParts which reduces the amount of external travel.
|
||||
LayerParts are generated after the Slicer step.
|
||||
|
||||
In order to generate the LayerParts, Clipper is used. A Clipper union with extended results gives a list of Polygons with holes in them. Each polygon is a LayerPart, and the holes are added to this LayerPart.
|
||||
|
||||
|
||||
Insets
|
||||
======
|
||||
Insets are also called "Perimeters" or "Loops" sometimes. Generating the insets is only a small bit of code, as Clipper does most of the heavy lifting.
|
||||
|
||||
Up/Down skin
|
||||
============
|
||||
The skin code generates the fully filled areas, it does this with some heavy boolean Clipper action. The skin step uses data from different layers to get the job done. Check the code for details.
|
||||
The sparse infill area code is almost the same as the skin code. With the difference that it keeps the other areas and uses different offsets.
|
||||
|
||||
Note that these steps generate the areas, not the actual infill lines. The infill line paths are generated later on. So the result of this step are lists of Polygons which are the areas that need to be filled.
|
||||
|
||||
GCode generation
|
||||
================
|
||||
The GCode generation is quite a large bit of code. As a lot is going on here. Important bits here are:
|
||||
* PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print.
|
||||
* Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used.
|
||||
* Comb: The combing code is the code that tries to avoid holes when moving the head around without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
|
||||
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
|
||||
@@ -1,29 +0,0 @@
|
||||
The Clipper code library, the "Software" (that includes Delphi, C++ & C#
|
||||
source code, accompanying samples and documentation), has been released
|
||||
under the following license, terms and conditions:
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,308 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Author : Angus Johnson *
|
||||
* Version : 4.9.7 *
|
||||
* Date : 29 November 2012 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2012 *
|
||||
* *
|
||||
* License: *
|
||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||
* http://www.boost.org/LICENSE_1_0.txt *
|
||||
* *
|
||||
* Attributions: *
|
||||
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
|
||||
* "A generic solution to polygon clipping" *
|
||||
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
|
||||
* http://portal.acm.org/citation.cfm?id=129906 *
|
||||
* *
|
||||
* Computer graphics and geometric modeling: implementation and algorithms *
|
||||
* By Max K. Agoston *
|
||||
* Springer; 1 edition (January 4, 2005) *
|
||||
* http://books.google.com/books?q=vatti+clipping+agoston *
|
||||
* *
|
||||
* See also: *
|
||||
* "Polygon Offsetting by Computing Winding Numbers" *
|
||||
* Paper no. DETC2005-85513 pp. 565-575 *
|
||||
* ASME 2005 International Design Engineering Technical Conferences *
|
||||
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
|
||||
* September 24–28, 2005 , Long Beach, California, USA *
|
||||
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef clipper_hpp
|
||||
#define clipper_hpp
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
|
||||
namespace ClipperLib {
|
||||
|
||||
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
|
||||
enum PolyType { ptSubject, ptClip };
|
||||
//By far the most widely used winding rules for polygon filling are
|
||||
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
|
||||
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
|
||||
//see http://glprogramming.com/red/chapter11.html
|
||||
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
|
||||
|
||||
typedef signed long long long64;
|
||||
typedef unsigned long long ulong64;
|
||||
|
||||
struct IntPoint {
|
||||
public:
|
||||
long64 X;
|
||||
long64 Y;
|
||||
IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
|
||||
friend std::ostream& operator <<(std::ostream &s, IntPoint &p);
|
||||
};
|
||||
|
||||
typedef std::vector< IntPoint > Polygon;
|
||||
typedef std::vector< Polygon > Polygons;
|
||||
|
||||
std::ostream& operator <<(std::ostream &s, Polygon &p);
|
||||
std::ostream& operator <<(std::ostream &s, Polygons &p);
|
||||
|
||||
struct ExPolygon {
|
||||
Polygon outer;
|
||||
Polygons holes;
|
||||
};
|
||||
typedef std::vector< ExPolygon > ExPolygons;
|
||||
|
||||
enum JoinType { jtSquare, jtRound, jtMiter };
|
||||
|
||||
bool Orientation(const Polygon &poly);
|
||||
double Area(const Polygon &poly);
|
||||
void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
|
||||
double delta, JoinType jointype = jtSquare, double MiterLimit = 2, bool CheckInputs = true);
|
||||
void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd);
|
||||
|
||||
void ReversePolygon(Polygon& p);
|
||||
void ReversePolygons(Polygons& p);
|
||||
|
||||
//used internally ...
|
||||
enum EdgeSide { esNeither = 0, esLeft = 1, esRight = 2, esBoth = 3 };
|
||||
enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
|
||||
|
||||
struct TEdge {
|
||||
long64 xbot;
|
||||
long64 ybot;
|
||||
long64 xcurr;
|
||||
long64 ycurr;
|
||||
long64 xtop;
|
||||
long64 ytop;
|
||||
double dx;
|
||||
long64 deltaX;
|
||||
long64 deltaY;
|
||||
long64 tmpX;
|
||||
PolyType polyType;
|
||||
EdgeSide side;
|
||||
int windDelta; //1 or -1 depending on winding direction
|
||||
int windCnt;
|
||||
int windCnt2; //winding count of the opposite polytype
|
||||
int outIdx;
|
||||
TEdge *next;
|
||||
TEdge *prev;
|
||||
TEdge *nextInLML;
|
||||
TEdge *nextInAEL;
|
||||
TEdge *prevInAEL;
|
||||
TEdge *nextInSEL;
|
||||
TEdge *prevInSEL;
|
||||
};
|
||||
|
||||
struct IntersectNode {
|
||||
TEdge *edge1;
|
||||
TEdge *edge2;
|
||||
IntPoint pt;
|
||||
IntersectNode *next;
|
||||
};
|
||||
|
||||
struct LocalMinima {
|
||||
long64 Y;
|
||||
TEdge *leftBound;
|
||||
TEdge *rightBound;
|
||||
LocalMinima *next;
|
||||
};
|
||||
|
||||
struct Scanbeam {
|
||||
long64 Y;
|
||||
Scanbeam *next;
|
||||
};
|
||||
|
||||
struct OutPt; //forward declaration
|
||||
|
||||
struct OutRec {
|
||||
int idx;
|
||||
bool isHole;
|
||||
OutRec *FirstLeft;
|
||||
OutRec *AppendLink;
|
||||
OutPt *pts;
|
||||
OutPt *bottomPt;
|
||||
OutPt *bottomFlag;
|
||||
EdgeSide sides;
|
||||
};
|
||||
|
||||
struct OutPt {
|
||||
int idx;
|
||||
IntPoint pt;
|
||||
OutPt *next;
|
||||
OutPt *prev;
|
||||
};
|
||||
|
||||
struct JoinRec {
|
||||
IntPoint pt1a;
|
||||
IntPoint pt1b;
|
||||
int poly1Idx;
|
||||
IntPoint pt2a;
|
||||
IntPoint pt2b;
|
||||
int poly2Idx;
|
||||
};
|
||||
|
||||
struct HorzJoinRec {
|
||||
TEdge *edge;
|
||||
int savedIdx;
|
||||
};
|
||||
|
||||
struct IntRect { long64 left; long64 top; long64 right; long64 bottom; };
|
||||
|
||||
typedef std::vector < OutRec* > PolyOutList;
|
||||
typedef std::vector < TEdge* > EdgeList;
|
||||
typedef std::vector < JoinRec* > JoinList;
|
||||
typedef std::vector < HorzJoinRec* > HorzJoinList;
|
||||
|
||||
//ClipperBase is the ancestor to the Clipper class. It should not be
|
||||
//instantiated directly. This class simply abstracts the conversion of sets of
|
||||
//polygon coordinates into edge objects that are stored in a LocalMinima list.
|
||||
class ClipperBase
|
||||
{
|
||||
public:
|
||||
ClipperBase();
|
||||
virtual ~ClipperBase();
|
||||
bool AddPolygon(const Polygon &pg, PolyType polyType);
|
||||
bool AddPolygons( const Polygons &ppg, PolyType polyType);
|
||||
virtual void Clear();
|
||||
IntRect GetBounds();
|
||||
protected:
|
||||
void DisposeLocalMinimaList();
|
||||
TEdge* AddBoundsToLML(TEdge *e);
|
||||
void PopLocalMinima();
|
||||
virtual void Reset();
|
||||
void InsertLocalMinima(LocalMinima *newLm);
|
||||
LocalMinima *m_CurrentLM;
|
||||
LocalMinima *m_MinimaList;
|
||||
bool m_UseFullRange;
|
||||
EdgeList m_edges;
|
||||
};
|
||||
|
||||
class Clipper : public virtual ClipperBase
|
||||
{
|
||||
public:
|
||||
Clipper();
|
||||
~Clipper();
|
||||
bool Execute(ClipType clipType,
|
||||
Polygons &solution,
|
||||
PolyFillType subjFillType = pftEvenOdd,
|
||||
PolyFillType clipFillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
ExPolygons &solution,
|
||||
PolyFillType subjFillType = pftEvenOdd,
|
||||
PolyFillType clipFillType = pftEvenOdd);
|
||||
void Clear();
|
||||
bool ReverseSolution() {return m_ReverseOutput;};
|
||||
void ReverseSolution(bool value) {m_ReverseOutput = value;};
|
||||
protected:
|
||||
void Reset();
|
||||
virtual bool ExecuteInternal(bool fixHoleLinkages);
|
||||
private:
|
||||
PolyOutList m_PolyOuts;
|
||||
JoinList m_Joins;
|
||||
HorzJoinList m_HorizJoins;
|
||||
ClipType m_ClipType;
|
||||
Scanbeam *m_Scanbeam;
|
||||
TEdge *m_ActiveEdges;
|
||||
TEdge *m_SortedEdges;
|
||||
IntersectNode *m_IntersectNodes;
|
||||
bool m_ExecuteLocked;
|
||||
PolyFillType m_ClipFillType;
|
||||
PolyFillType m_SubjFillType;
|
||||
bool m_ReverseOutput;
|
||||
void DisposeScanbeamList();
|
||||
void SetWindingCount(TEdge& edge);
|
||||
bool IsEvenOddFillType(const TEdge& edge) const;
|
||||
bool IsEvenOddAltFillType(const TEdge& edge) const;
|
||||
void InsertScanbeam(const long64 Y);
|
||||
long64 PopScanbeam();
|
||||
void InsertLocalMinimaIntoAEL(const long64 botY);
|
||||
void InsertEdgeIntoAEL(TEdge *edge);
|
||||
void AddEdgeToSEL(TEdge *edge);
|
||||
void CopyAELToSEL();
|
||||
void DeleteFromSEL(TEdge *e);
|
||||
void DeleteFromAEL(TEdge *e);
|
||||
void UpdateEdgeIntoAEL(TEdge *&e);
|
||||
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
|
||||
bool IsContributing(const TEdge& edge) const;
|
||||
bool IsTopHorz(const long64 XPos);
|
||||
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
||||
void DoMaxima(TEdge *e, long64 topY);
|
||||
void ProcessHorizontals();
|
||||
void ProcessHorizontal(TEdge *horzEdge);
|
||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
void AppendPolygon(TEdge *e1, TEdge *e2);
|
||||
void DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
|
||||
void DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
|
||||
void DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt);
|
||||
void IntersectEdges(TEdge *e1, TEdge *e2,
|
||||
const IntPoint &pt, const IntersectProtects protects);
|
||||
OutRec* CreateOutRec();
|
||||
void AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
void DisposeBottomPt(OutRec &outRec);
|
||||
void DisposeAllPolyPts();
|
||||
void DisposeOutRec(PolyOutList::size_type index);
|
||||
bool ProcessIntersections(const long64 botY, const long64 topY);
|
||||
void AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
void BuildIntersectList(const long64 botY, const long64 topY);
|
||||
void ProcessIntersectList();
|
||||
void ProcessEdgesAtTopOfScanbeam(const long64 topY);
|
||||
void BuildResult(Polygons& polys);
|
||||
void BuildResultEx(ExPolygons& polys);
|
||||
void SetHoleState(TEdge *e, OutRec *OutRec);
|
||||
void DisposeIntersectNodes();
|
||||
bool FixupIntersections();
|
||||
void FixupOutPolygon(OutRec &outRec);
|
||||
bool IsHole(TEdge *e);
|
||||
void FixHoleLinkage(OutRec *outRec);
|
||||
void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
|
||||
void ClearJoins();
|
||||
void AddHorzJoin(TEdge *e, int idx);
|
||||
void ClearHorzJoins();
|
||||
bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2);
|
||||
void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx);
|
||||
void JoinCommonEdges(bool fixHoleLinkages);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class clipperException : public std::exception
|
||||
{
|
||||
public:
|
||||
clipperException(const char* description): m_descr(description) {}
|
||||
virtual ~clipperException() throw() {}
|
||||
virtual const char* what() const throw() {return m_descr.c_str();}
|
||||
private:
|
||||
std::string m_descr;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} //ClipperLib namespace
|
||||
|
||||
#endif //clipper_hpp
|
||||
|
||||
|
||||
-182
@@ -1,182 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef COMB_H
|
||||
#define COMB_H
|
||||
|
||||
class Comb
|
||||
{
|
||||
private:
|
||||
Polygons& boundery;
|
||||
|
||||
int64_t* minX;
|
||||
int64_t* maxX;
|
||||
unsigned int* minIdx;
|
||||
unsigned int* maxIdx;
|
||||
|
||||
PointMatrix matrix;
|
||||
Point sp;
|
||||
Point ep;
|
||||
|
||||
bool preTest(Point startPoint, Point endPoint)
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
if (shorterThen(diff, 1500))
|
||||
return false;
|
||||
return collisionTest(startPoint, endPoint);
|
||||
}
|
||||
|
||||
bool collisionTest(Point startPoint, Point endPoint)
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
|
||||
matrix = PointMatrix(diff);
|
||||
sp = matrix.apply(startPoint);
|
||||
ep = matrix.apply(endPoint);
|
||||
|
||||
for(unsigned int n=0; n<boundery.size(); n++)
|
||||
{
|
||||
if (boundery[n].size() < 1)
|
||||
continue;
|
||||
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
|
||||
for(unsigned int i=0; i<boundery[n].size(); i++)
|
||||
{
|
||||
Point p1 = matrix.apply(boundery[n][i]);
|
||||
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
|
||||
{
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
|
||||
if (x > sp.X && x < ep.X)
|
||||
return true;
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void calcMinMax()
|
||||
{
|
||||
for(unsigned int n=0; n<boundery.size(); n++)
|
||||
{
|
||||
minX[n] = LLONG_MAX;
|
||||
maxX[n] = LLONG_MIN;
|
||||
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
|
||||
for(unsigned int i=0; i<boundery[n].size(); i++)
|
||||
{
|
||||
Point p1 = matrix.apply(boundery[n][i]);
|
||||
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
|
||||
{
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
|
||||
if (x >= sp.X && x <= ep.X)
|
||||
{
|
||||
if (x < minX[n]) { minX[n] = x; minIdx[n] = i; }
|
||||
if (x > maxX[n]) { maxX[n] = x; maxIdx[n] = i; }
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getPolygonAbove(int64_t x)
|
||||
{
|
||||
int64_t min = LLONG_MAX;
|
||||
unsigned int ret = UINT_MAX;
|
||||
for(unsigned int n=0; n<boundery.size(); n++)
|
||||
{
|
||||
if (minX[n] > x && minX[n] < min)
|
||||
{
|
||||
min = minX[n];
|
||||
ret = n;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Point getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx)
|
||||
{
|
||||
Point p0 = boundery[polygonNr][(idx > 0) ? (idx - 1) : (boundery[polygonNr].size() - 1)];
|
||||
Point p1 = boundery[polygonNr][idx];
|
||||
Point p2 = boundery[polygonNr][(idx < (boundery[polygonNr].size() - 1)) ? (idx + 1) : (0)];
|
||||
|
||||
Point off0 = crossZ(normal(p1 - p0, 1000));
|
||||
Point off1 = crossZ(normal(p2 - p1, 1000));
|
||||
Point n = normal(off0 + off1, 200);
|
||||
|
||||
return p1 + n;
|
||||
}
|
||||
|
||||
public:
|
||||
Comb(Polygons& _boundery)
|
||||
: boundery(_boundery)
|
||||
{
|
||||
minX = new int64_t[boundery.size()];
|
||||
maxX = new int64_t[boundery.size()];
|
||||
minIdx = new unsigned int[boundery.size()];
|
||||
maxIdx = new unsigned int[boundery.size()];
|
||||
}
|
||||
|
||||
~Comb()
|
||||
{
|
||||
delete minX;
|
||||
delete maxX;
|
||||
delete minIdx;
|
||||
delete maxIdx;
|
||||
}
|
||||
|
||||
bool calc(Point startPoint, Point endPoint, vector<Point>& combPoints)
|
||||
{
|
||||
//Check if we are crossing any bounderies, and pre-calculate some values.
|
||||
if (!preTest(startPoint, endPoint))
|
||||
{
|
||||
//We're not crossing any boundaries. So skip the comb generation.
|
||||
return true;
|
||||
}
|
||||
|
||||
calcMinMax();
|
||||
int64_t x = sp.X;
|
||||
vector<Point> pointList;
|
||||
while(true)
|
||||
{
|
||||
unsigned int n = getPolygonAbove(x);
|
||||
if (n == UINT_MAX) break;
|
||||
|
||||
pointList.push_back(matrix.unapply(Point(minX[n] - 200, sp.Y)));
|
||||
if ( (minIdx[n] - maxIdx[n] + boundery[n].size()) % boundery[n].size() > (maxIdx[n] - minIdx[n] + boundery[n].size()) % boundery[n].size())
|
||||
{
|
||||
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i < boundery[n].size() - 1) ? (i + 1) : (0))
|
||||
{
|
||||
pointList.push_back(getBounderyPointWithOffset(n, i));
|
||||
}
|
||||
}else{
|
||||
minIdx[n]--;
|
||||
if (minIdx[n] == UINT_MAX) minIdx[n] = boundery[n].size() - 1;
|
||||
maxIdx[n]--;
|
||||
if (maxIdx[n] == UINT_MAX) maxIdx[n] = boundery[n].size() - 1;
|
||||
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i > 0) ? (i - 1) : (boundery[n].size() - 1))
|
||||
{
|
||||
pointList.push_back(getBounderyPointWithOffset(n, i));
|
||||
}
|
||||
}
|
||||
pointList.push_back(matrix.unapply(Point(maxX[n] + 200, sp.Y)));
|
||||
|
||||
x = maxX[n];
|
||||
}
|
||||
pointList.push_back(endPoint);
|
||||
|
||||
Point p0 = startPoint;
|
||||
for(unsigned int n=1; n<pointList.size(); n++)
|
||||
{
|
||||
if (collisionTest(p0, pointList[n]))
|
||||
{
|
||||
if (collisionTest(p0, pointList[n-1]))
|
||||
return false;
|
||||
p0 = pointList[n-1];
|
||||
combPoints.push_back(p0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif//COMB_H
|
||||
Link simbólico
+1
@@ -0,0 +1 @@
|
||||
html/index.html
|
||||
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 18 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 20 KiB |
@@ -0,0 +1,12 @@
|
||||
Glossary
|
||||
========
|
||||
|
||||
|
||||
Term/Synonyms | Meaning
|
||||
--- | ---
|
||||
Extruder Train | The whole of a feeder, bowden tube and a nozzle
|
||||
Island/Part | isolated/unconnected part in 2D slice
|
||||
Inset | perimeter, the perimeters which are laid down around the infill
|
||||
Slicing | The act of extracting the contours of the object at a certain height (not the whole process which would also include gcode generation etc.)
|
||||
Tower (support) | A strut to reinforce parts of the support which would otherwise be unstable.
|
||||
Weaver / Neith / WirePrint | A non-layer-wise form of printing 'in thin air'
|
||||
@@ -0,0 +1,10 @@
|
||||
CuraEngine {#mainpage}
|
||||
=======
|
||||
|
||||
This is the documentation for CuraEngine, the back-end slicer of Cura.
|
||||
|
||||
[Overview](documentation/overview.md)
|
||||
|
||||
[Glossary](documentation/glossary.md)
|
||||
|
||||
[Code Conventions](documentation/code_conventions.md)
|
||||
@@ -0,0 +1,68 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
The Cura Engine is structured as mainly .h files. This is not standard for an C++ project. However, using less cpp files makes the optimizer work harder and removes linking error issues. It's partialy a result of lazyness but also for optimalizations.
|
||||
|
||||
The .h files contain different steps called from the main.cpp file. The main.cpp file contains the global slicing logic.
|
||||
|
||||
The slicing process follows the following global steps:
|
||||
* Load 3D model
|
||||
* Analize and fix 3D model
|
||||
* Slice 3D model into 2D layers
|
||||
* Build LayerParts from sliced layers
|
||||
* Generate Insets
|
||||
* Generate up/down skins areas
|
||||
* Generate sparse infill areas
|
||||
* Generate GCode for each layer
|
||||
|
||||
Each step has more logic in it. But this is a general overview.
|
||||
All data for the engine is stored in the "SliceDataStorage". It's important to remember that only the data from the previous step is valid.
|
||||
|
||||
Coordinates are stored in 64bit integers as microns in the code. So if you see a value of 1000 then this mean 1mm of distance. This is because Clipper works on 64bit integers and microns give a high enough resolution without limiting the size too much. Note that there are some bits and pieces of code that need to be careful about 64bit overflows, especially calculating lengths sqrt(x*x+y*y) can cause overflows.
|
||||
|
||||
OptimizedModel
|
||||
--------------
|
||||
The OptimizedModel is a 3D model stored with vertex<->face relations. This gives touching face relations which are used later on to slice into layers faster.
|
||||
|
||||
Slicer
|
||||
------
|
||||
While usually the whole GCode generation process is called Slicing. The slicer in the CuraEngine is the piece of code that generates layers. Each layer has closed 2D polygons.
|
||||
These polygons are generated in a 2 step process. First all triangles are cut into lines per layer, for each layer a "line segment" is added to that layer.
|
||||
Next all these line-segments are connected to eachother to make Polygons. The vertex<->face relations of the OptimizedModel help to make this process fast, as there is a huge chance that 2 connecting faces also make 2 connecting line-segments.
|
||||
This code also fixes up small holes in the 3D model, so your model doesn't need to be perfect Manifold. It also accounts for incorrect normals, so it can flip around line-segments to fit end-to-end.
|
||||
|
||||
After the Slicer we have closed Polygons which can be used in Clipper, as Clipper can only opperate on closed 2D polygons.
|
||||
|
||||
LayerParts
|
||||
----------
|
||||
An important concept to grasp is the LayerParts. LayerParts are seperate parts inside a single layer. For example, if you have a cube. Then each layer has a single LayerPart. However, if you have a table, then the layers which build the legs have a LayerPart per leg, and thus there will be 4 LayerParts.
|
||||
A LayerPart is a seperated area inside a single layer which does not touch any other LayerParts. Most operations run on LayerParts as it reduces the amount of data to process. During GCode generation handling each LayerPart as an own step makes sure you never travel between LayerParts and thus reducing the amount of external travel.
|
||||
LayerParts are generated after the Slicer step.
|
||||
|
||||
To generate the LayerParts Clipper is used. A Clipper union with extended results gives a list of Polygons with holes in them. Each polygon is a LayerPart, and the holes are added to this LayerPart.
|
||||
|
||||
Polygons
|
||||
--------
|
||||
Holes are polygons in counter-clockwise (or at-least, in the other direction) and the polygons are guaranteed to be "even-odd", so every crossing of polygon lines will switch from "fill" to "empty".
|
||||
|
||||
Insets
|
||||
------
|
||||
Insets are also called "Perimeters" or "Loops" sometimes. Generating the insets is only a small bit of code, as Clipper does most of the heavy lifting.
|
||||
|
||||
Up/Down skin
|
||||
------------
|
||||
The skin code generates the fully filled areas, it does this with some heavy boolean Clipper action. The skin step uses data from different layers to get the job done. Check the code for details.
|
||||
The sparse infill area code is almost the same as the skin code. With the difference that it keeps the other areas and uses different offsets.
|
||||
|
||||
Note that these steps generate the areas, not the actual infill lines. The infill line paths are generated later on. So the result of this step are list of Polygons which are the areas that need to be filled.
|
||||
|
||||
GCode generation
|
||||
----------------
|
||||
The GCode generation is quite a large bit of code. As a lot is going on here. Important bits here are:
|
||||
* PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print.
|
||||
* Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used.
|
||||
* Comb: The combing code is the code that tries to avoid holes when moving around the head without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
|
||||
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode, and to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
|
||||
-452
@@ -1,452 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef GCODEEXPORT_H
|
||||
#define GCODEEXPORT_H
|
||||
|
||||
class GCodeExport
|
||||
{
|
||||
private:
|
||||
FILE* f;
|
||||
double extrusionAmount;
|
||||
double extrusionPerMM;
|
||||
double retractionAmount;
|
||||
double extruderSwitchRetraction;
|
||||
Point3 currentPosition;
|
||||
Point extruderOffset[16];
|
||||
int currentSpeed, retractionSpeed;
|
||||
int zPos;
|
||||
bool isRetracted;
|
||||
int extruderNr;
|
||||
int currentFanSpeed;
|
||||
|
||||
double totalFilament;
|
||||
public:
|
||||
double totalPrintTime;
|
||||
|
||||
GCodeExport()
|
||||
: currentPosition(0,0,0)
|
||||
{
|
||||
extrusionAmount = 0;
|
||||
extrusionPerMM = 0;
|
||||
retractionAmount = 4.5;
|
||||
extruderSwitchRetraction = 14.5;
|
||||
extruderNr = 0;
|
||||
currentFanSpeed = -1;
|
||||
|
||||
totalPrintTime = 0.0;
|
||||
totalFilament = 0.0;
|
||||
|
||||
currentSpeed = 0;
|
||||
retractionSpeed = 45;
|
||||
isRetracted = false;
|
||||
memset(extruderOffset, 0, sizeof(extruderOffset));
|
||||
}
|
||||
|
||||
~GCodeExport()
|
||||
{
|
||||
if (f)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void setExtruderOffset(int id, Point p)
|
||||
{
|
||||
extruderOffset[id] = p;
|
||||
}
|
||||
|
||||
void setFilename(const char* filename)
|
||||
{
|
||||
f = fopen(filename, "w");
|
||||
}
|
||||
|
||||
bool isValid()
|
||||
{
|
||||
return f != NULL;
|
||||
}
|
||||
|
||||
void setExtrusion(int layerThickness, int filamentDiameter, int flow)
|
||||
{
|
||||
double filamentArea = M_PI * (double(filamentDiameter) / 1000.0 / 2.0) * (double(filamentDiameter) / 1000.0 / 2.0);
|
||||
extrusionPerMM = double(layerThickness) / 1000.0 / filamentArea * double(flow) / 100.0;
|
||||
}
|
||||
|
||||
void setRetractionSettings(int retractionAmount, int retractionSpeed)
|
||||
{
|
||||
this->retractionAmount = double(retractionAmount) / 1000.0;
|
||||
this->retractionSpeed = retractionSpeed;
|
||||
}
|
||||
|
||||
void setZ(int z)
|
||||
{
|
||||
this->zPos = z;
|
||||
}
|
||||
|
||||
Point getPositionXY()
|
||||
{
|
||||
return Point(currentPosition.x, currentPosition.y);
|
||||
}
|
||||
|
||||
int getPositionZ()
|
||||
{
|
||||
return currentPosition.z;
|
||||
}
|
||||
|
||||
int getExtruderNr()
|
||||
{
|
||||
return extruderNr;
|
||||
}
|
||||
|
||||
double getTotalFilamentUsed()
|
||||
{
|
||||
return totalFilament + extrusionAmount;
|
||||
}
|
||||
|
||||
double getTotalPrintTime()
|
||||
{
|
||||
return totalPrintTime;
|
||||
}
|
||||
|
||||
void addComment(const char* comment, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, comment);
|
||||
fprintf(f, ";");
|
||||
vfprintf(f, comment, args);
|
||||
fprintf(f, "\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void addLine(const char* line, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, line);
|
||||
vfprintf(f, line, args);
|
||||
fprintf(f, "\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void resetExtrusionValue()
|
||||
{
|
||||
if (extrusionAmount != 0.0)
|
||||
{
|
||||
fprintf(f, "G92 E0\n");
|
||||
totalFilament += extrusionAmount;
|
||||
extrusionAmount = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void addDelay(double timeAmount)
|
||||
{
|
||||
fprintf(f, "G4 P%d\n", int(timeAmount * 1000));
|
||||
}
|
||||
|
||||
void addMove(Point p, int speed, int lineWidth)
|
||||
{
|
||||
if (lineWidth != 0)
|
||||
{
|
||||
Point diff = p - getPositionXY();
|
||||
if (isRetracted)
|
||||
{
|
||||
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount);
|
||||
currentSpeed = retractionSpeed;
|
||||
isRetracted = false;
|
||||
//}else if (shorterThen(diff, 50)){
|
||||
// return;
|
||||
}
|
||||
extrusionAmount += extrusionPerMM * double(lineWidth) / 1000.0 * vSizeMM(diff);
|
||||
fprintf(f, "G1");
|
||||
}else{
|
||||
fprintf(f, "G0");
|
||||
}
|
||||
|
||||
if (currentSpeed != speed)
|
||||
{
|
||||
fprintf(f, " F%i", speed * 60);
|
||||
currentSpeed = speed;
|
||||
}
|
||||
fprintf(f, " X%0.2f Y%0.2f", float(p.X - extruderOffset[extruderNr].X)/1000, float(p.Y - extruderOffset[extruderNr].Y)/1000);
|
||||
if (zPos != currentPosition.z)
|
||||
fprintf(f, " Z%0.2f", float(zPos)/1000);
|
||||
if (lineWidth != 0)
|
||||
fprintf(f, " E%0.5lf", extrusionAmount);
|
||||
fprintf(f, "\n");
|
||||
|
||||
currentPosition = Point3(p.X, p.Y, zPos);
|
||||
}
|
||||
|
||||
void addRetraction()
|
||||
{
|
||||
if (retractionAmount > 0 && !isRetracted)
|
||||
{
|
||||
fprintf(f, "G1 F%i E%0.5lf\n", retractionSpeed * 60, extrusionAmount - retractionAmount);
|
||||
currentSpeed = retractionSpeed;
|
||||
isRetracted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void switchExtruder(int newExtruder)
|
||||
{
|
||||
if (extruderNr == newExtruder)
|
||||
return;
|
||||
|
||||
extruderNr = newExtruder;
|
||||
|
||||
fprintf(f, "G1 F%i E%0.4lf\n", retractionSpeed * 60, extrusionAmount - extruderSwitchRetraction);
|
||||
currentSpeed = retractionSpeed;
|
||||
isRetracted = true;
|
||||
fprintf(f, "T%i\n", extruderNr);
|
||||
}
|
||||
|
||||
void addCode(const char* str)
|
||||
{
|
||||
fprintf(f, "%s\n", str);
|
||||
}
|
||||
|
||||
void addFanCommand(int speed)
|
||||
{
|
||||
if (currentFanSpeed == speed)
|
||||
return;
|
||||
if (speed > 0)
|
||||
fprintf(f, "M106 S%d\n", speed * 255 / 100);
|
||||
else
|
||||
fprintf(f, "M107\n");
|
||||
currentFanSpeed = speed;
|
||||
}
|
||||
|
||||
int getFileSize(){
|
||||
return ftell(f);
|
||||
}
|
||||
void tellFileSize() {
|
||||
float fsize = (float) ftell(f);
|
||||
if(fsize > 1024*1024) {
|
||||
fsize /= 1024.0*1024.0;
|
||||
fprintf(stdout, "Wrote %5.1f MB.\n",fsize);
|
||||
}
|
||||
if(fsize > 1024) {
|
||||
fsize /= 1024.0;
|
||||
fprintf(stdout, "Wrote %5.1f kilobytes.\n",fsize);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class GCodePathConfig
|
||||
{
|
||||
public:
|
||||
int speed;
|
||||
int lineWidth;
|
||||
const char* name;
|
||||
|
||||
GCodePathConfig(int speed, int lineWidth, const char* name)
|
||||
: speed(speed), lineWidth(lineWidth), name(name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class GCodePath
|
||||
{
|
||||
public:
|
||||
GCodePathConfig* config;
|
||||
bool retract;
|
||||
int extruder;
|
||||
vector<Point> points;
|
||||
};
|
||||
|
||||
class GCodePlanner
|
||||
{
|
||||
private:
|
||||
GCodeExport& gcode;
|
||||
|
||||
Point lastPosition;
|
||||
vector<GCodePath> paths;
|
||||
Comb* comb;
|
||||
|
||||
GCodePathConfig moveConfig;
|
||||
int speedFactor;
|
||||
int currentExtruder;
|
||||
double extraTime;
|
||||
double totalPrintTime;
|
||||
private:
|
||||
GCodePath* getLatestPathWithConfig(GCodePathConfig* config)
|
||||
{
|
||||
if (paths.size() > 0 && paths[paths.size()-1].config == config)
|
||||
{
|
||||
return &paths[paths.size()-1];
|
||||
}
|
||||
paths.push_back(GCodePath());
|
||||
GCodePath* ret = &paths[paths.size()-1];
|
||||
ret->retract = false;
|
||||
ret->config = config;
|
||||
ret->extruder = currentExtruder;
|
||||
return ret;
|
||||
}
|
||||
public:
|
||||
GCodePlanner(GCodeExport& gcode, int moveSpeed)
|
||||
: gcode(gcode), moveConfig(moveSpeed, 0, "move")
|
||||
{
|
||||
lastPosition = gcode.getPositionXY();
|
||||
comb = NULL;
|
||||
speedFactor = 100;
|
||||
extraTime = 0.0;
|
||||
totalPrintTime = 0.0;
|
||||
currentExtruder = gcode.getExtruderNr();
|
||||
}
|
||||
~GCodePlanner()
|
||||
{
|
||||
if (comb)
|
||||
delete comb;
|
||||
}
|
||||
void setExtruder(int extruder)
|
||||
{
|
||||
currentExtruder = extruder;
|
||||
}
|
||||
|
||||
void setCombBoundary(Polygons* polygons)
|
||||
{
|
||||
if (comb)
|
||||
delete comb;
|
||||
if (polygons)
|
||||
comb = new Comb(*polygons);
|
||||
else
|
||||
comb = NULL;
|
||||
}
|
||||
|
||||
void setSpeedFactor(int speedFactor)
|
||||
{
|
||||
if (speedFactor < 1) speedFactor = 1;
|
||||
this->speedFactor = speedFactor;
|
||||
}
|
||||
int getSpeedFactor()
|
||||
{
|
||||
return this->speedFactor;
|
||||
}
|
||||
|
||||
void addMove(Point p)
|
||||
{
|
||||
GCodePath* path = getLatestPathWithConfig(&moveConfig);
|
||||
if (comb != NULL)
|
||||
{
|
||||
vector<Point> pointList;
|
||||
if (comb->calc(lastPosition, p, pointList))
|
||||
{
|
||||
for(unsigned int n=0; n<pointList.size(); n++)
|
||||
{
|
||||
path->points.push_back(pointList[n]);
|
||||
}
|
||||
}else{
|
||||
path->retract = true;
|
||||
}
|
||||
}
|
||||
path->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void addExtrusionMove(Point p, GCodePathConfig* config)
|
||||
{
|
||||
getLatestPathWithConfig(config)->points.push_back(p);
|
||||
lastPosition = p;
|
||||
}
|
||||
|
||||
void addPolygon(ClipperLib::Polygon& polygon, int startIdx, GCodePathConfig* config)
|
||||
{
|
||||
Point p0 = polygon[startIdx];
|
||||
addMove(p0);
|
||||
for(unsigned int i=1; i<polygon.size(); i++)
|
||||
{
|
||||
Point p1 = polygon[(startIdx + i) % polygon.size()];
|
||||
addExtrusionMove(p1, config);
|
||||
p0 = p1;
|
||||
}
|
||||
if (polygon.size() > 2)
|
||||
addExtrusionMove(polygon[startIdx], config);
|
||||
}
|
||||
|
||||
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config)
|
||||
{
|
||||
PathOptimizer orderOptimizer(lastPosition);
|
||||
for(unsigned int i=0;i<polygons.size();i++)
|
||||
orderOptimizer.addPolygon(polygons[i]);
|
||||
orderOptimizer.optimize();
|
||||
for(unsigned int i=0;i<orderOptimizer.polyOrder.size();i++)
|
||||
{
|
||||
int nr = orderOptimizer.polyOrder[i];
|
||||
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config);
|
||||
}
|
||||
}
|
||||
|
||||
void forceMinimalLayerTime(double minTime, int minimalSpeed)
|
||||
{
|
||||
Point p0 = gcode.getPositionXY();
|
||||
double totalTime = 0.0;
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
totalTime += vSizeMM(p0 - path->points[i]) / double(path->config->speed);
|
||||
p0 = path->points[i];
|
||||
}
|
||||
}
|
||||
if (totalTime < minTime)
|
||||
{
|
||||
double factor = totalTime / minTime;
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
int speed = path->config->speed * factor;
|
||||
if (speed < minimalSpeed)
|
||||
factor = double(minimalSpeed) / double(path->config->speed);
|
||||
}
|
||||
setSpeedFactor(factor * 100);
|
||||
|
||||
if (minTime - (totalTime / factor) > 0.1)
|
||||
{
|
||||
//TODO: Use up this extra time (circle around the print?)
|
||||
this->extraTime = minTime - (totalTime / factor);
|
||||
}
|
||||
this->totalPrintTime = totalTime * factor;
|
||||
}else{
|
||||
this->totalPrintTime = totalTime;
|
||||
}
|
||||
}
|
||||
|
||||
void writeGCode(bool liftHeadIfNeeded)
|
||||
{
|
||||
GCodePathConfig* lastConfig = NULL;
|
||||
int extruder = gcode.getExtruderNr();
|
||||
for(unsigned int n=0; n<paths.size(); n++)
|
||||
{
|
||||
GCodePath* path = &paths[n];
|
||||
if (extruder != path->extruder)
|
||||
{
|
||||
extruder = path->extruder;
|
||||
gcode.switchExtruder(extruder);
|
||||
}else if (path->retract)
|
||||
{
|
||||
gcode.addRetraction();
|
||||
}
|
||||
if (path->config != &moveConfig && lastConfig != path->config)
|
||||
{
|
||||
gcode.addComment("TYPE:%s", path->config->name);
|
||||
lastConfig = path->config;
|
||||
}
|
||||
int speed = path->config->speed * speedFactor / 100;
|
||||
for(unsigned int i=0; i<path->points.size(); i++)
|
||||
{
|
||||
gcode.addMove(path->points[i], speed, path->config->lineWidth);
|
||||
}
|
||||
}
|
||||
|
||||
gcode.totalPrintTime += this->totalPrintTime;
|
||||
if (liftHeadIfNeeded && extraTime > 0.0)
|
||||
{
|
||||
gcode.totalPrintTime += extraTime;
|
||||
|
||||
gcode.addComment("Small layer, adding delay of %f", extraTime);
|
||||
gcode.addRetraction();
|
||||
gcode.setZ(gcode.getPositionZ() + 3000);
|
||||
gcode.addMove(gcode.getPositionXY(), moveConfig.speed, 0);
|
||||
gcode.addMove(gcode.getPositionXY() - Point(-20000, 0), moveConfig.speed, 0);
|
||||
gcode.addDelay(extraTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif//GCODEEXPORT_H
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef INFILL_H
|
||||
#define INFILL_H
|
||||
|
||||
void generateConcentricInfill(Polygons outline, Polygons& result, int offsets[], int offsetsSize)
|
||||
{
|
||||
int step = 0;
|
||||
while(1)
|
||||
{
|
||||
for(unsigned int polygonNr=0; polygonNr<outline.size(); polygonNr++)
|
||||
result.push_back(outline[polygonNr]);
|
||||
ClipperLib::OffsetPolygons(outline, outline, -offsets[step], ClipperLib::jtSquare, 2, false);
|
||||
if (outline.size() < 1)
|
||||
break;
|
||||
step = (step + 1) % offsetsSize;
|
||||
}
|
||||
}
|
||||
|
||||
int compare_int64_t(const void* a, const void* b)
|
||||
{
|
||||
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
|
||||
if (n < 0) return -1;
|
||||
if (n > 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void generateLineInfill(Polygons outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
|
||||
{
|
||||
ClipperLib::OffsetPolygons(outline, outline, extrusionWidth * infillOverlap / 100, ClipperLib::jtSquare, 2, false);
|
||||
PointMatrix matrix(rotation);
|
||||
|
||||
matrix.apply(outline);
|
||||
|
||||
AABB boundary(outline);
|
||||
|
||||
int lineCount = (boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing;
|
||||
vector<int64_t> cutList[lineCount];
|
||||
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
|
||||
{
|
||||
Point p1 = outline[polyNr][outline[polyNr].size()-1];
|
||||
for(unsigned int i=0; i < outline[polyNr].size(); i++)
|
||||
{
|
||||
Point p0 = outline[polyNr][i];
|
||||
int idx0 = (p0.X - boundary.min.X) / lineSpacing;
|
||||
int idx1 = (p1.X - boundary.min.X) / lineSpacing;
|
||||
int64_t xMin = p0.X, xMax = p1.X;
|
||||
if (p0.X > p1.X) { xMin = p1.X; xMax = p0.X; }
|
||||
if (idx0 > idx1) { int tmp = idx0; idx0 = idx1; idx1 = tmp; }
|
||||
for(int idx = idx0; idx<=idx1; idx++)
|
||||
{
|
||||
int x = (idx * lineSpacing) + boundary.min.X + lineSpacing / 2;
|
||||
if (x < xMin) continue;
|
||||
if (x >= xMax) continue;
|
||||
int y = p0.Y + (p1.Y - p0.Y) * (x - p0.X) / (p1.X - p0.X);
|
||||
cutList[idx].push_back(y);
|
||||
}
|
||||
p1 = p0;
|
||||
}
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
for(int64_t x = boundary.min.X + lineSpacing / 2; x < boundary.max.X; x += lineSpacing)
|
||||
{
|
||||
qsort(cutList[idx].data(), cutList[idx].size(), sizeof(int64_t), compare_int64_t);
|
||||
for(unsigned int i = 0; i + 1 < cutList[idx].size(); i+=2)
|
||||
{
|
||||
//if (cutList[idx][i+1] - cutList[idx][i] < extrusionWidth / 2) continue;
|
||||
ClipperLib::Polygon p;
|
||||
p.push_back(matrix.unapply(Point(x, cutList[idx][i])));
|
||||
p.push_back(matrix.unapply(Point(x, cutList[idx][i+1])));
|
||||
result.push_back(p);
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif//INFILL_H
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef INSET_H
|
||||
#define INSET_H
|
||||
|
||||
void generateInsets(SliceLayerPart* part, int offset, int insetCount)
|
||||
{
|
||||
for(int i=0; i<insetCount; i++)
|
||||
{
|
||||
part->insets.push_back(Polygons());
|
||||
ClipperLib::OffsetPolygons(part->outline, part->insets[i], -offset * i - offset/2, ClipperLib::jtSquare, 2, false);
|
||||
optimizePolygons(part->insets[i]);
|
||||
if (part->insets[i].size() < 1)
|
||||
{
|
||||
part->insets.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generateInsets(SliceLayer* layer, int offset, int insetCount)
|
||||
{
|
||||
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
|
||||
{
|
||||
generateInsets(&layer->parts[partNr], offset, insetCount);
|
||||
}
|
||||
|
||||
//Remove the parts which did not generate an inset. As these parts are too small to print,
|
||||
// and later code can now assume that there is always minimal 1 inset line.
|
||||
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
|
||||
{
|
||||
if (layer->parts[partNr].insets.size() < 1)
|
||||
{
|
||||
layer->parts.erase(layer->parts.begin() + partNr);
|
||||
partNr -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif//INSET_H
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef LAYERPART_H
|
||||
#define LAYERPART_H
|
||||
|
||||
/*
|
||||
The layer-part creation step is the first step in creating actual useful data for 3D printing.
|
||||
It takes the result of the Slice step, which is an unordered list of polygons, and makes groups of polygons,
|
||||
each of these groups is called a "part", which sometimes are also known as "islands". These parts represent
|
||||
isolated areas in the 2D layer with possible holes.
|
||||
|
||||
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
|
||||
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
|
||||
|
||||
It's also the first step that stores the result in the "data storage" so all other steps can access it.
|
||||
*/
|
||||
|
||||
|
||||
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool fixHorrible)
|
||||
{
|
||||
ClipperLib::Polygons polyList;
|
||||
for(unsigned int i=0; i<layer->polygonList.size(); i++)
|
||||
{
|
||||
ClipperLib::Polygon p;
|
||||
p.push_back(layer->polygonList[i].points[0]);
|
||||
for(unsigned int j=1; j<layer->polygonList[i].points.size(); j++)
|
||||
{
|
||||
p.push_back(layer->polygonList[i].points[j]);
|
||||
}
|
||||
if (fixHorrible && ClipperLib::Orientation(p))
|
||||
ClipperLib::ReversePolygon(p);
|
||||
polyList.push_back(p);
|
||||
}
|
||||
|
||||
ClipperLib::ExPolygons resultPolys;
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPolygons(polyList, ClipperLib::ptSubject);
|
||||
if (fixHorrible)
|
||||
clipper.Execute(ClipperLib::ctUnion, resultPolys, ClipperLib::pftNonZero);
|
||||
else
|
||||
clipper.Execute(ClipperLib::ctUnion, resultPolys);
|
||||
|
||||
for(unsigned int i=0; i<resultPolys.size(); i++)
|
||||
{
|
||||
storageLayer.parts.push_back(SliceLayerPart());
|
||||
|
||||
storageLayer.parts[i].outline.push_back(resultPolys[i].outer);
|
||||
for(unsigned int j=0; j<resultPolys[i].holes.size(); j++)
|
||||
{
|
||||
storageLayer.parts[i].outline.push_back(resultPolys[i].holes[j]);
|
||||
}
|
||||
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
|
||||
}
|
||||
}
|
||||
|
||||
void createLayerParts(SliceVolumeStorage& storage, Slicer* slicer, bool fixHorrible)
|
||||
{
|
||||
for(unsigned int layerNr = 0; layerNr < slicer->layers.size(); layerNr++)
|
||||
{
|
||||
storage.layers.push_back(SliceLayer());
|
||||
createLayerWithParts(storage.layers[layerNr], &slicer->layers[layerNr], fixHorrible);
|
||||
//LayerPartsLayer(&slicer->layers[layerNr])
|
||||
}
|
||||
}
|
||||
|
||||
void dumpLayerparts(SliceDataStorage& storage, const char* filename)
|
||||
{
|
||||
FILE* out = fopen(filename, "w");
|
||||
fprintf(out, "<!DOCTYPE html><html><body>");
|
||||
Point3 modelSize = storage.modelSize;
|
||||
Point3 modelMin = storage.modelMin;
|
||||
|
||||
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
|
||||
{
|
||||
for(unsigned int layerNr=0;layerNr<storage.volumes[volumeIdx].layers.size(); layerNr++)
|
||||
{
|
||||
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 150px; height:150px\">\n");
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
|
||||
for(unsigned int i=0;i<layer->parts.size();i++)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[i];
|
||||
for(unsigned int j=0;j<part->outline.size();j++)
|
||||
{
|
||||
fprintf(out, "<polygon points=\"");
|
||||
for(unsigned int k=0;k<part->outline[j].size();k++)
|
||||
{
|
||||
fprintf(out, "%f,%f ", float(part->outline[j][k].X - modelMin.x)/modelSize.x*150, float(part->outline[j][k].Y - modelMin.y)/modelSize.y*150);
|
||||
}
|
||||
if (j == 0)
|
||||
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
|
||||
else
|
||||
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
|
||||
}
|
||||
}
|
||||
fprintf(out, "</svg>\n");
|
||||
}
|
||||
}
|
||||
fprintf(out, "</body></html>");
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
#endif//LAYERPART_H
|
||||
@@ -0,0 +1,3 @@
|
||||
These libraries are extern, and while they're utilized by this project,
|
||||
they are in fact not actually developed on this repository.
|
||||
If changes are desired for these projects, please refer to their official repositories.
|
||||
@@ -0,0 +1,26 @@
|
||||
The Clipper Library (including Delphi, C++ & C# source code, other accompanying
|
||||
code, examples and documentation), hereafter called "the Software", has been
|
||||
released under the following license, terms and conditions:
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the Software covered by this license to use, reproduce,
|
||||
display, distribute, execute, and transmit the Software, and to prepare
|
||||
derivative works of the Software, and to permit third-parties to whom the
|
||||
Software is furnished to do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including the
|
||||
above license grant, this restriction and the following disclaimer, must be
|
||||
included in all copies of the Software, in whole or in part, and all derivative
|
||||
works of the Software, unless such copies or derivative works are solely in the
|
||||
form of machine-executable object code generated by a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,265 +1,376 @@
|
||||
============================================================
|
||||
Clipper Change Log
|
||||
============================================================
|
||||
|
||||
v4.9.7 - 29 November 2012
|
||||
* Bugfix: Bug that very rarely returned the wrong polygon
|
||||
orientation.
|
||||
* Bugfix: Obscure bug affecting OffsetPolygons when using
|
||||
jtRound for the JoinType parameter and when polygons also
|
||||
contain very large coordinate values (> +/-100000000000).
|
||||
|
||||
v4.9.6 - 9 November 2012
|
||||
* Bugfix: Another obscure bug related to joining polygons.
|
||||
|
||||
v4.9.4 - 2 November 2012
|
||||
* Bugfix: Bugs in Int128 class occasionally causing
|
||||
wrong orientations.
|
||||
* Bugfix: Further fixes related to joining polygons.
|
||||
|
||||
v4.9.0 - 9 October 2012
|
||||
* Bugfix: Obscure bug related to joining polygons.
|
||||
|
||||
v4.8.9 - 25 September 2012
|
||||
* Bugfix: Obscure bug related to precision of intersections.
|
||||
|
||||
v4.8.8 - 30 August 2012
|
||||
* Bugfix: Fixed bug in OffsetPolygons function introduced in
|
||||
version 4.8.5.
|
||||
|
||||
v4.8.7 - 24 August 2012
|
||||
* Bugfix: ReversePolygon function in C++ translation was broken.
|
||||
* Bugfix: Two obscure bugs affecting orientation fixed too.
|
||||
|
||||
v4.8.6 - 11 August 2012
|
||||
* Bugfix: Potential for memory overflow errors when using
|
||||
ExPolygons structure.
|
||||
* Bugfix: The polygon coordinate range has been reduced to
|
||||
+/- 0x3FFFFFFFFFFFFFFF (4.6e18).
|
||||
* Update: ReversePolygons function was misnamed ReversePoints in C++.
|
||||
* Update: SimplifyPolygon function now takes a PolyFillType parameter.
|
||||
|
||||
v4.8.5 - 15 July 2012
|
||||
* Bugfix: Potential for memory overflow errors in OffsetPolygons().
|
||||
|
||||
v4.8.4 - 1 June 2012
|
||||
* Bugfix: Another obscure bug affecting ExPolygons structure.
|
||||
|
||||
v4.8.3 - 27 May 2012
|
||||
* Bugfix: Obscure bug causing incorrect removal of a vertex.
|
||||
|
||||
v4.8.2 - 21 May 2012
|
||||
* Bugfix: Obscure bug could cause an exception when using
|
||||
ExPolygon structure.
|
||||
|
||||
v4.8.1 - 12 May 2012
|
||||
* Update: Cody tidy and minor bug fixes.
|
||||
|
||||
v4.8.0 - 30 April 2012
|
||||
* Bugfix: Occasional errors in orientation fixed.
|
||||
* Update: Added notes on rounding to the documentation.
|
||||
|
||||
v4.7.6 - 11 April 2012
|
||||
* Fixed a bug in Orientation function (affecting C# translations only).
|
||||
* Minor documentation update.
|
||||
|
||||
v4.7.5 - 28 March 2012
|
||||
* Bugfix: Fixed a recently introduced bug that occasionally caused an
|
||||
unhandled exception in C++ and C# translations.
|
||||
|
||||
v4.7.4 - 15 March 2012
|
||||
* Bugfix: Another minor bugfix.
|
||||
|
||||
v4.7.2 - 4 March 2012
|
||||
* Bugfix: Fixed bug introduced in ver 4.7 which sometimes caused
|
||||
an exception if ExPolygon structure was passed to Clipper's
|
||||
Execute method.
|
||||
|
||||
v4.7.1 - 3 March 2012
|
||||
* Bugfix: Rare crash when JoinCommonEdges joined polygons that
|
||||
'cancelled' each other.
|
||||
* Bugfix: Clipper's internal Orientation method occasionally
|
||||
returned wrong result.
|
||||
* Update: Improved C# code (thanks to numerous excellent suggestions
|
||||
from David Piepgrass)
|
||||
|
||||
v4.7 - 10 February 2012
|
||||
* Improved the joining of output polygons sharing a common edge.
|
||||
|
||||
v4.6.6 - 3 February 2012
|
||||
* Bugfix: Another obscure bug occasionally causing incorrect
|
||||
polygon orientation.
|
||||
|
||||
v4.6.5 - 17 January 2012
|
||||
* Bugfix: Obscure bug occasionally causing incorrect hole
|
||||
assignment in ExPolygon structure.
|
||||
|
||||
v4.6.4 - 8 November 2011
|
||||
* Added: SimplifyPolygon and SimplifyPolygons functions.
|
||||
|
||||
v4.6.3 - 11 November 2011
|
||||
* Bugfix: Fixed another minor mitering bug in OffsetPolygons.
|
||||
|
||||
v4.6.2 - 10 November 2011
|
||||
* Bugfix: Fixed a rare bug in the orientation of polygons
|
||||
returned by Clipper's Execute() method.
|
||||
* Bugfix: Previous update introduced a mitering bug in the
|
||||
OffsetPolygons function.
|
||||
|
||||
v4.6 - 29 October 2011
|
||||
* Added: Support for Positive and Negative polygon fill
|
||||
types (in addition to the EvenOdd and NonZero fill types).
|
||||
* Bugfix: The OffsetPolygons function was generating the
|
||||
occasional artefact when 'shrinking' polygons.
|
||||
|
||||
v4.5.5 - 8 October 2011
|
||||
* Bugfix: Fixed an obscure bug in Clipper's JoinCommonEdges
|
||||
method.
|
||||
* Update: Replaced IsClockwise function with Orientation
|
||||
function. The orientation issues affecting OffsetPolygons
|
||||
should now be finally resolved.
|
||||
* Change: The Area function once again returns a signed value.
|
||||
|
||||
v4.5.1 - 28 September 2011
|
||||
* Deleted: The UseFullCoordinateRange property has been
|
||||
deleted since integer range is now managed implicitly.
|
||||
* BugFix: Minor bug in OffsetPolygon mitering.
|
||||
* Change: C# JoinType enum moved from Clipper class to
|
||||
ClipperLib namespace.
|
||||
* Change: The Area function now returns the absolute area
|
||||
(irrespective of orientation).
|
||||
* Change: The IsClockwise function now requires a second
|
||||
parameter - YAxisPositiveUpward - to accommodate displays
|
||||
with Y-axis oriented in either direction
|
||||
|
||||
v4.4.4 - 10 September 2011
|
||||
* Change: Deleted jtButt from JoinType (used by the
|
||||
OffsetPolygons function).
|
||||
* BugFix: Fixed another minor bug in OffsetPolygons function.
|
||||
* Update: Further improvements to the help file
|
||||
|
||||
v4.4.3 - 29 August 2011
|
||||
* BugFix: fixed a minor rounding issue in OffsetPolygons
|
||||
function (affected C++ & C# translations).
|
||||
* BugFix: fixed a minor bug in OffsetPolygons' function
|
||||
declaration (affected C++ translation only).
|
||||
* Change: 'clipper' namespace changed to 'ClipperLib'
|
||||
namespace in both C++ and C# code to remove the ambiguity
|
||||
between the Clipper class and the namespace. (This also
|
||||
required numerous updates to the accompanying demos.)
|
||||
|
||||
v4.4.2 - 26 August 2011
|
||||
* BugFix: minor bugfixes in Clipper.
|
||||
* Update: the OffsetPolygons function has been significantly
|
||||
improved by offering 4 different join styles.
|
||||
|
||||
v4.4.0 - 6 August 2011
|
||||
* BugFix: A number of minor bugs have been fixed that mostly
|
||||
affected the new ExPolygons structure.
|
||||
|
||||
v4.3.0 - 17 June 2011
|
||||
* New: ExPolygons structure that explicitly associates 'hole'
|
||||
polygons with their 'outer' container polygons.
|
||||
* New: Execute method overloaded so the solution parameter
|
||||
can now be either Polygons or ExPolygons.
|
||||
* BugFix: Fixed a rare bug in solution polygons orientation.
|
||||
|
||||
v4.2.8 - 21 May 2011
|
||||
* Update: JoinCommonEdges() improved once more.
|
||||
* BugFix: Several minor bugs fixed.
|
||||
|
||||
v4.2.6 - 1 May 2011
|
||||
* Bugfix: minor bug in SlopesEqual function.
|
||||
* Update: Merging of output polygons sharing common edges
|
||||
has been significantly inproved
|
||||
|
||||
v4.2.4 - 26 April 2011
|
||||
Input polygon coordinates can now contain the full range of
|
||||
signed 64bit integers (ie +/-9,223,372,036,854,775,807). This
|
||||
means that floating point values can be converted to and from
|
||||
Clipper's 64bit integer coordinates structure (IntPoint) and
|
||||
still retain a precision of up to 18 decimal places. However,
|
||||
since the large-integer math that supports this expanded range
|
||||
imposes a small cost on performance (~15%), a new property
|
||||
UseFullCoordinateRange has been added to the Clipper class to
|
||||
allow users the choice of whether or not to use this expanded
|
||||
coordinate range. If this property is disabled, coordinate values
|
||||
are restricted to +/-1,500,000,000.
|
||||
|
||||
v4.2 - 12 April 2011
|
||||
JoinCommonEdges() code significantly improved plus other minor
|
||||
improvements.
|
||||
|
||||
v4.1.2 - 9 April 2011
|
||||
* Update: Minor code tidy.
|
||||
* Bugfix: Possible endless loop in JoinCommonEdges() in clipper.pas.
|
||||
|
||||
v4.1.1 - 8 April 2011
|
||||
* Update: All polygon coordinates are now stored as 64bit integers
|
||||
(though they're still restricted to range -1.5e9 to +1.5e9 pending
|
||||
the inclusion of code supporting 64bit math).
|
||||
* Change: AddPolygon and AddPolygons methods now return boolean
|
||||
values.
|
||||
* Bugfix: Bug in JoinCommonEdges() caused potential endless loop.
|
||||
* Bugfix: Bug in IsClockwise(). (C++ code only)
|
||||
|
||||
v4.0 - 5 April 2011
|
||||
* Clipper 4 is a major rewrite of earlier versions. The biggest
|
||||
change is that floating point values are no longer used,
|
||||
except for the storing of edge slope values. The main benefit
|
||||
of this is the issue of numerical robustness has been
|
||||
addressed. Due to other major code improvements Clipper v4
|
||||
is approximately 40% faster than Clipper v3.
|
||||
* The AddPolyPolygon method has been renamed to AddPolygons.
|
||||
* The IgnoreOrientation property has been removed.
|
||||
* The clipper_misc library has been merged back into the
|
||||
main clipper library.
|
||||
|
||||
v3.1.0 - 17 February 2011
|
||||
* Bugfix: Obscure bug in TClipperBase.SetDx method that caused
|
||||
problems with very small edges ( edges <1/1000th pixel in size).
|
||||
|
||||
v3.0.3 - 9 February 2011
|
||||
* Bugfix: Significant bug, but only in C# code.
|
||||
* Update: Minor refactoring.
|
||||
|
||||
v3.0 - 31 January 2011
|
||||
* Update: Major rewrite of the portion of code that calculates
|
||||
the output polygons' orientation.
|
||||
* Update: Help file significantly improved.
|
||||
* Change: Renamed ForceOrientation property to IgnoreOrientation.
|
||||
If the orientation of output polygons is not important, or can
|
||||
be managed separately, clipping routines can be sped up by about
|
||||
60% by setting IgnoreOrientation to true. Defaults to false.
|
||||
* Change: The OffsetPolygon and Area functions have been moved to
|
||||
the new unit - clipper_misc.
|
||||
|
||||
2.99 - 15 January 2011
|
||||
* Bugfix: Obscure bug in AddPolygon method could cause an endless loop.
|
||||
|
||||
2.8 - 20 November 2010
|
||||
* Updated: Output polygons which previously shared a common
|
||||
edge are now merged.
|
||||
* Changed: The orientation of outer polygons is now clockwise
|
||||
when the display's Y axis is positive downwards (as is
|
||||
typical for most Windows applications). Inner polygons
|
||||
(holes) have the opposite orientation.
|
||||
* Added: Support module for Cairo Graphics Library (with demo).
|
||||
* Updated: C# and C++ demos.
|
||||
|
||||
2.522 - 15 October 2010
|
||||
* Added C# translation (thanks to Olivier Lejeune) and
|
||||
a link to Ruby bindings (thanks to Mike Owens).
|
||||
|
||||
2.0 - 30 July 2010
|
||||
* Clipper now clips using both the Even-Odd (alternate) and
|
||||
Non-Zero (winding) polygon filling rules. (Previously Clipper
|
||||
assumed the Even-Odd rule for polygon filling.)
|
||||
|
||||
1.4c - 16 June 2010
|
||||
* Added C++ support for AGG graphics library
|
||||
|
||||
1.2s - 2 June 2010
|
||||
* Added C++ translation of clipper.pas
|
||||
|
||||
=====================================================================
|
||||
Clipper Change Log
|
||||
=====================================================================
|
||||
|
||||
v6.1.3 (19 January 2014)
|
||||
* Fixed potential endless loop condition when adding open
|
||||
paths to Clipper.
|
||||
* Fixed missing implementation of SimplifyPolygon function
|
||||
in C++ code.
|
||||
* Fixed incorrect upper range constant for polygon coordinates
|
||||
in Delphi code.
|
||||
* Added PointInPolygon function.
|
||||
* Overloaded MinkowskiSum function to accommodate multi-contour
|
||||
paths.
|
||||
|
||||
v6.1.2 (15 December 2013)
|
||||
* Fixed broken C++ header file.
|
||||
* Minor improvement to joining polygons.
|
||||
|
||||
v6.1.1 (13 December 2013)
|
||||
* Fixed a couple of bugs affecting open paths that could
|
||||
raise unhandled exceptions.
|
||||
|
||||
v6.1.0 (12 December 2013)
|
||||
* Deleted: Previously deprecated code has been removed.
|
||||
* Modified: The OffsetPaths function is now deprecated as it has
|
||||
been replaced by the ClipperOffset class which is much more
|
||||
flexible.
|
||||
* Bugfixes: Several minor bugs have been fixed including
|
||||
occasionally an incorrect nesting within the PolyTree structure.
|
||||
|
||||
v6.0.0 (30 October 2013)
|
||||
* Added: Open path (polyline) clipping. A new 'Curves' demo
|
||||
application showcases this (see the 'Curves' directory).
|
||||
* Update: Major improvement in the merging of
|
||||
shared/collinear edges in clip solutions (see Execute).
|
||||
* Added: The IntPoint structure now has an optional 'Z' member.
|
||||
(See the precompiler directive use_xyz.)
|
||||
* Added: Users can now force Clipper to use 32bit integers
|
||||
(via the precompiler directive use_int32) instead of using
|
||||
64bit integers.
|
||||
* Modified: To accommodate open paths, the Polygon and Polygons
|
||||
structures have been renamed Path and Paths respectively. The
|
||||
AddPolygon and AddPolygons methods of the ClipperBase class
|
||||
have been renamed AddPath and AddPaths respectively. Several
|
||||
other functions have been similarly renamed.
|
||||
* Modified: The PolyNode Class has a new IsOpen property.
|
||||
* Modified: The Clipper class has a new ZFillFunction property.
|
||||
* Added: MinkowskiSum and MinkowskiDiff functions added.
|
||||
* Added: Several other new functions have been added including
|
||||
PolyTreeToPaths, OpenPathsFromPolyTree and ClosedPathsFromPolyTree.
|
||||
* Added: The Clipper constructor now accepts an optional InitOptions
|
||||
parameter to simplify setting properties.
|
||||
* Bugfixes: Numerous minor bugs have been fixed.
|
||||
* Deprecated: Version 6 is a major upgrade from previous versions
|
||||
and quite a number of changes have been made to exposed structures
|
||||
and functions. To minimize inconvenience to existing library users,
|
||||
some code has been retained and some added to maintain backward
|
||||
compatibility. However, because this code will be removed in a
|
||||
future update, it has been marked as deprecated and a precompiler
|
||||
directive use_deprecated has been defined.
|
||||
|
||||
v5.1.6 (23 May 2013)
|
||||
* BugFix: CleanPolygon function was buggy.
|
||||
* Changed: The behaviour of the 'miter' JoinType has been
|
||||
changed so that when squaring occurs, it's no longer
|
||||
extended up to the miter limit but is squared off at
|
||||
exactly 'delta' units. (This improves the look of mitering
|
||||
with larger limits at acute angles.)
|
||||
* Added: New OffsetPolyLines function
|
||||
* Update: Minor code refactoring and optimisations
|
||||
|
||||
v5.1.5 (5 May 2013)
|
||||
* Added: ForceSimple property to Clipper class
|
||||
* Update: Improved documentation
|
||||
|
||||
v5.1.4 (24 March 2013)
|
||||
* Update: CleanPolygon function enhanced.
|
||||
* Update: Documentation improved.
|
||||
|
||||
v5.1.3 (14 March 2013)
|
||||
* Bugfix: Minor bugfixes.
|
||||
* Update: Documentation significantly improved.
|
||||
|
||||
v5.1.2 (26 February 2013)
|
||||
* Bugfix: PolyNode class was missing a constructor.
|
||||
* Update: The MiterLimit parameter in the OffsetPolygons
|
||||
function has been renamed Limit and can now also be used to
|
||||
limit the number of vertices used to construct arcs when
|
||||
JoinType is set to jtRound.
|
||||
|
||||
v5.1.0 (17 February 2013)
|
||||
* Update: ExPolygons has been replaced with the PolyTree &
|
||||
PolyNode classes to more fully represent the parent-child
|
||||
relationships of the polygons returned by Clipper.
|
||||
* Added: New CleanPolygon and CleanPolygons functions.
|
||||
* Bugfix: Another orientation bug fixed.
|
||||
|
||||
v5.0.2 - 30 December 2012
|
||||
* Bugfix: Significant fixes in and tidy of the internal
|
||||
Int128 class (which is used only when polygon coordinate
|
||||
values are greater than ±0x3FFFFFFF (~1.07e9)).
|
||||
* Update: The Area algorithm has been updated and is faster.
|
||||
* Update: Documentation updates. The newish but undocumented
|
||||
'CheckInputs' parameter of the OffsetPolygons function has been
|
||||
renamed 'AutoFix' and documented too. The comments on rounding
|
||||
have also been improved (ie clearer and expanded).
|
||||
|
||||
v4.10.0 - 25 December 2012
|
||||
* Bugfix: Orientation bugs should now be resolved (finally!).
|
||||
* Bugfix: Bug in Int128 class
|
||||
|
||||
v4.9.8 - 2 December 2012
|
||||
* Bugfix: Further fixes to rare Orientation bug.
|
||||
|
||||
v4.9.7 - 29 November 2012
|
||||
* Bugfix: Bug that very rarely returned the wrong polygon
|
||||
orientation.
|
||||
* Bugfix: Obscure bug affecting OffsetPolygons when using
|
||||
jtRound for the JoinType parameter and when polygons also
|
||||
contain very large coordinate values (> +/-100000000000).
|
||||
|
||||
v4.9.6 - 9 November 2012
|
||||
* Bugfix: Another obscure bug related to joining polygons.
|
||||
|
||||
v4.9.4 - 2 November 2012
|
||||
* Bugfix: Bugs in Int128 class occasionally causing
|
||||
wrong orientations.
|
||||
* Bugfix: Further fixes related to joining polygons.
|
||||
|
||||
v4.9.0 - 9 October 2012
|
||||
* Bugfix: Obscure bug related to joining polygons.
|
||||
|
||||
v4.8.9 - 25 September 2012
|
||||
* Bugfix: Obscure bug related to precision of intersections.
|
||||
|
||||
v4.8.8 - 30 August 2012
|
||||
* Bugfix: Fixed bug in OffsetPolygons function introduced in
|
||||
version 4.8.5.
|
||||
|
||||
v4.8.7 - 24 August 2012
|
||||
* Bugfix: ReversePolygon function in C++ translation was broken.
|
||||
* Bugfix: Two obscure bugs affecting orientation fixed too.
|
||||
|
||||
v4.8.6 - 11 August 2012
|
||||
* Bugfix: Potential for memory overflow errors when using
|
||||
ExPolygons structure.
|
||||
* Bugfix: The polygon coordinate range has been reduced to
|
||||
+/- 0x3FFFFFFFFFFFFFFF (4.6e18).
|
||||
* Update: ReversePolygons function was misnamed ReversePoints in C++.
|
||||
* Update: SimplifyPolygon function now takes a PolyFillType parameter.
|
||||
|
||||
v4.8.5 - 15 July 2012
|
||||
* Bugfix: Potential for memory overflow errors in OffsetPolygons().
|
||||
|
||||
v4.8.4 - 1 June 2012
|
||||
* Bugfix: Another obscure bug affecting ExPolygons structure.
|
||||
|
||||
v4.8.3 - 27 May 2012
|
||||
* Bugfix: Obscure bug causing incorrect removal of a vertex.
|
||||
|
||||
v4.8.2 - 21 May 2012
|
||||
* Bugfix: Obscure bug could cause an exception when using
|
||||
ExPolygon structure.
|
||||
|
||||
v4.8.1 - 12 May 2012
|
||||
* Update: Cody tidy and minor bug fixes.
|
||||
|
||||
v4.8.0 - 30 April 2012
|
||||
* Bugfix: Occasional errors in orientation fixed.
|
||||
* Update: Added notes on rounding to the documentation.
|
||||
|
||||
v4.7.6 - 11 April 2012
|
||||
* Fixed a bug in Orientation function (affecting C# translations only).
|
||||
* Minor documentation update.
|
||||
|
||||
v4.7.5 - 28 March 2012
|
||||
* Bugfix: Fixed a recently introduced bug that occasionally caused an
|
||||
unhandled exception in C++ and C# translations.
|
||||
|
||||
v4.7.4 - 15 March 2012
|
||||
* Bugfix: Another minor bugfix.
|
||||
|
||||
v4.7.2 - 4 March 2012
|
||||
* Bugfix: Fixed bug introduced in ver 4.7 which sometimes caused
|
||||
an exception if ExPolygon structure was passed to Clipper's
|
||||
Execute method.
|
||||
|
||||
v4.7.1 - 3 March 2012
|
||||
* Bugfix: Rare crash when JoinCommonEdges joined polygons that
|
||||
'cancelled' each other.
|
||||
* Bugfix: Clipper's internal Orientation method occasionally
|
||||
returned wrong result.
|
||||
* Update: Improved C# code (thanks to numerous excellent suggestions
|
||||
from David Piepgrass)
|
||||
|
||||
v4.7 - 10 February 2012
|
||||
* Improved the joining of output polygons sharing a common edge.
|
||||
|
||||
v4.6.6 - 3 February 2012
|
||||
* Bugfix: Another obscure bug occasionally causing incorrect
|
||||
polygon orientation.
|
||||
|
||||
v4.6.5 - 17 January 2012
|
||||
* Bugfix: Obscure bug occasionally causing incorrect hole
|
||||
assignment in ExPolygon structure.
|
||||
|
||||
v4.6.4 - 8 November 2011
|
||||
* Added: SimplifyPolygon and SimplifyPolygons functions.
|
||||
|
||||
v4.6.3 - 11 November 2011
|
||||
* Bugfix: Fixed another minor mitering bug in OffsetPolygons.
|
||||
|
||||
v4.6.2 - 10 November 2011
|
||||
* Bugfix: Fixed a rare bug in the orientation of polygons
|
||||
returned by Clipper's Execute() method.
|
||||
* Bugfix: Previous update introduced a mitering bug in the
|
||||
OffsetPolygons function.
|
||||
|
||||
v4.6 - 29 October 2011
|
||||
* Added: Support for Positive and Negative polygon fill
|
||||
types (in addition to the EvenOdd and NonZero fill types).
|
||||
* Bugfix: The OffsetPolygons function was generating the
|
||||
occasional artefact when 'shrinking' polygons.
|
||||
|
||||
v4.5.5 - 8 October 2011
|
||||
* Bugfix: Fixed an obscure bug in Clipper's JoinCommonEdges
|
||||
method.
|
||||
* Update: Replaced IsClockwise function with Orientation
|
||||
function. The orientation issues affecting OffsetPolygons
|
||||
should now be finally resolved.
|
||||
* Change: The Area function once again returns a signed value.
|
||||
|
||||
v4.5.1 - 28 September 2011
|
||||
* Deleted: The UseFullCoordinateRange property has been
|
||||
deleted since integer range is now managed implicitly.
|
||||
* BugFix: Minor bug in OffsetPolygon mitering.
|
||||
* Change: C# JoinType enum moved from Clipper class to
|
||||
ClipperLib namespace.
|
||||
* Change: The Area function now returns the absolute area
|
||||
(irrespective of orientation).
|
||||
* Change: The IsClockwise function now requires a second
|
||||
parameter - YAxisPositiveUpward - to accommodate displays
|
||||
with Y-axis oriented in either direction
|
||||
|
||||
v4.4.4 - 10 September 2011
|
||||
* Change: Deleted jtButt from JoinType (used by the
|
||||
OffsetPolygons function).
|
||||
* BugFix: Fixed another minor bug in OffsetPolygons function.
|
||||
* Update: Further improvements to the help file
|
||||
|
||||
v4.4.3 - 29 August 2011
|
||||
* BugFix: fixed a minor rounding issue in OffsetPolygons
|
||||
function (affected C++ & C# translations).
|
||||
* BugFix: fixed a minor bug in OffsetPolygons' function
|
||||
declaration (affected C++ translation only).
|
||||
* Change: 'clipper' namespace changed to 'ClipperLib'
|
||||
namespace in both C++ and C# code to remove the ambiguity
|
||||
between the Clipper class and the namespace. (This also
|
||||
required numerous updates to the accompanying demos.)
|
||||
|
||||
v4.4.2 - 26 August 2011
|
||||
* BugFix: minor bugfixes in Clipper.
|
||||
* Update: the OffsetPolygons function has been significantly
|
||||
improved by offering 4 different join styles.
|
||||
|
||||
v4.4.0 - 6 August 2011
|
||||
* BugFix: A number of minor bugs have been fixed that mostly
|
||||
affected the new ExPolygons structure.
|
||||
|
||||
v4.3.0 - 17 June 2011
|
||||
* New: ExPolygons structure that explicitly associates 'hole'
|
||||
polygons with their 'outer' container polygons.
|
||||
* New: Execute method overloaded so the solution parameter
|
||||
can now be either Polygons or ExPolygons.
|
||||
* BugFix: Fixed a rare bug in solution polygons orientation.
|
||||
|
||||
v4.2.8 - 21 May 2011
|
||||
* Update: JoinCommonEdges() improved once more.
|
||||
* BugFix: Several minor bugs fixed.
|
||||
|
||||
v4.2.6 - 1 May 2011
|
||||
* Bugfix: minor bug in SlopesEqual function.
|
||||
* Update: Merging of output polygons sharing common edges
|
||||
has been significantly inproved
|
||||
|
||||
v4.2.4 - 26 April 2011
|
||||
Input polygon coordinates can now contain the full range of
|
||||
signed 64bit integers (ie +/-9,223,372,036,854,775,807). This
|
||||
means that floating point values can be converted to and from
|
||||
Clipper's 64bit integer coordinates structure (IntPoint) and
|
||||
still retain a precision of up to 18 decimal places. However,
|
||||
since the large-integer math that supports this expanded range
|
||||
imposes a small cost on performance (~15%), a new property
|
||||
UseFullCoordinateRange has been added to the Clipper class to
|
||||
allow users the choice of whether or not to use this expanded
|
||||
coordinate range. If this property is disabled, coordinate values
|
||||
are restricted to +/-1,500,000,000.
|
||||
|
||||
v4.2 - 12 April 2011
|
||||
JoinCommonEdges() code significantly improved plus other minor
|
||||
improvements.
|
||||
|
||||
v4.1.2 - 9 April 2011
|
||||
* Update: Minor code tidy.
|
||||
* Bugfix: Possible endless loop in JoinCommonEdges() in clipper.pas.
|
||||
|
||||
v4.1.1 - 8 April 2011
|
||||
* Update: All polygon coordinates are now stored as 64bit integers
|
||||
(though they're still restricted to range -1.5e9 to +1.5e9 pending
|
||||
the inclusion of code supporting 64bit math).
|
||||
* Change: AddPolygon and AddPolygons methods now return boolean
|
||||
values.
|
||||
* Bugfix: Bug in JoinCommonEdges() caused potential endless loop.
|
||||
* Bugfix: Bug in IsClockwise(). (C++ code only)
|
||||
|
||||
v4.0 - 5 April 2011
|
||||
* Clipper 4 is a major rewrite of earlier versions. The biggest
|
||||
change is that floating point values are no longer used,
|
||||
except for the storing of edge slope values. The main benefit
|
||||
of this is the issue of numerical robustness has been
|
||||
addressed. Due to other major code improvements Clipper v4
|
||||
is approximately 40% faster than Clipper v3.
|
||||
* The AddPolyPolygon method has been renamed to AddPolygons.
|
||||
* The IgnoreOrientation property has been removed.
|
||||
* The clipper_misc library has been merged back into the
|
||||
main clipper library.
|
||||
|
||||
v3.1.0 - 17 February 2011
|
||||
* Bugfix: Obscure bug in TClipperBase.SetDx method that caused
|
||||
problems with very small edges ( edges <1/1000th pixel in size).
|
||||
|
||||
v3.0.3 - 9 February 2011
|
||||
* Bugfix: Significant bug, but only in C# code.
|
||||
* Update: Minor refactoring.
|
||||
|
||||
v3.0 - 31 January 2011
|
||||
* Update: Major rewrite of the portion of code that calculates
|
||||
the output polygons' orientation.
|
||||
* Update: Help file significantly improved.
|
||||
* Change: Renamed ForceOrientation property to IgnoreOrientation.
|
||||
If the orientation of output polygons is not important, or can
|
||||
be managed separately, clipping routines can be sped up by about
|
||||
60% by setting IgnoreOrientation to true. Defaults to false.
|
||||
* Change: The OffsetPolygon and Area functions have been moved to
|
||||
the new unit - clipper_misc.
|
||||
|
||||
2.99 - 15 January 2011
|
||||
* Bugfix: Obscure bug in AddPolygon method could cause an endless loop.
|
||||
|
||||
2.8 - 20 November 2010
|
||||
* Updated: Output polygons which previously shared a common
|
||||
edge are now merged.
|
||||
* Changed: The orientation of outer polygons is now clockwise
|
||||
when the display's Y axis is positive downwards (as is
|
||||
typical for most Windows applications). Inner polygons
|
||||
(holes) have the opposite orientation.
|
||||
* Added: Support module for Cairo Graphics Library (with demo).
|
||||
* Updated: C# and C++ demos.
|
||||
|
||||
2.522 - 15 October 2010
|
||||
* Added C# translation (thanks to Olivier Lejeune) and
|
||||
a link to Ruby bindings (thanks to Mike Owens).
|
||||
|
||||
2.0 - 30 July 2010
|
||||
* Clipper now clips using both the Even-Odd (alternate) and
|
||||
Non-Zero (winding) polygon filling rules. (Previously Clipper
|
||||
assumed the Even-Odd rule for polygon filling.)
|
||||
|
||||
1.4c - 16 June 2010
|
||||
* Added C++ support for AGG graphics library
|
||||
|
||||
1.2s - 2 June 2010
|
||||
* Added C++ translation of clipper.pas
|
||||
|
||||
1.0 - 9 May 2010
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,398 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Author : Angus Johnson *
|
||||
* Version : 6.1.3a *
|
||||
* Date : 22 January 2014 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2014 *
|
||||
* *
|
||||
* License: *
|
||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||
* http://www.boost.org/LICENSE_1_0.txt *
|
||||
* *
|
||||
* Attributions: *
|
||||
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
|
||||
* "A generic solution to polygon clipping" *
|
||||
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
|
||||
* http://portal.acm.org/citation.cfm?id=129906 *
|
||||
* *
|
||||
* Computer graphics and geometric modeling: implementation and algorithms *
|
||||
* By Max K. Agoston *
|
||||
* Springer; 1 edition (January 4, 2005) *
|
||||
* http://books.google.com/books?q=vatti+clipping+agoston *
|
||||
* *
|
||||
* See also: *
|
||||
* "Polygon Offsetting by Computing Winding Numbers" *
|
||||
* Paper no. DETC2005-85513 pp. 565-575 *
|
||||
* ASME 2005 International Design Engineering Technical Conferences *
|
||||
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
|
||||
* September 24-28, 2005 , Long Beach, California, USA *
|
||||
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef clipper_hpp
|
||||
#define clipper_hpp
|
||||
|
||||
#define CLIPPER_VERSION "6.1.3"
|
||||
|
||||
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
|
||||
//improve performance but coordinate values are limited to the range +/- 46340
|
||||
//#define use_int32
|
||||
|
||||
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
|
||||
//#define use_xyz
|
||||
|
||||
//use_lines: Enables line clipping. Adds a very minor cost to performance.
|
||||
//#define use_lines
|
||||
|
||||
//use_deprecated: Enables support for the obsolete OffsetPaths() function
|
||||
//which has been replace with the ClipperOffset class.
|
||||
#define use_deprecated
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
|
||||
namespace ClipperLib {
|
||||
|
||||
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
|
||||
enum PolyType { ptSubject, ptClip };
|
||||
//By far the most widely used winding rules for polygon filling are
|
||||
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
|
||||
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
|
||||
//see http://glprogramming.com/red/chapter11.html
|
||||
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
|
||||
|
||||
#ifdef use_int32
|
||||
typedef int cInt;
|
||||
typedef unsigned int cUInt;
|
||||
#else
|
||||
typedef signed long long cInt;
|
||||
typedef unsigned long long cUInt;
|
||||
#endif
|
||||
|
||||
struct IntPoint {
|
||||
cInt X;
|
||||
cInt Y;
|
||||
#ifdef use_xyz
|
||||
cInt Z;
|
||||
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
|
||||
#else
|
||||
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
|
||||
#endif
|
||||
|
||||
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
|
||||
{
|
||||
return a.X == b.X && a.Y == b.Y;
|
||||
}
|
||||
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
|
||||
{
|
||||
return a.X != b.X || a.Y != b.Y;
|
||||
}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef std::vector< IntPoint > Path;
|
||||
typedef std::vector< Path > Paths;
|
||||
|
||||
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
|
||||
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
|
||||
|
||||
std::ostream& operator <<(std::ostream &s, const IntPoint &p);
|
||||
std::ostream& operator <<(std::ostream &s, const Path &p);
|
||||
std::ostream& operator <<(std::ostream &s, const Paths &p);
|
||||
|
||||
struct DoublePoint
|
||||
{
|
||||
double X;
|
||||
double Y;
|
||||
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
|
||||
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef use_xyz
|
||||
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
|
||||
#endif
|
||||
|
||||
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
|
||||
enum JoinType {jtSquare, jtRound, jtMiter};
|
||||
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
|
||||
#ifdef use_deprecated
|
||||
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
|
||||
#endif
|
||||
|
||||
class PolyNode;
|
||||
typedef std::vector< PolyNode* > PolyNodes;
|
||||
|
||||
class PolyNode
|
||||
{
|
||||
public:
|
||||
PolyNode();
|
||||
Path Contour;
|
||||
PolyNodes Childs;
|
||||
PolyNode* Parent;
|
||||
PolyNode* GetNext() const;
|
||||
bool IsHole() const;
|
||||
bool IsOpen() const;
|
||||
int ChildCount() const;
|
||||
private:
|
||||
unsigned Index; //node index in Parent.Childs
|
||||
bool m_IsOpen;
|
||||
JoinType m_jointype;
|
||||
EndType m_endtype;
|
||||
PolyNode* GetNextSiblingUp() const;
|
||||
void AddChild(PolyNode& child);
|
||||
friend class Clipper; //to access Index
|
||||
friend class ClipperOffset;
|
||||
};
|
||||
|
||||
class PolyTree: public PolyNode
|
||||
{
|
||||
public:
|
||||
~PolyTree(){Clear();};
|
||||
PolyNode* GetFirst() const;
|
||||
void Clear();
|
||||
int Total() const;
|
||||
private:
|
||||
PolyNodes AllNodes;
|
||||
friend class Clipper; //to access AllNodes
|
||||
};
|
||||
|
||||
bool Orientation(const Path &poly);
|
||||
double Area(const Path &poly);
|
||||
int PointInPolygon(const IntPoint &pt, const Path &path);
|
||||
|
||||
#ifdef use_deprecated
|
||||
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
|
||||
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
|
||||
#endif
|
||||
|
||||
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
|
||||
|
||||
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
|
||||
void CleanPolygon(Path& poly, double distance = 1.415);
|
||||
void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
|
||||
void CleanPolygons(Paths& polys, double distance = 1.415);
|
||||
|
||||
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
|
||||
void MinkowskiSum(const Path& pattern, const Paths& paths,
|
||||
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
|
||||
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
|
||||
|
||||
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
|
||||
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
|
||||
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
|
||||
|
||||
void ReversePath(Path& p);
|
||||
void ReversePaths(Paths& p);
|
||||
|
||||
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
|
||||
|
||||
//enums that are used internally ...
|
||||
enum EdgeSide { esLeft = 1, esRight = 2};
|
||||
|
||||
//forward declarations (for stuff used internally) ...
|
||||
struct TEdge;
|
||||
struct IntersectNode;
|
||||
struct LocalMinima;
|
||||
struct Scanbeam;
|
||||
struct OutPt;
|
||||
struct OutRec;
|
||||
struct Join;
|
||||
|
||||
typedef std::vector < OutRec* > PolyOutList;
|
||||
typedef std::vector < TEdge* > EdgeList;
|
||||
typedef std::vector < Join* > JoinList;
|
||||
typedef std::vector < IntersectNode* > IntersectList;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//ClipperBase is the ancestor to the Clipper class. It should not be
|
||||
//instantiated directly. This class simply abstracts the conversion of sets of
|
||||
//polygon coordinates into edge objects that are stored in a LocalMinima list.
|
||||
class ClipperBase
|
||||
{
|
||||
public:
|
||||
ClipperBase();
|
||||
virtual ~ClipperBase();
|
||||
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
|
||||
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
|
||||
virtual void Clear();
|
||||
IntRect GetBounds();
|
||||
bool PreserveCollinear() {return m_PreserveCollinear;};
|
||||
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
|
||||
protected:
|
||||
void DisposeLocalMinimaList();
|
||||
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
|
||||
void PopLocalMinima();
|
||||
virtual void Reset();
|
||||
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
|
||||
void InsertLocalMinima(LocalMinima *newLm);
|
||||
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
|
||||
TEdge* DescendToMin(TEdge *&E);
|
||||
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
|
||||
LocalMinima *m_CurrentLM;
|
||||
LocalMinima *m_MinimaList;
|
||||
bool m_UseFullRange;
|
||||
EdgeList m_edges;
|
||||
bool m_PreserveCollinear;
|
||||
bool m_HasOpenPaths;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Clipper : public virtual ClipperBase
|
||||
{
|
||||
public:
|
||||
Clipper(int initOptions = 0);
|
||||
~Clipper();
|
||||
bool Execute(ClipType clipType,
|
||||
Paths &solution,
|
||||
PolyFillType subjFillType = pftEvenOdd,
|
||||
PolyFillType clipFillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
PolyTree &polytree,
|
||||
PolyFillType subjFillType = pftEvenOdd,
|
||||
PolyFillType clipFillType = pftEvenOdd);
|
||||
bool ReverseSolution() {return m_ReverseOutput;};
|
||||
void ReverseSolution(bool value) {m_ReverseOutput = value;};
|
||||
bool StrictlySimple() {return m_StrictSimple;};
|
||||
void StrictlySimple(bool value) {m_StrictSimple = value;};
|
||||
//set the callback function for z value filling on intersections (otherwise Z is 0)
|
||||
#ifdef use_xyz
|
||||
void ZFillFunction(TZFillCallback zFillFunc);
|
||||
#endif
|
||||
protected:
|
||||
void Reset();
|
||||
virtual bool ExecuteInternal();
|
||||
private:
|
||||
PolyOutList m_PolyOuts;
|
||||
JoinList m_Joins;
|
||||
JoinList m_GhostJoins;
|
||||
IntersectList m_IntersectList;
|
||||
ClipType m_ClipType;
|
||||
std::set< cInt, std::greater<cInt> > m_Scanbeam;
|
||||
TEdge *m_ActiveEdges;
|
||||
TEdge *m_SortedEdges;
|
||||
bool m_ExecuteLocked;
|
||||
PolyFillType m_ClipFillType;
|
||||
PolyFillType m_SubjFillType;
|
||||
bool m_ReverseOutput;
|
||||
bool m_UsingPolyTree;
|
||||
bool m_StrictSimple;
|
||||
#ifdef use_xyz
|
||||
TZFillCallback m_ZFill; //custom callback
|
||||
#endif
|
||||
void SetWindingCount(TEdge& edge);
|
||||
bool IsEvenOddFillType(const TEdge& edge) const;
|
||||
bool IsEvenOddAltFillType(const TEdge& edge) const;
|
||||
void InsertScanbeam(const cInt Y);
|
||||
cInt PopScanbeam();
|
||||
void InsertLocalMinimaIntoAEL(const cInt botY);
|
||||
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
|
||||
void AddEdgeToSEL(TEdge *edge);
|
||||
void CopyAELToSEL();
|
||||
void DeleteFromSEL(TEdge *e);
|
||||
void DeleteFromAEL(TEdge *e);
|
||||
void UpdateEdgeIntoAEL(TEdge *&e);
|
||||
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
|
||||
bool IsContributing(const TEdge& edge) const;
|
||||
bool IsTopHorz(const cInt XPos);
|
||||
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
||||
void DoMaxima(TEdge *e);
|
||||
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
|
||||
void ProcessHorizontals(bool IsTopOfScanbeam);
|
||||
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
|
||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutRec* GetOutRec(int idx);
|
||||
void AppendPolygon(TEdge *e1, TEdge *e2);
|
||||
void IntersectEdges(TEdge *e1, TEdge *e2,
|
||||
const IntPoint &pt, bool protect = false);
|
||||
OutRec* CreateOutRec();
|
||||
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
void DisposeAllOutRecs();
|
||||
void DisposeOutRec(PolyOutList::size_type index);
|
||||
bool ProcessIntersections(const cInt botY, const cInt topY);
|
||||
void BuildIntersectList(const cInt botY, const cInt topY);
|
||||
void ProcessIntersectList();
|
||||
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
|
||||
void BuildResult(Paths& polys);
|
||||
void BuildResult2(PolyTree& polytree);
|
||||
void SetHoleState(TEdge *e, OutRec *outrec);
|
||||
void DisposeIntersectNodes();
|
||||
bool FixupIntersectionOrder();
|
||||
void FixupOutPolygon(OutRec &outrec);
|
||||
bool IsHole(TEdge *e);
|
||||
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
|
||||
void FixHoleLinkage(OutRec &outrec);
|
||||
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
|
||||
void ClearJoins();
|
||||
void ClearGhostJoins();
|
||||
void AddGhostJoin(OutPt *op, const IntPoint offPt);
|
||||
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
|
||||
void JoinCommonEdges();
|
||||
void DoSimplePolygons();
|
||||
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
#ifdef use_xyz
|
||||
void SetZ(IntPoint& pt, TEdge& e);
|
||||
#endif
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ClipperOffset
|
||||
{
|
||||
public:
|
||||
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
|
||||
~ClipperOffset();
|
||||
void AddPath(const Path& path, JoinType joinType, EndType endType);
|
||||
void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
|
||||
void Execute(Paths& solution, double delta);
|
||||
void Execute(PolyTree& solution, double delta);
|
||||
void Clear();
|
||||
double MiterLimit;
|
||||
double ArcTolerance;
|
||||
private:
|
||||
Paths m_destPolys;
|
||||
Path m_srcPoly;
|
||||
Path m_destPoly;
|
||||
std::vector<DoublePoint> m_normals;
|
||||
double m_delta, m_sinA, m_sin, m_cos;
|
||||
double m_miterLim, m_StepsPerRad;
|
||||
IntPoint m_lowest;
|
||||
PolyNode m_polyNodes;
|
||||
|
||||
void FixOrientations();
|
||||
void DoOffset(double delta);
|
||||
void OffsetPoint(int j, int& k, JoinType jointype);
|
||||
void DoSquare(int j, int k);
|
||||
void DoMiter(int j, int k, double r);
|
||||
void DoRound(int j, int k);
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class clipperException : public std::exception
|
||||
{
|
||||
public:
|
||||
clipperException(const char* description): m_descr(description) {}
|
||||
virtual ~clipperException() throw() {}
|
||||
virtual const char* what() const throw() {return m_descr.c_str();}
|
||||
private:
|
||||
std::string m_descr;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} //ClipperLib namespace
|
||||
|
||||
#endif //clipper_hpp
|
||||
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ALLOCATORS_H_
|
||||
#define RAPIDJSON_ALLOCATORS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocator
|
||||
|
||||
/*! \class rapidjson::Allocator
|
||||
\brief Concept for allocating, resizing and freeing memory block.
|
||||
|
||||
Note that Malloc() and Realloc() are non-static but Free() is static.
|
||||
|
||||
So if an allocator need to support Free(), it needs to put its pointer in
|
||||
the header of memory block.
|
||||
|
||||
\code
|
||||
concept Allocator {
|
||||
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
|
||||
|
||||
// Allocate a memory block.
|
||||
// \param size of the memory block in bytes.
|
||||
// \returns pointer to the memory block.
|
||||
void* Malloc(size_t size);
|
||||
|
||||
// Resize a memory block.
|
||||
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
|
||||
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
|
||||
// \param newSize the new size in bytes.
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
|
||||
|
||||
// Free a memory block.
|
||||
// \param pointer to the memory block. Null pointer is permitted.
|
||||
static void Free(void *ptr);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CrtAllocator
|
||||
|
||||
//! C-runtime library allocator.
|
||||
/*! This class is just wrapper for standard C library memory routines.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
class CrtAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = true;
|
||||
void* Malloc(size_t size) {
|
||||
if (size) // behavior of malloc(0) is implementation defined.
|
||||
return std::malloc(size);
|
||||
else
|
||||
return NULL; // standardize to returning NULL.
|
||||
}
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); }
|
||||
static void Free(void *ptr) { std::free(ptr); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MemoryPoolAllocator
|
||||
|
||||
//! Default memory allocator used by the parser and DOM.
|
||||
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
|
||||
|
||||
It does not free memory blocks. And Realloc() only allocate new memory.
|
||||
|
||||
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
|
||||
|
||||
User may also supply a buffer as the first chunk.
|
||||
|
||||
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
|
||||
|
||||
The user-buffer is not deallocated by this allocator.
|
||||
|
||||
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
template <typename BaseAllocator = CrtAllocator>
|
||||
class MemoryPoolAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
||||
|
||||
//! Constructor with chunkSize.
|
||||
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
}
|
||||
|
||||
//! Constructor with user-supplied buffer.
|
||||
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
|
||||
|
||||
The user buffer will not be deallocated when this allocator is destructed.
|
||||
|
||||
\param buffer User supplied buffer.
|
||||
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
|
||||
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
|
||||
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
|
||||
chunkHead_->capacity = size - sizeof(ChunkHeader);
|
||||
chunkHead_->size = 0;
|
||||
chunkHead_->next = 0;
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
*/
|
||||
~MemoryPoolAllocator() {
|
||||
Clear();
|
||||
RAPIDJSON_DELETE(ownBaseAllocator_);
|
||||
}
|
||||
|
||||
//! Deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
void Clear() {
|
||||
while(chunkHead_ != 0 && chunkHead_ != userBuffer_) {
|
||||
ChunkHeader* next = chunkHead_->next;
|
||||
baseAllocator_->Free(chunkHead_);
|
||||
chunkHead_ = next;
|
||||
}
|
||||
}
|
||||
|
||||
//! Computes the total capacity of allocated memory chunks.
|
||||
/*! \return total capacity in bytes.
|
||||
*/
|
||||
size_t Capacity() const {
|
||||
size_t capacity = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
capacity += c->capacity;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
//! Computes the memory blocks allocated.
|
||||
/*! \return total used bytes.
|
||||
*/
|
||||
size_t Size() const {
|
||||
size_t size = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
size += c->size;
|
||||
return size;
|
||||
}
|
||||
|
||||
//! Allocates a memory block. (concept Allocator)
|
||||
void* Malloc(size_t size) {
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
size = RAPIDJSON_ALIGN(size);
|
||||
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
||||
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
|
||||
|
||||
void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size;
|
||||
chunkHead_->size += size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//! Resizes a memory block (concept Allocator)
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
if (originalPtr == 0)
|
||||
return Malloc(newSize);
|
||||
|
||||
// Do not shrink if new size is smaller than original
|
||||
if (originalSize >= newSize)
|
||||
return originalPtr;
|
||||
|
||||
// Simply expand it if it is the last allocation and there is sufficient space
|
||||
if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) {
|
||||
size_t increment = static_cast<size_t>(newSize - originalSize);
|
||||
increment = RAPIDJSON_ALIGN(increment);
|
||||
if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
||||
chunkHead_->size += increment;
|
||||
return originalPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||
void* newBuffer = Malloc(newSize);
|
||||
RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
|
||||
if (originalSize)
|
||||
std::memcpy(newBuffer, originalPtr, originalSize);
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
//! Frees a memory block (concept Allocator)
|
||||
static void Free(void *ptr) { (void)ptr; } // Do nothing
|
||||
|
||||
private:
|
||||
//! Copy constructor is not permitted.
|
||||
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||
//! Copy assignment operator is not permitted.
|
||||
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||
|
||||
//! Creates a new chunk.
|
||||
/*! \param capacity Capacity of the chunk in bytes.
|
||||
*/
|
||||
void AddChunk(size_t capacity) {
|
||||
if (!baseAllocator_)
|
||||
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
|
||||
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity));
|
||||
chunk->capacity = capacity;
|
||||
chunk->size = 0;
|
||||
chunk->next = chunkHead_;
|
||||
chunkHead_ = chunk;
|
||||
}
|
||||
|
||||
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
|
||||
|
||||
//! Chunk header for perpending to each chunk.
|
||||
/*! Chunks are stored as a singly linked list.
|
||||
*/
|
||||
struct ChunkHeader {
|
||||
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
||||
size_t size; //!< Current size of allocated memory in bytes.
|
||||
ChunkHeader *next; //!< Next chunk in the linked list.
|
||||
};
|
||||
|
||||
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
||||
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
||||
void *userBuffer_; //!< User supplied buffer.
|
||||
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
||||
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,261 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
|
||||
#define RAPIDJSON_ENCODEDSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Input byte stream wrapper with a statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
|
||||
*/
|
||||
template <typename Encoding, typename InputByteStream>
|
||||
class EncodedInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedInputStream(InputByteStream& is) : is_(is) {
|
||||
current_ = Encoding::TakeBOM(is_);
|
||||
}
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedInputStream(const EncodedInputStream&);
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
|
||||
InputByteStream& is_;
|
||||
Ch current_;
|
||||
};
|
||||
|
||||
//! Output byte stream wrapper with statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileWriteStream.
|
||||
*/
|
||||
template <typename Encoding, typename OutputByteStream>
|
||||
class EncodedOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
|
||||
if (putBOM)
|
||||
Encoding::PutBOM(os_);
|
||||
}
|
||||
|
||||
void Put(Ch c) { Encoding::Put(os_, c); }
|
||||
void Flush() { os_.Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); }
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedOutputStream(const EncodedOutputStream&);
|
||||
EncodedOutputStream& operator=(const EncodedOutputStream&);
|
||||
|
||||
OutputByteStream& os_;
|
||||
};
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for reading.
|
||||
\tparam InputByteStream type of input byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename InputByteStream>
|
||||
class AutoUTFInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param is input stream to be wrapped.
|
||||
\param type UTF encoding type if it is not detected from the stream.
|
||||
*/
|
||||
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
DetectType();
|
||||
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||
takeFunc_ = f[type_];
|
||||
current_ = takeFunc_(*is_);
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
bool HasBOM() const { return hasBOM_; }
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
|
||||
size_t Tell() const { return is_->Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFInputStream(const AutoUTFInputStream&);
|
||||
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
|
||||
|
||||
// Detect encoding type with BOM or RFC 4627
|
||||
void DetectType() {
|
||||
// BOM (Byte Order Mark):
|
||||
// 00 00 FE FF UTF-32BE
|
||||
// FF FE 00 00 UTF-32LE
|
||||
// FE FF UTF-16BE
|
||||
// FF FE UTF-16LE
|
||||
// EF BB BF UTF-8
|
||||
|
||||
const unsigned char* c = (const unsigned char *)is_->Peek4();
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
|
||||
hasBOM_ = false;
|
||||
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
|
||||
|
||||
// RFC 4627: Section 3
|
||||
// "Since the first two characters of a JSON text will always be ASCII
|
||||
// characters [RFC0020], it is possible to determine whether an octet
|
||||
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
// at the pattern of nulls in the first four octets."
|
||||
// 00 00 00 xx UTF-32BE
|
||||
// 00 xx 00 xx UTF-16BE
|
||||
// xx 00 00 00 UTF-32LE
|
||||
// xx 00 xx 00 UTF-16LE
|
||||
// xx xx xx xx UTF-8
|
||||
|
||||
if (!hasBOM_) {
|
||||
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
|
||||
switch (pattern) {
|
||||
case 0x08: type_ = kUTF32BE; break;
|
||||
case 0x0A: type_ = kUTF16BE; break;
|
||||
case 0x01: type_ = kUTF32LE; break;
|
||||
case 0x05: type_ = kUTF16LE; break;
|
||||
case 0x0F: type_ = kUTF8; break;
|
||||
default: break; // Use type defined by user.
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
}
|
||||
|
||||
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||
InputByteStream* is_;
|
||||
UTFType type_;
|
||||
Ch current_;
|
||||
TakeFunc takeFunc_;
|
||||
bool hasBOM_;
|
||||
};
|
||||
|
||||
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for writing.
|
||||
\tparam InputByteStream type of output byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename OutputByteStream>
|
||||
class AutoUTFOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param os output stream to be wrapped.
|
||||
\param type UTF encoding type.
|
||||
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||
*/
|
||||
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||
putFunc_ = f[type_];
|
||||
|
||||
if (putBOM)
|
||||
PutBOM();
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
|
||||
void Put(Ch c) { putFunc_(*os_, c); }
|
||||
void Flush() { os_->Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); }
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFOutputStream(const AutoUTFOutputStream&);
|
||||
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
|
||||
|
||||
void PutBOM() {
|
||||
typedef void (*PutBOMFunc)(OutputByteStream&);
|
||||
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
|
||||
f[type_](*os_);
|
||||
}
|
||||
|
||||
typedef void (*PutFunc)(OutputByteStream&, Ch);
|
||||
|
||||
OutputByteStream* os_;
|
||||
UTFType type_;
|
||||
PutFunc putFunc_;
|
||||
};
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
@@ -0,0 +1,625 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODINGS_H_
|
||||
#define RAPIDJSON_ENCODINGS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
#elif defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
RAPIDJSON_DIAG_OFF(overflow)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Encoding
|
||||
|
||||
/*! \class rapidjson::Encoding
|
||||
\brief Concept for encoding of Unicode characters.
|
||||
|
||||
\code
|
||||
concept Encoding {
|
||||
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
|
||||
|
||||
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
|
||||
|
||||
//! \brief Encode a Unicode codepoint to an output stream.
|
||||
//! \param os Output stream.
|
||||
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint);
|
||||
|
||||
//! \brief Decode a Unicode codepoint from an input stream.
|
||||
//! \param is Input stream.
|
||||
//! \param codepoint Output of the unicode codepoint.
|
||||
//! \return true if a valid codepoint can be decoded from the stream.
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint);
|
||||
|
||||
//! \brief Validate one Unicode codepoint from an encoded stream.
|
||||
//! \param is Input stream to obtain codepoint.
|
||||
//! \param os Output for copying one codepoint.
|
||||
//! \return true if it is valid.
|
||||
//! \note This function just validating and copying the codepoint without actually decode it.
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os);
|
||||
|
||||
// The following functions are deal with byte streams.
|
||||
|
||||
//! Take a character from input byte stream, skip BOM if exist.
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF8
|
||||
|
||||
//! UTF-8 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-8
|
||||
http://tools.ietf.org/html/rfc3629
|
||||
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct UTF8 {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu)
|
||||
#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0)
|
||||
#define TAIL() COPY(); TRANS(0x70)
|
||||
Ch c = is.Take();
|
||||
if (!(c & 0x80)) {
|
||||
*codepoint = (unsigned char)c;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char type = GetRange((unsigned char)c);
|
||||
*codepoint = (0xFF >> type) & (unsigned char)c;
|
||||
bool result = true;
|
||||
switch (type) {
|
||||
case 2: TAIL(); return result;
|
||||
case 3: TAIL(); TAIL(); return result;
|
||||
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef COPY
|
||||
#undef TRANS
|
||||
#undef TAIL
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
#define COPY() os.Put(c = is.Take())
|
||||
#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0)
|
||||
#define TAIL() COPY(); TRANS(0x70)
|
||||
Ch c;
|
||||
COPY();
|
||||
if (!(c & 0x80))
|
||||
return true;
|
||||
|
||||
bool result = true;
|
||||
switch (GetRange((unsigned char)c)) {
|
||||
case 2: TAIL(); return result;
|
||||
case 3: TAIL(); TAIL(); return result;
|
||||
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef COPY
|
||||
#undef TRANS
|
||||
#undef TAIL
|
||||
}
|
||||
|
||||
static unsigned char GetRange(unsigned char c) {
|
||||
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
|
||||
static const unsigned char type[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
|
||||
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
Ch c = Take(is);
|
||||
if ((unsigned char)c != 0xEFu) return c;
|
||||
c = is.Take();
|
||||
if ((unsigned char)c != 0xBBu) return c;
|
||||
c = is.Take();
|
||||
if ((unsigned char)c != 0xBFu) return c;
|
||||
c = is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return is.Take();
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF16
|
||||
|
||||
//! UTF-16 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-16
|
||||
http://tools.ietf.org/html/rfc2781
|
||||
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF16LE and UTF16BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
os.Put((v & 0x3FF) | 0xDC00);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
Ch c = is.Take();
|
||||
if (c < 0xD800 || c > 0xDFFF) {
|
||||
*codepoint = c;
|
||||
return true;
|
||||
}
|
||||
else if (c <= 0xDBFF) {
|
||||
*codepoint = (c & 0x3FF) << 10;
|
||||
c = is.Take();
|
||||
*codepoint |= (c & 0x3FF);
|
||||
*codepoint += 0x10000;
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
if (c < 0xD800 || c > 0xDFFF)
|
||||
return true;
|
||||
else if (c <= 0xDBFF) {
|
||||
os.Put(c = is.Take());
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 little endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned short)c == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take();
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(c & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 big endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned short)c == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put(c & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF32
|
||||
|
||||
//! UTF-32 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-32
|
||||
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF32LE and UTF32BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c = is.Take();
|
||||
*codepoint = c;
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 little endian enocoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned)c == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take();
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take() << 16;
|
||||
c |= (unsigned char)is.Take() << 24;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(c & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put((c >> 16) & 0xFFu);
|
||||
os.Put((c >> 24) & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned)c == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take() << 24;
|
||||
c |= (unsigned char)is.Take() << 16;
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put((c >> 24) & 0xFFu);
|
||||
os.Put((c >> 16) & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put(c & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ASCII
|
||||
|
||||
//! ASCII encoding.
|
||||
/*! http://en.wikipedia.org/wiki/ASCII
|
||||
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct ASCII {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 0 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
unsigned char c = static_cast<unsigned char>(is.Take());
|
||||
*codepoint = c;
|
||||
return c <= 0X7F;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
unsigned char c = is.Take();
|
||||
os.Put(c);
|
||||
return c <= 0x7F;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
Ch c = Take(is);
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return is.Take();
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
(void)os;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AutoUTF
|
||||
|
||||
//! Runtime-specified UTF encoding type of a stream.
|
||||
enum UTFType {
|
||||
kUTF8 = 0, //!< UTF-8.
|
||||
kUTF16LE = 1, //!< UTF-16 little endian.
|
||||
kUTF16BE = 2, //!< UTF-16 big endian.
|
||||
kUTF32LE = 3, //!< UTF-32 little endian.
|
||||
kUTF32BE = 4 //!< UTF-32 big endian.
|
||||
};
|
||||
|
||||
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
|
||||
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
|
||||
return (*f[is.GetType()])(is, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
|
||||
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
|
||||
return (*f[is.GetType()])(is, os);
|
||||
}
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Transcoder
|
||||
|
||||
//! Encoding conversion.
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::Encode(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Validate one Unicode codepoint from an encoded stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||
}
|
||||
};
|
||||
|
||||
//! Specialization of Transcoder with same source and target encoding.
|
||||
template<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||
}
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSV_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
@@ -0,0 +1,65 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_EN_H__
|
||||
#define RAPIDJSON_ERROR_EN_H__
|
||||
|
||||
#include "error.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Maps error code of parsing into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param parseErrorCode Error code obtained in parsing.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
|
||||
switch (parseErrorCode) {
|
||||
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
|
||||
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values.");
|
||||
|
||||
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||
|
||||
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
||||
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
||||
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
||||
|
||||
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
||||
|
||||
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
||||
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
|
||||
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
|
||||
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
|
||||
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
|
||||
|
||||
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
|
||||
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
|
||||
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
|
||||
|
||||
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||
|
||||
default:
|
||||
return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ERROR_EN_H__
|
||||
@@ -0,0 +1,144 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_ERROR_H__
|
||||
#define RAPIDJSON_ERROR_ERROR_H__
|
||||
|
||||
/*! \file error.h */
|
||||
|
||||
/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_CHARTYPE
|
||||
|
||||
//! Character type of error messages.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
The default character type is \c char.
|
||||
On Windows, user can define this macro as \c TCHAR for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_CHARTYPE
|
||||
#define RAPIDJSON_ERROR_CHARTYPE char
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_STRING
|
||||
|
||||
//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
By default this conversion macro does nothing.
|
||||
On Windows, user can define this macro as \c _T(x) for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_STRING
|
||||
#define RAPIDJSON_ERROR_STRING(x) x
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ParseErrorCode
|
||||
|
||||
//! Error code of parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericReader::Parse, GenericReader::GetParseErrorCode
|
||||
*/
|
||||
enum ParseErrorCode {
|
||||
kParseErrorNone = 0, //!< No error.
|
||||
|
||||
kParseErrorDocumentEmpty, //!< The document is empty.
|
||||
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
|
||||
|
||||
kParseErrorValueInvalid, //!< Invalid value.
|
||||
|
||||
kParseErrorObjectMissName, //!< Missing a name for object member.
|
||||
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
|
||||
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
|
||||
|
||||
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
|
||||
|
||||
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
|
||||
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
|
||||
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
|
||||
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
|
||||
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
|
||||
|
||||
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
|
||||
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
|
||||
kParseErrorNumberMissExponent, //!< Miss exponent in number.
|
||||
|
||||
kParseErrorTermination, //!< Parsing was terminated.
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
};
|
||||
|
||||
//! Result of parsing (wraps ParseErrorCode)
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\code
|
||||
Document doc;
|
||||
ParseResult ok = doc.Parse("[42]");
|
||||
if (!ok) {
|
||||
fprintf(stderr, "JSON parse error: %s (%u)",
|
||||
GetParseError_En(ok.Code()), ok.Offset());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
\endcode
|
||||
\see GenericReader::Parse, GenericDocument::Parse
|
||||
*/
|
||||
struct ParseResult {
|
||||
|
||||
//! Default constructor, no error.
|
||||
ParseResult() : code_(kParseErrorNone), offset_(0) {}
|
||||
//! Constructor to set an error.
|
||||
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
|
||||
|
||||
//! Get the error code.
|
||||
ParseErrorCode Code() const { return code_; }
|
||||
//! Get the error offset, if \ref IsError(), 0 otherwise.
|
||||
size_t Offset() const { return offset_; }
|
||||
|
||||
//! Conversion to \c bool, returns \c true, iff !\ref IsError().
|
||||
operator bool() const { return !IsError(); }
|
||||
//! Whether the result is an error.
|
||||
bool IsError() const { return code_ != kParseErrorNone; }
|
||||
|
||||
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
|
||||
bool operator==(ParseErrorCode code) const { return code_ == code; }
|
||||
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
|
||||
|
||||
//! Reset error code.
|
||||
void Clear() { Set(kParseErrorNone); }
|
||||
//! Update error code and offset.
|
||||
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
|
||||
|
||||
private:
|
||||
ParseErrorCode code_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
//! Function pointer type of GetParseError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetParseError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ERROR_ERROR_H__
|
||||
@@ -0,0 +1,88 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEREADSTREAM_H_
|
||||
#define RAPIDJSON_FILEREADSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! File byte stream for input using fread().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileReadStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type (byte).
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param fp File pointer opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (readCount_ < bufferSize_) {
|
||||
buffer_[readCount_] = '\0';
|
||||
++bufferLast_;
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::FILE* fp_;
|
||||
Ch *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
@@ -0,0 +1,91 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
|
||||
#define RAPIDJSON_FILEWRITESTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of C file stream for input using fread().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileWriteStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type. Only support char.
|
||||
|
||||
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
}
|
||||
|
||||
void Put(char c) {
|
||||
if (current_ >= bufferEnd_)
|
||||
Flush();
|
||||
|
||||
*current_++ = c;
|
||||
}
|
||||
|
||||
void PutN(char c, size_t n) {
|
||||
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
while (n > avail) {
|
||||
std::memset(current_, c, avail);
|
||||
current_ += avail;
|
||||
Flush();
|
||||
n -= avail;
|
||||
avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
std::memset(current_, c, n);
|
||||
current_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (current_ != buffer_) {
|
||||
fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
||||
current_ = buffer_;
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char Take() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
FileWriteStream(const FileWriteStream&);
|
||||
FileWriteStream& operator=(const FileWriteStream&);
|
||||
|
||||
std::FILE* fp_;
|
||||
char *buffer_;
|
||||
char *bufferEnd_;
|
||||
char *current_;
|
||||
};
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(FileWriteStream& stream, char c, size_t n) {
|
||||
stream.PutN(c, n);
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
@@ -0,0 +1,280 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_BIGINTEGER_H_
|
||||
#define RAPIDJSON_BIGINTEGER_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
#include <intrin.h> // for _umul128
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class BigInteger {
|
||||
public:
|
||||
typedef uint64_t Type;
|
||||
|
||||
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
|
||||
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||
}
|
||||
|
||||
explicit BigInteger(uint64_t u) : count_(1) {
|
||||
digits_[0] = u;
|
||||
}
|
||||
|
||||
BigInteger(const char* decimals, size_t length) : count_(1) {
|
||||
RAPIDJSON_ASSERT(length > 0);
|
||||
digits_[0] = 0;
|
||||
size_t i = 0;
|
||||
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
|
||||
while (length >= kMaxDigitPerIteration) {
|
||||
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
|
||||
length -= kMaxDigitPerIteration;
|
||||
i += kMaxDigitPerIteration;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
AppendDecimal64(decimals + i, decimals + i + length);
|
||||
}
|
||||
|
||||
BigInteger& operator=(uint64_t u) {
|
||||
digits_[0] = u;
|
||||
count_ = 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator+=(uint64_t u) {
|
||||
Type backup = digits_[0];
|
||||
digits_[0] += u;
|
||||
for (size_t i = 0; i < count_ - 1; i++) {
|
||||
if (digits_[i] >= backup)
|
||||
return *this; // no carry
|
||||
backup = digits_[i + 1];
|
||||
digits_[i + 1] += 1;
|
||||
}
|
||||
|
||||
// Last carry
|
||||
if (digits_[count_ - 1] < backup)
|
||||
PushBack(1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint64_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint64_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
uint64_t hi;
|
||||
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
|
||||
k = hi;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint32_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint32_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
const uint64_t c = digits_[i] >> 32;
|
||||
const uint64_t d = digits_[i] & 0xFFFFFFFF;
|
||||
const uint64_t uc = u * c;
|
||||
const uint64_t ud = u * d;
|
||||
const uint64_t p0 = ud + k;
|
||||
const uint64_t p1 = uc + (p0 >> 32);
|
||||
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
|
||||
k = p1 >> 32;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator<<=(size_t shift) {
|
||||
if (IsZero() || shift == 0) return *this;
|
||||
|
||||
size_t offset = shift / kTypeBit;
|
||||
size_t interShift = shift % kTypeBit;
|
||||
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
|
||||
|
||||
if (interShift == 0) {
|
||||
std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
|
||||
count_ += offset;
|
||||
}
|
||||
else {
|
||||
digits_[count_] = 0;
|
||||
for (size_t i = count_; i > 0; i--)
|
||||
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
|
||||
digits_[offset] = digits_[0] << interShift;
|
||||
count_ += offset;
|
||||
if (digits_[count_])
|
||||
count_++;
|
||||
}
|
||||
|
||||
std::memset(digits_, 0, offset * sizeof(Type));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const BigInteger& rhs) const {
|
||||
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
|
||||
}
|
||||
|
||||
bool operator==(const Type rhs) const {
|
||||
return count_ == 1 && digits_[0] == rhs;
|
||||
}
|
||||
|
||||
BigInteger& MultiplyPow5(unsigned exp) {
|
||||
static const uint32_t kPow5[12] = {
|
||||
5,
|
||||
5 * 5,
|
||||
5 * 5 * 5,
|
||||
5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
|
||||
};
|
||||
if (exp == 0) return *this;
|
||||
for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
|
||||
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
|
||||
if (exp > 0) *this *= kPow5[exp - 1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Compute absolute difference of this and rhs.
|
||||
// Assume this != rhs
|
||||
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
||||
int cmp = Compare(rhs);
|
||||
RAPIDJSON_ASSERT(cmp != 0);
|
||||
const BigInteger *a, *b; // Makes a > b
|
||||
bool ret;
|
||||
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
||||
else { a = this; b = &rhs; ret = false; }
|
||||
|
||||
Type borrow = 0;
|
||||
for (size_t i = 0; i < a->count_; i++) {
|
||||
Type d = a->digits_[i] - borrow;
|
||||
if (i < b->count_)
|
||||
d -= b->digits_[i];
|
||||
borrow = (d > a->digits_[i]) ? 1 : 0;
|
||||
out->digits_[i] = d;
|
||||
if (d != 0)
|
||||
out->count_ = i + 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Compare(const BigInteger& rhs) const {
|
||||
if (count_ != rhs.count_)
|
||||
return count_ < rhs.count_ ? -1 : 1;
|
||||
|
||||
for (size_t i = count_; i-- > 0;)
|
||||
if (digits_[i] != rhs.digits_[i])
|
||||
return digits_[i] < rhs.digits_[i] ? -1 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GetCount() const { return count_; }
|
||||
Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
|
||||
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
|
||||
|
||||
private:
|
||||
void AppendDecimal64(const char* begin, const char* end) {
|
||||
uint64_t u = ParseUint64(begin, end);
|
||||
if (IsZero())
|
||||
*this = u;
|
||||
else {
|
||||
unsigned exp = static_cast<unsigned>(end - begin);
|
||||
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
|
||||
}
|
||||
}
|
||||
|
||||
void PushBack(Type digit) {
|
||||
RAPIDJSON_ASSERT(count_ < kCapacity);
|
||||
digits_[count_++] = digit;
|
||||
}
|
||||
|
||||
static uint64_t ParseUint64(const char* begin, const char* end) {
|
||||
uint64_t r = 0;
|
||||
for (const char* p = begin; p != end; ++p) {
|
||||
RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
|
||||
r = r * 10 + (*p - '0');
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Assume a * b + k < 2^128
|
||||
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t low = _umul128(a, b, outHigh) + k;
|
||||
if (low < k)
|
||||
(*outHigh)++;
|
||||
return low;
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
|
||||
p += k;
|
||||
*outHigh = p >> 64;
|
||||
return static_cast<uint64_t>(p);
|
||||
#else
|
||||
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
|
||||
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
|
||||
x1 += (x0 >> 32); // can't give carry
|
||||
x1 += x2;
|
||||
if (x1 < x2)
|
||||
x3 += (static_cast<uint64_t>(1) << 32);
|
||||
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
|
||||
uint64_t hi = x3 + (x1 >> 32);
|
||||
|
||||
lo += k;
|
||||
if (lo < k)
|
||||
hi++;
|
||||
*outHigh = hi;
|
||||
return lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
||||
static const size_t kCapacity = kBitCount / sizeof(Type);
|
||||
static const size_t kTypeBit = sizeof(Type) * 8;
|
||||
|
||||
Type digits_[kCapacity];
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_BIGINTEGER_H_
|
||||
@@ -0,0 +1,247 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DIYFP_H_
|
||||
#define RAPIDJSON_DIYFP_H_
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#if defined(_M_AMD64)
|
||||
#pragma intrinsic(_BitScanReverse64)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
struct DiyFp {
|
||||
DiyFp() {}
|
||||
|
||||
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
|
||||
|
||||
explicit DiyFp(double d) {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
} u = { d };
|
||||
|
||||
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
|
||||
uint64_t significand = (u.u64 & kDpSignificandMask);
|
||||
if (biased_e != 0) {
|
||||
f = significand + kDpHiddenBit;
|
||||
e = biased_e - kDpExponentBias;
|
||||
}
|
||||
else {
|
||||
f = significand;
|
||||
e = kDpMinExponent + 1;
|
||||
}
|
||||
}
|
||||
|
||||
DiyFp operator-(const DiyFp& rhs) const {
|
||||
return DiyFp(f - rhs.f, e);
|
||||
}
|
||||
|
||||
DiyFp operator*(const DiyFp& rhs) const {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t h;
|
||||
uint64_t l = _umul128(f, rhs.f, &h);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
|
||||
uint64_t h = p >> 64;
|
||||
uint64_t l = static_cast<uint64_t>(p);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#else
|
||||
const uint64_t M32 = 0xFFFFFFFF;
|
||||
const uint64_t a = f >> 32;
|
||||
const uint64_t b = f & M32;
|
||||
const uint64_t c = rhs.f >> 32;
|
||||
const uint64_t d = rhs.f & M32;
|
||||
const uint64_t ac = a * c;
|
||||
const uint64_t bc = b * c;
|
||||
const uint64_t ad = a * d;
|
||||
const uint64_t bd = b * d;
|
||||
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
|
||||
tmp += 1U << 31; /// mult_round
|
||||
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
|
||||
#endif
|
||||
}
|
||||
|
||||
DiyFp Normalize() const {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
unsigned long index;
|
||||
_BitScanReverse64(&index, f);
|
||||
return DiyFp(f << (63 - index), e - (63 - index));
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
int s = __builtin_clzll(f);
|
||||
return DiyFp(f << s, e - s);
|
||||
#else
|
||||
DiyFp res = *this;
|
||||
while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
DiyFp NormalizeBoundary() const {
|
||||
DiyFp res = *this;
|
||||
while (!(res.f & (kDpHiddenBit << 1))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
|
||||
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
|
||||
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
|
||||
mi.f <<= mi.e - pl.e;
|
||||
mi.e = pl.e;
|
||||
*plus = pl;
|
||||
*minus = mi;
|
||||
}
|
||||
|
||||
double ToDouble() const {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
}u;
|
||||
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||
static_cast<uint64_t>(e + kDpExponentBias);
|
||||
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static const int kDiySignificandSize = 64;
|
||||
static const int kDpSignificandSize = 52;
|
||||
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
||||
static const int kDpMinExponent = -kDpExponentBias;
|
||||
static const int kDpDenormalExponent = -kDpExponentBias + 1;
|
||||
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
uint64_t f;
|
||||
int e;
|
||||
};
|
||||
|
||||
inline DiyFp GetCachedPowerByIndex(size_t index) {
|
||||
// 10^-348, 10^-340, ..., 10^340
|
||||
static const uint64_t kCachedPowers_F[] = {
|
||||
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
|
||||
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
|
||||
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
|
||||
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
|
||||
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
|
||||
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
|
||||
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
|
||||
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
|
||||
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
|
||||
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
|
||||
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
|
||||
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
|
||||
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
|
||||
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
|
||||
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
|
||||
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
|
||||
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
|
||||
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
|
||||
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
|
||||
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
|
||||
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
|
||||
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
|
||||
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
|
||||
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
|
||||
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
|
||||
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
|
||||
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
|
||||
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
|
||||
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
|
||||
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
|
||||
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
|
||||
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
|
||||
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
|
||||
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
|
||||
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
|
||||
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
|
||||
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
|
||||
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
|
||||
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
|
||||
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
|
||||
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
|
||||
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
|
||||
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
|
||||
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
|
||||
};
|
||||
static const int16_t kCachedPowers_E[] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
|
||||
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
|
||||
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
|
||||
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
|
||||
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
|
||||
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
|
||||
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
|
||||
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
|
||||
907, 933, 960, 986, 1013, 1039, 1066
|
||||
};
|
||||
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower(int e, int* K) {
|
||||
|
||||
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
||||
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
|
||||
int k = static_cast<int>(dk);
|
||||
if (dk - k > 0.0)
|
||||
k++;
|
||||
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
|
||||
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower10(int exp, int *outExp) {
|
||||
unsigned index = (exp + 348) / 8;
|
||||
*outExp = -348 + index * 8;
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DIYFP_H_
|
||||
@@ -0,0 +1,217 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DTOA_
|
||||
#define RAPIDJSON_DTOA_
|
||||
|
||||
#include "itoa.h" // GetDigitsLut()
|
||||
#include "diyfp.h"
|
||||
#include "ieee754.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
|
||||
while (rest < wp_w && delta - rest >= ten_kappa &&
|
||||
(rest + ten_kappa < wp_w || /// closer
|
||||
wp_w - rest > rest + ten_kappa - wp_w)) {
|
||||
buffer[len - 1]--;
|
||||
rest += ten_kappa;
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned CountDecimalDigit32(uint32_t n) {
|
||||
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
|
||||
if (n < 10) return 1;
|
||||
if (n < 100) return 2;
|
||||
if (n < 1000) return 3;
|
||||
if (n < 10000) return 4;
|
||||
if (n < 100000) return 5;
|
||||
if (n < 1000000) return 6;
|
||||
if (n < 10000000) return 7;
|
||||
if (n < 100000000) return 8;
|
||||
// Will not reach 10 digits in DigitGen()
|
||||
//if (n < 1000000000) return 9;
|
||||
//return 10;
|
||||
return 9;
|
||||
}
|
||||
|
||||
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
||||
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
||||
const DiyFp wp_w = Mp - W;
|
||||
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||
uint64_t p2 = Mp.f & (one.f - 1);
|
||||
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
|
||||
*len = 0;
|
||||
|
||||
while (kappa > 0) {
|
||||
uint32_t d = 0;
|
||||
switch (kappa) {
|
||||
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
||||
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
||||
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
||||
case 6: d = p1 / 100000; p1 %= 100000; break;
|
||||
case 5: d = p1 / 10000; p1 %= 10000; break;
|
||||
case 4: d = p1 / 1000; p1 %= 1000; break;
|
||||
case 3: d = p1 / 100; p1 %= 100; break;
|
||||
case 2: d = p1 / 10; p1 %= 10; break;
|
||||
case 1: d = p1; p1 = 0; break;
|
||||
default:;
|
||||
}
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||
if (tmp <= delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// kappa = 0
|
||||
for (;;) {
|
||||
p2 *= 10;
|
||||
delta *= 10;
|
||||
char d = static_cast<char>(p2 >> -one.e);
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + d);
|
||||
p2 &= one.f - 1;
|
||||
kappa--;
|
||||
if (p2 < delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Grisu2(double value, char* buffer, int* length, int* K) {
|
||||
const DiyFp v(value);
|
||||
DiyFp w_m, w_p;
|
||||
v.NormalizedBoundaries(&w_m, &w_p);
|
||||
|
||||
const DiyFp c_mk = GetCachedPower(w_p.e, K);
|
||||
const DiyFp W = v.Normalize() * c_mk;
|
||||
DiyFp Wp = w_p * c_mk;
|
||||
DiyFp Wm = w_m * c_mk;
|
||||
Wm.f++;
|
||||
Wp.f--;
|
||||
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
|
||||
}
|
||||
|
||||
inline char* WriteExponent(int K, char* buffer) {
|
||||
if (K < 0) {
|
||||
*buffer++ = '-';
|
||||
K = -K;
|
||||
}
|
||||
|
||||
if (K >= 100) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
|
||||
K %= 100;
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else if (K >= 10) {
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* Prettify(char* buffer, int length, int k) {
|
||||
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
|
||||
if (length <= kk && kk <= 21) {
|
||||
// 1234e7 -> 12340000000
|
||||
for (int i = length; i < kk; i++)
|
||||
buffer[i] = '0';
|
||||
buffer[kk] = '.';
|
||||
buffer[kk + 1] = '0';
|
||||
return &buffer[kk + 2];
|
||||
}
|
||||
else if (0 < kk && kk <= 21) {
|
||||
// 1234e-2 -> 12.34
|
||||
std::memmove(&buffer[kk + 1], &buffer[kk], length - kk);
|
||||
buffer[kk] = '.';
|
||||
return &buffer[length + 1];
|
||||
}
|
||||
else if (-6 < kk && kk <= 0) {
|
||||
// 1234e-6 -> 0.001234
|
||||
const int offset = 2 - kk;
|
||||
std::memmove(&buffer[offset], &buffer[0], length);
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
for (int i = 2; i < offset; i++)
|
||||
buffer[i] = '0';
|
||||
return &buffer[length + offset];
|
||||
}
|
||||
else if (length == 1) {
|
||||
// 1e30
|
||||
buffer[1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[2]);
|
||||
}
|
||||
else {
|
||||
// 1234e30 -> 1.234e33
|
||||
std::memmove(&buffer[2], &buffer[1], length - 1);
|
||||
buffer[1] = '.';
|
||||
buffer[length + 1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
inline char* dtoa(double value, char* buffer) {
|
||||
Double d(value);
|
||||
if (d.IsZero()) {
|
||||
if (d.Sign())
|
||||
*buffer++ = '-'; // -0.0, Issue #289
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else {
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
value = -value;
|
||||
}
|
||||
int length, K;
|
||||
Grisu2(value, buffer, &length, &K);
|
||||
return Prettify(buffer, length, K);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DTOA_
|
||||
@@ -0,0 +1,77 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_IEEE754_
|
||||
#define RAPIDJSON_IEEE754_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class Double {
|
||||
public:
|
||||
Double() {}
|
||||
Double(double d) : d(d) {}
|
||||
Double(uint64_t u) : u(u) {}
|
||||
|
||||
double Value() const { return d; }
|
||||
uint64_t Uint64Value() const { return u; }
|
||||
|
||||
double NextPositiveDouble() const {
|
||||
RAPIDJSON_ASSERT(!Sign());
|
||||
return Double(u + 1).Value();
|
||||
}
|
||||
|
||||
bool Sign() const { return (u & kSignMask) != 0; }
|
||||
uint64_t Significand() const { return u & kSignificandMask; }
|
||||
int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; }
|
||||
|
||||
bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; }
|
||||
bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; }
|
||||
bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; }
|
||||
bool IsZero() const { return (u & (kExponentMask | kSignificandMask)) == 0; }
|
||||
|
||||
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
|
||||
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
|
||||
uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; }
|
||||
|
||||
static unsigned EffectiveSignificandSize(int order) {
|
||||
if (order >= -1021)
|
||||
return 53;
|
||||
else if (order <= -1074)
|
||||
return 0;
|
||||
else
|
||||
return order + 1074;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kSignificandSize = 52;
|
||||
static const int kExponentBias = 0x3FF;
|
||||
static const int kDenormalExponent = 1 - kExponentBias;
|
||||
static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
|
||||
static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
union {
|
||||
double d;
|
||||
uint64_t u;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_IEEE754_
|
||||
@@ -0,0 +1,304 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ITOA_
|
||||
#define RAPIDJSON_ITOA_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline const char* GetDigitsLut() {
|
||||
static const char cDigitsLut[200] = {
|
||||
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
|
||||
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
|
||||
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
|
||||
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
|
||||
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
|
||||
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
|
||||
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
|
||||
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
|
||||
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
|
||||
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
|
||||
};
|
||||
return cDigitsLut;
|
||||
}
|
||||
|
||||
inline char* u32toa(uint32_t value, char* buffer) {
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
|
||||
if (value < 10000) {
|
||||
const uint32_t d1 = (value / 100) << 1;
|
||||
const uint32_t d2 = (value % 100) << 1;
|
||||
|
||||
if (value >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else if (value < 100000000) {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = value / 10000;
|
||||
const uint32_t c = value % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
else {
|
||||
// value = aabbbbcccc in decimal
|
||||
|
||||
const uint32_t a = value / 100000000; // 1 to 42
|
||||
value %= 100000000;
|
||||
|
||||
if (a >= 10) {
|
||||
const unsigned i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
|
||||
const uint32_t b = value / 10000; // 0 to 9999
|
||||
const uint32_t c = value % 10000; // 0 to 9999
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i32toa(int32_t value, char* buffer) {
|
||||
uint32_t u = static_cast<uint32_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u32toa(u, buffer);
|
||||
}
|
||||
|
||||
inline char* u64toa(uint64_t value, char* buffer) {
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
const uint64_t kTen8 = 100000000;
|
||||
const uint64_t kTen9 = kTen8 * 10;
|
||||
const uint64_t kTen10 = kTen8 * 100;
|
||||
const uint64_t kTen11 = kTen8 * 1000;
|
||||
const uint64_t kTen12 = kTen8 * 10000;
|
||||
const uint64_t kTen13 = kTen8 * 100000;
|
||||
const uint64_t kTen14 = kTen8 * 1000000;
|
||||
const uint64_t kTen15 = kTen8 * 10000000;
|
||||
const uint64_t kTen16 = kTen8 * kTen8;
|
||||
|
||||
if (value < kTen8) {
|
||||
uint32_t v = static_cast<uint32_t>(value);
|
||||
if (v < 10000) {
|
||||
const uint32_t d1 = (v / 100) << 1;
|
||||
const uint32_t d2 = (v % 100) << 1;
|
||||
|
||||
if (v >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (v >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (v >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = v / 10000;
|
||||
const uint32_t c = v % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
}
|
||||
else if (value < kTen16) {
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
if (value >= kTen15)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= kTen14)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= kTen13)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
if (value >= kTen12)
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
if (value >= kTen11)
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
if (value >= kTen10)
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
if (value >= kTen9)
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
if (value >= kTen8)
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
|
||||
value %= kTen16;
|
||||
|
||||
if (a < 10)
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
else if (a < 100) {
|
||||
const uint32_t i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else if (a < 1000) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
|
||||
|
||||
const uint32_t i = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t i = (a / 100) << 1;
|
||||
const uint32_t j = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
*buffer++ = cDigitsLut[j];
|
||||
*buffer++ = cDigitsLut[j + 1];
|
||||
}
|
||||
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i64toa(int64_t value, char* buffer) {
|
||||
uint64_t u = static_cast<uint64_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u64toa(u, buffer);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ITOA_
|
||||
@@ -0,0 +1,183 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_META_H_
|
||||
#define RAPIDJSON_INTERNAL_META_H_
|
||||
|
||||
#ifndef RAPIDJSON_RAPIDJSON_H_
|
||||
#error <rapidjson.h> not yet included. Do not include this file directly.
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(6334)
|
||||
#endif
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
//@cond RAPIDJSON_INTERNAL
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
|
||||
template <typename T> struct Void { typedef void Type; };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BoolType, TrueType, FalseType
|
||||
//
|
||||
template <bool Cond> struct BoolType {
|
||||
static const bool Value = Cond;
|
||||
typedef BoolType Type;
|
||||
};
|
||||
typedef BoolType<true> TrueType;
|
||||
typedef BoolType<false> FalseType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
|
||||
//
|
||||
|
||||
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
|
||||
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
|
||||
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
|
||||
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
|
||||
|
||||
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
|
||||
template <> struct AndExprCond<true, true> : TrueType {};
|
||||
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
|
||||
template <> struct OrExprCond<false, false> : FalseType {};
|
||||
|
||||
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
|
||||
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
|
||||
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
|
||||
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AddConst, MaybeAddConst, RemoveConst
|
||||
template <typename T> struct AddConst { typedef const T Type; };
|
||||
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||
template <typename T> struct RemoveConst { typedef T Type; };
|
||||
template <typename T> struct RemoveConst<const T> { typedef T Type; };
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsSame, IsConst, IsMoreConst, IsPointer
|
||||
//
|
||||
template <typename T, typename U> struct IsSame : FalseType {};
|
||||
template <typename T> struct IsSame<T, T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsConst : FalseType {};
|
||||
template <typename T> struct IsConst<const T> : TrueType {};
|
||||
|
||||
template <typename CT, typename T>
|
||||
struct IsMoreConst
|
||||
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
|
||||
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
|
||||
|
||||
template <typename T> struct IsPointer : FalseType {};
|
||||
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsBaseOf
|
||||
//
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: BoolType< ::std::is_base_of<B,D>::value> {};
|
||||
|
||||
#else // simplified version adopted from Boost
|
||||
|
||||
template<typename B, typename D> struct IsBaseOfImpl {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
|
||||
|
||||
typedef char (&Yes)[1];
|
||||
typedef char (&No) [2];
|
||||
|
||||
template <typename T>
|
||||
static Yes Check(const D*, T);
|
||||
static No Check(const B*, int);
|
||||
|
||||
struct Host {
|
||||
operator const B*() const;
|
||||
operator const D*();
|
||||
};
|
||||
|
||||
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
|
||||
};
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
|
||||
|
||||
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EnableIf / DisableIf
|
||||
//
|
||||
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
|
||||
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
|
||||
|
||||
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
|
||||
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct EnableIf : EnableIfCond<Condition::Value, T> {};
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct DisableIf : DisableIfCond<Condition::Value, T> {};
|
||||
|
||||
// SFINAE helpers
|
||||
struct SfinaeTag {};
|
||||
template <typename T> struct RemoveSfinaeTag;
|
||||
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
|
||||
|
||||
#define RAPIDJSON_REMOVEFPTR_(type) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
|
||||
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
|
||||
|
||||
#define RAPIDJSON_ENABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_DISABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
//@endcond
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_META_H_
|
||||
@@ -0,0 +1,53 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_POW10_
|
||||
#define RAPIDJSON_POW10_
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Computes integer powers of 10 in double (10.0^n).
|
||||
/*! This function uses lookup table for fast and accurate results.
|
||||
\param n non-negative exponent. Must <= 308.
|
||||
\return 10.0^n
|
||||
*/
|
||||
inline double Pow10(int n) {
|
||||
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
|
||||
1e+0,
|
||||
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
|
||||
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
|
||||
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
|
||||
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
|
||||
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
|
||||
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
|
||||
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
|
||||
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
|
||||
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
|
||||
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
|
||||
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
|
||||
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
|
||||
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
|
||||
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
|
||||
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
|
||||
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
|
||||
};
|
||||
RAPIDJSON_ASSERT(n >= 0 && n <= 308);
|
||||
return e[n];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_POW10_
|
||||
@@ -0,0 +1,177 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STACK_H_
|
||||
#define RAPIDJSON_INTERNAL_STACK_H_
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stack
|
||||
|
||||
//! A type-unsafe stack for storing different types of data.
|
||||
/*! \tparam Allocator Allocator for allocating stack memory.
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class Stack {
|
||||
public:
|
||||
// Optimization note: Do not allocate memory for stack_ in constructor.
|
||||
// Do it lazily when first Push() -> Expand() -> Resize().
|
||||
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
|
||||
RAPIDJSON_ASSERT(stackCapacity > 0);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack(Stack&& rhs)
|
||||
: allocator_(rhs.allocator_),
|
||||
ownAllocator_(rhs.ownAllocator_),
|
||||
stack_(rhs.stack_),
|
||||
stackTop_(rhs.stackTop_),
|
||||
stackEnd_(rhs.stackEnd_),
|
||||
initialCapacity_(rhs.initialCapacity_)
|
||||
{
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
~Stack() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack& operator=(Stack&& rhs) {
|
||||
if (&rhs != this)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
allocator_ = rhs.allocator_;
|
||||
ownAllocator_ = rhs.ownAllocator_;
|
||||
stack_ = rhs.stack_;
|
||||
stackTop_ = rhs.stackTop_;
|
||||
stackEnd_ = rhs.stackEnd_;
|
||||
initialCapacity_ = rhs.initialCapacity_;
|
||||
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Clear() { stackTop_ = stack_; }
|
||||
|
||||
void ShrinkToFit() {
|
||||
if (Empty()) {
|
||||
// If the stack is empty, completely deallocate the memory.
|
||||
Allocator::Free(stack_);
|
||||
stack_ = 0;
|
||||
stackTop_ = 0;
|
||||
stackEnd_ = 0;
|
||||
}
|
||||
else
|
||||
Resize(GetSize());
|
||||
}
|
||||
|
||||
// Optimization note: try to minimize the size of this function for force inline.
|
||||
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (stackTop_ + sizeof(T) * count >= stackEnd_)
|
||||
Expand<T>(count);
|
||||
|
||||
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||
stackTop_ += sizeof(T) * count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Pop(size_t count) {
|
||||
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||
stackTop_ -= count * sizeof(T);
|
||||
return reinterpret_cast<T*>(stackTop_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Top() {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Bottom() { return (T*)stack_; }
|
||||
|
||||
Allocator& GetAllocator() { return *allocator_; }
|
||||
bool Empty() const { return stackTop_ == stack_; }
|
||||
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
|
||||
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void Expand(size_t count) {
|
||||
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
|
||||
size_t newCapacity;
|
||||
if (stack_ == 0) {
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
|
||||
newCapacity = initialCapacity_;
|
||||
} else {
|
||||
newCapacity = GetCapacity();
|
||||
newCapacity += (newCapacity + 1) / 2;
|
||||
}
|
||||
size_t newSize = GetSize() + sizeof(T) * count;
|
||||
if (newCapacity < newSize)
|
||||
newCapacity = newSize;
|
||||
|
||||
Resize(newCapacity);
|
||||
}
|
||||
|
||||
void Resize(size_t newCapacity) {
|
||||
const size_t size = GetSize(); // Backup the current size
|
||||
stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity);
|
||||
stackTop_ = stack_ + size;
|
||||
stackEnd_ = stack_ + newCapacity;
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
Allocator::Free(stack_);
|
||||
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
|
||||
}
|
||||
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Stack(const Stack&);
|
||||
Stack& operator=(const Stack&);
|
||||
|
||||
Allocator* allocator_;
|
||||
Allocator* ownAllocator_;
|
||||
char *stack_;
|
||||
char *stackTop_;
|
||||
char *stackEnd_;
|
||||
size_t initialCapacity_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STACK_H_
|
||||
@@ -0,0 +1,37 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Custom strlen() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s Null-terminated input string.
|
||||
\return Number of characters in the string.
|
||||
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
|
||||
*/
|
||||
template <typename Ch>
|
||||
inline SizeType StrLen(const Ch* s) {
|
||||
const Ch* p = s;
|
||||
while (*p) ++p;
|
||||
return SizeType(p - s);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
@@ -0,0 +1,265 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRTOD_
|
||||
#define RAPIDJSON_STRTOD_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
#include "ieee754.h"
|
||||
#include "biginteger.h"
|
||||
#include "diyfp.h"
|
||||
#include "pow10.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline double FastPath(double significand, int exp) {
|
||||
if (exp < -308)
|
||||
return 0.0;
|
||||
else if (exp >= 0)
|
||||
return significand * internal::Pow10(exp);
|
||||
else
|
||||
return significand / internal::Pow10(-exp);
|
||||
}
|
||||
|
||||
inline double StrtodNormalPrecision(double d, int p) {
|
||||
if (p < -308) {
|
||||
// Prevent expSum < -308, making Pow10(p) = 0
|
||||
d = FastPath(d, -308);
|
||||
d = FastPath(d, p + 308);
|
||||
}
|
||||
else
|
||||
d = FastPath(d, p);
|
||||
return d;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Min3(T a, T b, T c) {
|
||||
T m = a;
|
||||
if (m > b) m = b;
|
||||
if (m > c) m = c;
|
||||
return m;
|
||||
}
|
||||
|
||||
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
|
||||
const Double db(b);
|
||||
const uint64_t bInt = db.IntegerSignificand();
|
||||
const int bExp = db.IntegerExponent();
|
||||
const int hExp = bExp - 1;
|
||||
|
||||
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
|
||||
|
||||
// Adjust for decimal exponent
|
||||
if (dExp >= 0) {
|
||||
dS_Exp2 += dExp;
|
||||
dS_Exp5 += dExp;
|
||||
}
|
||||
else {
|
||||
bS_Exp2 -= dExp;
|
||||
bS_Exp5 -= dExp;
|
||||
hS_Exp2 -= dExp;
|
||||
hS_Exp5 -= dExp;
|
||||
}
|
||||
|
||||
// Adjust for binary exponent
|
||||
if (bExp >= 0)
|
||||
bS_Exp2 += bExp;
|
||||
else {
|
||||
dS_Exp2 -= bExp;
|
||||
hS_Exp2 -= bExp;
|
||||
}
|
||||
|
||||
// Adjust for half ulp exponent
|
||||
if (hExp >= 0)
|
||||
hS_Exp2 += hExp;
|
||||
else {
|
||||
dS_Exp2 -= hExp;
|
||||
bS_Exp2 -= hExp;
|
||||
}
|
||||
|
||||
// Remove common power of two factor from all three scaled values
|
||||
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
|
||||
dS_Exp2 -= common_Exp2;
|
||||
bS_Exp2 -= common_Exp2;
|
||||
hS_Exp2 -= common_Exp2;
|
||||
|
||||
BigInteger dS = d;
|
||||
dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2;
|
||||
|
||||
BigInteger bS(bInt);
|
||||
bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2;
|
||||
|
||||
BigInteger hS(1);
|
||||
hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2;
|
||||
|
||||
BigInteger delta(0);
|
||||
dS.Difference(bS, &delta);
|
||||
|
||||
return delta.Compare(hS);
|
||||
}
|
||||
|
||||
inline bool StrtodFast(double d, int p, double* result) {
|
||||
// Use fast path for string-to-double conversion if possible
|
||||
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||
if (p > 22 && p < 22 + 16) {
|
||||
// Fast Path Cases In Disguise
|
||||
d *= internal::Pow10(p - 22);
|
||||
p = 22;
|
||||
}
|
||||
|
||||
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
|
||||
*result = FastPath(d, p);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute an approximation and see if it is within 1/2 ULP
|
||||
inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
|
||||
uint64_t significand = 0;
|
||||
size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
||||
for (; i < length; i++) {
|
||||
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
|
||||
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
|
||||
break;
|
||||
significand = significand * 10 + (decimals[i] - '0');
|
||||
}
|
||||
|
||||
if (i < length && decimals[i] >= '5') // Rounding
|
||||
significand++;
|
||||
|
||||
size_t remaining = length - i;
|
||||
const unsigned kUlpShift = 3;
|
||||
const unsigned kUlp = 1 << kUlpShift;
|
||||
int error = (remaining == 0) ? 0 : kUlp / 2;
|
||||
|
||||
DiyFp v(significand, 0);
|
||||
v = v.Normalize();
|
||||
error <<= -v.e;
|
||||
|
||||
const int dExp = (int)decimalPosition - (int)i + exp;
|
||||
|
||||
int actualExp;
|
||||
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
||||
if (actualExp != dExp) {
|
||||
static const DiyFp kPow10[] = {
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
|
||||
};
|
||||
int adjustment = dExp - actualExp - 1;
|
||||
RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
|
||||
v = v * kPow10[adjustment];
|
||||
if (length + adjustment > 19) // has more digits than decimal digits in 64-bit
|
||||
error += kUlp / 2;
|
||||
}
|
||||
|
||||
v = v * cachedPower;
|
||||
|
||||
error += kUlp + (error == 0 ? 0 : 1);
|
||||
|
||||
const int oldExp = v.e;
|
||||
v = v.Normalize();
|
||||
error <<= oldExp - v.e;
|
||||
|
||||
const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
|
||||
unsigned precisionSize = 64 - effectiveSignificandSize;
|
||||
if (precisionSize + kUlpShift >= 64) {
|
||||
unsigned scaleExp = (precisionSize + kUlpShift) - 63;
|
||||
v.f >>= scaleExp;
|
||||
v.e += scaleExp;
|
||||
error = (error >> scaleExp) + 1 + kUlp;
|
||||
precisionSize -= scaleExp;
|
||||
}
|
||||
|
||||
DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
|
||||
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
|
||||
if (precisionBits >= halfWay + error)
|
||||
rounded.f++;
|
||||
|
||||
*result = rounded.ToDouble();
|
||||
|
||||
return halfWay - error >= precisionBits || precisionBits >= halfWay + error;
|
||||
}
|
||||
|
||||
inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||
const BigInteger dInt(decimals, length);
|
||||
const int dExp = (int)decimalPosition - (int)length + exp;
|
||||
Double a(approx);
|
||||
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
||||
if (cmp < 0)
|
||||
return a.Value(); // within half ULP
|
||||
else if (cmp == 0) {
|
||||
// Round towards even
|
||||
if (a.Significand() & 1)
|
||||
return a.NextPositiveDouble();
|
||||
else
|
||||
return a.Value();
|
||||
}
|
||||
else // adjustment
|
||||
return a.NextPositiveDouble();
|
||||
}
|
||||
|
||||
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||
RAPIDJSON_ASSERT(d >= 0.0);
|
||||
RAPIDJSON_ASSERT(length >= 1);
|
||||
|
||||
double result;
|
||||
if (StrtodFast(d, p, &result))
|
||||
return result;
|
||||
|
||||
// Trim leading zeros
|
||||
while (*decimals == '0' && length > 1) {
|
||||
length--;
|
||||
decimals++;
|
||||
decimalPosition--;
|
||||
}
|
||||
|
||||
// Trim trailing zeros
|
||||
while (decimals[length - 1] == '0' && length > 1) {
|
||||
length--;
|
||||
decimalPosition--;
|
||||
exp++;
|
||||
}
|
||||
|
||||
// Trim right-most digits
|
||||
const int kMaxDecimalDigit = 780;
|
||||
if ((int)length > kMaxDecimalDigit) {
|
||||
int delta = (int(length) - kMaxDecimalDigit);
|
||||
exp += delta;
|
||||
decimalPosition -= delta;
|
||||
length = kMaxDecimalDigit;
|
||||
}
|
||||
|
||||
// If too small, underflow to zero
|
||||
if (int(length) + exp < -324)
|
||||
return 0.0;
|
||||
|
||||
if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
|
||||
return result;
|
||||
|
||||
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
|
||||
return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STRTOD_
|
||||
@@ -0,0 +1,57 @@
|
||||
Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
|
||||
Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
|
||||
If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License.
|
||||
If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON.
|
||||
A copy of the MIT License is included in this file.
|
||||
|
||||
Other dependencies and licenses:
|
||||
|
||||
Open Source Software Licensed Under the BSD License:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
The msinttypes r29
|
||||
Copyright (c) 2006-2013 Alexander Chemeris
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Open Source Software Licensed Under the JSON License:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
json.org
|
||||
Copyright (c) 2002 JSON.org
|
||||
All Rights Reserved.
|
||||
|
||||
JSON_checker
|
||||
Copyright (c) 2002 JSON.org
|
||||
All Rights Reserved.
|
||||
|
||||
|
||||
Terms of the JSON License:
|
||||
---------------------------------------------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Terms of the MIT License:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,70 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYBUFFER_H_
|
||||
#define RAPIDJSON_MEMORYBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
|
||||
|
||||
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryBuffer and StringBuffer:
|
||||
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
|
||||
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
|
||||
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Allocator = CrtAllocator>
|
||||
struct GenericMemoryBuffer {
|
||||
typedef char Ch; // byte
|
||||
|
||||
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() { stack_.ShrinkToFit(); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetBuffer() const {
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
};
|
||||
|
||||
typedef GenericMemoryBuffer<> MemoryBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
|
||||
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
||||
@@ -0,0 +1,61 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYSTREAM_H_
|
||||
#define RAPIDJSON_MEMORYSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory input byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
|
||||
|
||||
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryStream and StringStream:
|
||||
1. StringStream has encoding but MemoryStream is a byte stream.
|
||||
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
|
||||
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
|
||||
\note implements Stream concept
|
||||
*/
|
||||
struct MemoryStream {
|
||||
typedef char Ch; // byte
|
||||
|
||||
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
|
||||
|
||||
Ch Peek() const { return (src_ == end_) ? '\0' : *src_; }
|
||||
Ch Take() { return (src_ == end_) ? '\0' : *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return Tell() + 4 <= size_ ? src_ : 0;
|
||||
}
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* begin_; //!< Original head of the string.
|
||||
const Ch* end_; //!< End of stream.
|
||||
size_t size_; //!< Size of the stream.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
||||
@@ -0,0 +1,316 @@
|
||||
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_INTTYPES_H_ // [
|
||||
#define _MSC_INTTYPES_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
// miloyip: VC supports inttypes.h since VC2013
|
||||
#if _MSC_VER >= 1800
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
|
||||
// 7.8 Format conversion of integer types
|
||||
|
||||
typedef struct {
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
// 7.8.1 Macros for format specifiers
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
|
||||
|
||||
// The fprintf macros for signed integers are:
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIdLEAST16 "hd"
|
||||
#define PRIiLEAST16 "hi"
|
||||
#define PRIdFAST16 "hd"
|
||||
#define PRIiFAST16 "hi"
|
||||
|
||||
#define PRId32 "I32d"
|
||||
#define PRIi32 "I32i"
|
||||
#define PRIdLEAST32 "I32d"
|
||||
#define PRIiLEAST32 "I32i"
|
||||
#define PRIdFAST32 "I32d"
|
||||
#define PRIiFAST32 "I32i"
|
||||
|
||||
#define PRId64 "I64d"
|
||||
#define PRIi64 "I64i"
|
||||
#define PRIdLEAST64 "I64d"
|
||||
#define PRIiLEAST64 "I64i"
|
||||
#define PRIdFAST64 "I64d"
|
||||
#define PRIiFAST64 "I64i"
|
||||
|
||||
#define PRIdMAX "I64d"
|
||||
#define PRIiMAX "I64i"
|
||||
|
||||
#define PRIdPTR "Id"
|
||||
#define PRIiPTR "Ii"
|
||||
|
||||
// The fprintf macros for unsigned integers are:
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
#define PRIoLEAST16 "ho"
|
||||
#define PRIuLEAST16 "hu"
|
||||
#define PRIxLEAST16 "hx"
|
||||
#define PRIXLEAST16 "hX"
|
||||
#define PRIoFAST16 "ho"
|
||||
#define PRIuFAST16 "hu"
|
||||
#define PRIxFAST16 "hx"
|
||||
#define PRIXFAST16 "hX"
|
||||
|
||||
#define PRIo32 "I32o"
|
||||
#define PRIu32 "I32u"
|
||||
#define PRIx32 "I32x"
|
||||
#define PRIX32 "I32X"
|
||||
#define PRIoLEAST32 "I32o"
|
||||
#define PRIuLEAST32 "I32u"
|
||||
#define PRIxLEAST32 "I32x"
|
||||
#define PRIXLEAST32 "I32X"
|
||||
#define PRIoFAST32 "I32o"
|
||||
#define PRIuFAST32 "I32u"
|
||||
#define PRIxFAST32 "I32x"
|
||||
#define PRIXFAST32 "I32X"
|
||||
|
||||
#define PRIo64 "I64o"
|
||||
#define PRIu64 "I64u"
|
||||
#define PRIx64 "I64x"
|
||||
#define PRIX64 "I64X"
|
||||
#define PRIoLEAST64 "I64o"
|
||||
#define PRIuLEAST64 "I64u"
|
||||
#define PRIxLEAST64 "I64x"
|
||||
#define PRIXLEAST64 "I64X"
|
||||
#define PRIoFAST64 "I64o"
|
||||
#define PRIuFAST64 "I64u"
|
||||
#define PRIxFAST64 "I64x"
|
||||
#define PRIXFAST64 "I64X"
|
||||
|
||||
#define PRIoMAX "I64o"
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRIxMAX "I64x"
|
||||
#define PRIXMAX "I64X"
|
||||
|
||||
#define PRIoPTR "Io"
|
||||
#define PRIuPTR "Iu"
|
||||
#define PRIxPTR "Ix"
|
||||
#define PRIXPTR "IX"
|
||||
|
||||
// The fscanf macros for signed integers are:
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
|
||||
#define SCNd16 "hd"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNdFAST16 "hd"
|
||||
#define SCNiFAST16 "hi"
|
||||
|
||||
#define SCNd32 "ld"
|
||||
#define SCNi32 "li"
|
||||
#define SCNdLEAST32 "ld"
|
||||
#define SCNiLEAST32 "li"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNiFAST32 "li"
|
||||
|
||||
#define SCNd64 "I64d"
|
||||
#define SCNi64 "I64i"
|
||||
#define SCNdLEAST64 "I64d"
|
||||
#define SCNiLEAST64 "I64i"
|
||||
#define SCNdFAST64 "I64d"
|
||||
#define SCNiFAST64 "I64i"
|
||||
|
||||
#define SCNdMAX "I64d"
|
||||
#define SCNiMAX "I64i"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNdPTR "I64d"
|
||||
# define SCNiPTR "I64i"
|
||||
#else // _WIN64 ][
|
||||
# define SCNdPTR "ld"
|
||||
# define SCNiPTR "li"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// The fscanf macros for unsigned integers are:
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNX8 "X"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNXLEAST8 "X"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNXFAST8 "X"
|
||||
|
||||
#define SCNo16 "ho"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNX16 "hX"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNXLEAST16 "hX"
|
||||
#define SCNoFAST16 "ho"
|
||||
#define SCNuFAST16 "hu"
|
||||
#define SCNxFAST16 "hx"
|
||||
#define SCNXFAST16 "hX"
|
||||
|
||||
#define SCNo32 "lo"
|
||||
#define SCNu32 "lu"
|
||||
#define SCNx32 "lx"
|
||||
#define SCNX32 "lX"
|
||||
#define SCNoLEAST32 "lo"
|
||||
#define SCNuLEAST32 "lu"
|
||||
#define SCNxLEAST32 "lx"
|
||||
#define SCNXLEAST32 "lX"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNXFAST32 "lX"
|
||||
|
||||
#define SCNo64 "I64o"
|
||||
#define SCNu64 "I64u"
|
||||
#define SCNx64 "I64x"
|
||||
#define SCNX64 "I64X"
|
||||
#define SCNoLEAST64 "I64o"
|
||||
#define SCNuLEAST64 "I64u"
|
||||
#define SCNxLEAST64 "I64x"
|
||||
#define SCNXLEAST64 "I64X"
|
||||
#define SCNoFAST64 "I64o"
|
||||
#define SCNuFAST64 "I64u"
|
||||
#define SCNxFAST64 "I64x"
|
||||
#define SCNXFAST64 "I64X"
|
||||
|
||||
#define SCNoMAX "I64o"
|
||||
#define SCNuMAX "I64u"
|
||||
#define SCNxMAX "I64x"
|
||||
#define SCNXMAX "I64X"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNoPTR "I64o"
|
||||
# define SCNuPTR "I64u"
|
||||
# define SCNxPTR "I64x"
|
||||
# define SCNXPTR "I64X"
|
||||
#else // _WIN64 ][
|
||||
# define SCNoPTR "lo"
|
||||
# define SCNuPTR "lu"
|
||||
# define SCNxPTR "lx"
|
||||
# define SCNXPTR "lX"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#endif // __STDC_FORMAT_MACROS ]
|
||||
|
||||
// 7.8.2 Functions for greatest-width integer types
|
||||
|
||||
// 7.8.2.1 The imaxabs function
|
||||
#define imaxabs _abs64
|
||||
|
||||
// 7.8.2.2 The imaxdiv function
|
||||
|
||||
// This is modified version of div() function from Microsoft's div.c found
|
||||
// in %MSVC.NET%\crt\src\div.c
|
||||
#ifdef STATIC_IMAXDIV // [
|
||||
static
|
||||
#else // STATIC_IMAXDIV ][
|
||||
_inline
|
||||
#endif // STATIC_IMAXDIV ]
|
||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t result;
|
||||
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||
#define strtoimax _strtoi64
|
||||
#define strtoumax _strtoui64
|
||||
|
||||
// 7.8.2.4 The wcstoimax and wcstoumax functions
|
||||
#define wcstoimax _wcstoi64
|
||||
#define wcstoumax _wcstoui64
|
||||
|
||||
#endif // _MSC_VER >= 1800
|
||||
|
||||
#endif // _MSC_INTTYPES_H_ ]
|
||||
@@ -0,0 +1,300 @@
|
||||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
|
||||
#if _MSC_VER >= 1600 // [
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
#undef INT8_C
|
||||
#undef INT16_C
|
||||
#undef INT32_C
|
||||
#undef INT64_C
|
||||
#undef UINT8_C
|
||||
#undef UINT16_C
|
||||
#undef UINT32_C
|
||||
#undef UINT64_C
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#else // ] _MSC_VER >= 1700 [
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#endif // _MSC_VER >= 1600 ]
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
||||
@@ -0,0 +1,207 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_PRETTYWRITER_H_
|
||||
#define RAPIDJSON_PRETTYWRITER_H_
|
||||
|
||||
#include "writer.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Writer with indentation and spacing.
|
||||
/*!
|
||||
\tparam OutputStream Type of ouptut os.
|
||||
\tparam SourceEncoding Encoding of source string.
|
||||
\tparam TargetEncoding Encoding of output stream.
|
||||
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> {
|
||||
public:
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
|
||||
typedef typename Base::Ch Ch;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param allocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
||||
|
||||
//! Set custom indentation.
|
||||
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
||||
\param indentCharCount Number of indent characters for each indentation level.
|
||||
\note The default indentation is 4 spaces.
|
||||
*/
|
||||
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
|
||||
RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
|
||||
indentChar_ = indentChar;
|
||||
indentCharCount_ = indentCharCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! @name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
//@{
|
||||
|
||||
bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
|
||||
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
|
||||
bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
|
||||
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
|
||||
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
|
||||
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
PrettyPrefix(kStringType);
|
||||
return Base::WriteString(str, length);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool String(const std::basic_string<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
PrettyPrefix(kObjectType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
|
||||
return Base::WriteStartObject();
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
bool ret = Base::WriteEndObject();
|
||||
(void)ret;
|
||||
RAPIDJSON_ASSERT(ret == true);
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
PrettyPrefix(kArrayType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
|
||||
return Base::WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
bool ret = Base::WriteEndArray();
|
||||
(void)ret;
|
||||
RAPIDJSON_ASSERT(ret == true);
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/*! @name Convenience extensions */
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
protected:
|
||||
void PrettyPrefix(Type type) {
|
||||
(void)type;
|
||||
if (Base::level_stack_.GetSize() != 0) { // this value is not at root
|
||||
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
|
||||
|
||||
if (level->inArray) {
|
||||
if (level->valueCount > 0) {
|
||||
Base::os_->Put(','); // add comma if it is not the first element in array
|
||||
Base::os_->Put('\n');
|
||||
}
|
||||
else
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
else { // in object
|
||||
if (level->valueCount > 0) {
|
||||
if (level->valueCount % 2 == 0) {
|
||||
Base::os_->Put(',');
|
||||
Base::os_->Put('\n');
|
||||
}
|
||||
else {
|
||||
Base::os_->Put(':');
|
||||
Base::os_->Put(' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
Base::os_->Put('\n');
|
||||
|
||||
if (level->valueCount % 2 == 0)
|
||||
WriteIndent();
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
|
||||
Base::hasRoot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteIndent() {
|
||||
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
|
||||
PutN(*Base::os_, indentChar_, count);
|
||||
}
|
||||
|
||||
Ch indentChar_;
|
||||
unsigned indentCharCount_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
PrettyWriter(const PrettyWriter&);
|
||||
PrettyWriter& operator=(const PrettyWriter&);
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
@@ -0,0 +1,620 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_RAPIDJSON_H_
|
||||
#define RAPIDJSON_RAPIDJSON_H_
|
||||
|
||||
/*!\file rapidjson.h
|
||||
\brief common definitions and configuration
|
||||
|
||||
\see RAPIDJSON_CONFIG
|
||||
*/
|
||||
|
||||
/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
|
||||
\brief Configuration macros for library features
|
||||
|
||||
Some RapidJSON features are configurable to adapt the library to a wide
|
||||
variety of platforms, environments and usage scenarios. Most of the
|
||||
features can be configured in terms of overriden or predefined
|
||||
preprocessor macros at compile-time.
|
||||
|
||||
Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
|
||||
|
||||
\note These macros should be given on the compiler command-line
|
||||
(where applicable) to avoid inconsistent values when compiling
|
||||
different translation units of a single application.
|
||||
*/
|
||||
|
||||
#include <cstdlib> // malloc(), realloc(), free(), size_t
|
||||
#include <cstring> // memset(), memcpy(), memmove(), memcmp()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NAMESPACE_(BEGIN|END)
|
||||
/*! \def RAPIDJSON_NAMESPACE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace
|
||||
|
||||
In order to avoid symbol clashes and/or "One Definition Rule" errors
|
||||
between multiple inclusions of (different versions of) RapidJSON in
|
||||
a single binary, users can customize the name of the main RapidJSON
|
||||
namespace.
|
||||
|
||||
In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
|
||||
to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
|
||||
levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
|
||||
RAPIDJSON_NAMESPACE_END need to be defined as well:
|
||||
|
||||
\code
|
||||
// in some .cpp file
|
||||
#define RAPIDJSON_NAMESPACE my::rapidjson
|
||||
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
|
||||
#define RAPIDJSON_NAMESPACE_END } }
|
||||
#include "rapidjson/..."
|
||||
\endcode
|
||||
|
||||
\see rapidjson
|
||||
*/
|
||||
/*! \def RAPIDJSON_NAMESPACE_BEGIN
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace (opening expression)
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
/*! \def RAPIDJSON_NAMESPACE_END
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace (closing expression)
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
#ifndef RAPIDJSON_NAMESPACE
|
||||
#define RAPIDJSON_NAMESPACE rapidjson
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NAMESPACE_BEGIN
|
||||
#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NAMESPACE_END
|
||||
#define RAPIDJSON_NAMESPACE_END }
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_INT64DEFINE
|
||||
|
||||
/*! \def RAPIDJSON_NO_INT64DEFINE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Use external 64-bit integer types.
|
||||
|
||||
RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
|
||||
to be available at global scope.
|
||||
|
||||
If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
|
||||
prevent RapidJSON from defining its own types.
|
||||
*/
|
||||
#ifndef RAPIDJSON_NO_INT64DEFINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#ifdef _MSC_VER
|
||||
#include "msinttypes/stdint.h"
|
||||
#include "msinttypes/inttypes.h"
|
||||
#else
|
||||
// Other compilers should have this.
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
//!@endcond
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_NO_INT64DEFINE
|
||||
#endif
|
||||
#endif // RAPIDJSON_NO_INT64TYPEDEF
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_FORCEINLINE
|
||||
|
||||
#ifndef RAPIDJSON_FORCEINLINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#if defined(_MSC_VER) && !defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __forceinline
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define RAPIDJSON_FORCEINLINE
|
||||
#endif
|
||||
//!@endcond
|
||||
#endif // RAPIDJSON_FORCEINLINE
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ENDIAN
|
||||
#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
|
||||
#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
|
||||
|
||||
//! Endianness of the machine.
|
||||
/*!
|
||||
\def RAPIDJSON_ENDIAN
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
|
||||
GCC 4.6 provided macro for detecting endianness of the target machine. But other
|
||||
compilers may not have this. User can define RAPIDJSON_ENDIAN to either
|
||||
\ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
|
||||
|
||||
Default detection implemented with reference to
|
||||
\li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
|
||||
\li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
|
||||
*/
|
||||
#ifndef RAPIDJSON_ENDIAN
|
||||
// Detect with GCC 4.6's macro
|
||||
# ifdef __BYTE_ORDER__
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# else
|
||||
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif // __BYTE_ORDER__
|
||||
// Detect with GLIBC's endian.h
|
||||
# elif defined(__GLIBC__)
|
||||
# include <endian.h>
|
||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# else
|
||||
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif // __GLIBC__
|
||||
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
|
||||
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
// Detect with architecture macros
|
||||
# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
|
||||
# define RAPIDJSON_ENDIAN
|
||||
# else
|
||||
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif
|
||||
#endif // RAPIDJSON_ENDIAN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_64BIT
|
||||
|
||||
//! Whether using 64-bit architecture
|
||||
#ifndef RAPIDJSON_64BIT
|
||||
#if defined(__LP64__) || defined(_WIN64)
|
||||
#define RAPIDJSON_64BIT 1
|
||||
#else
|
||||
#define RAPIDJSON_64BIT 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_64BIT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ALIGN
|
||||
|
||||
//! Data alignment of the machine.
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
\param x pointer to align
|
||||
|
||||
Some machines require strict data alignment. Currently the default uses 4 bytes
|
||||
alignment. User can customize by defining the RAPIDJSON_ALIGN function macro.,
|
||||
*/
|
||||
#ifndef RAPIDJSON_ALIGN
|
||||
#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_UINT64_C2
|
||||
|
||||
//! Construct a 64-bit literal by a pair of 32-bit integer.
|
||||
/*!
|
||||
64-bit literal with or without ULL suffix is prone to compiler warnings.
|
||||
UINT64_C() is C macro which cause compilation problems.
|
||||
Use this macro to define 64-bit constants by a pair of 32-bit integer.
|
||||
*/
|
||||
#ifndef RAPIDJSON_UINT64_C2
|
||||
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
|
||||
|
||||
/*! \def RAPIDJSON_SIMD
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Enable SSE2/SSE4.2 optimization.
|
||||
|
||||
RapidJSON supports optimized implementations for some parsing operations
|
||||
based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible
|
||||
processors.
|
||||
|
||||
To enable these optimizations, two different symbols can be defined;
|
||||
\code
|
||||
// Enable SSE2 optimization.
|
||||
#define RAPIDJSON_SSE2
|
||||
|
||||
// Enable SSE4.2 optimization.
|
||||
#define RAPIDJSON_SSE42
|
||||
\endcode
|
||||
|
||||
\c RAPIDJSON_SSE42 takes precedence, if both are defined.
|
||||
|
||||
If any of these symbols is defined, RapidJSON defines the macro
|
||||
\c RAPIDJSON_SIMD to indicate the availability of the optimized code.
|
||||
*/
|
||||
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
|
||||
|| defined(RAPIDJSON_DOXYGEN_RUNNING)
|
||||
#define RAPIDJSON_SIMD
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
|
||||
#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief User-provided \c SizeType definition.
|
||||
|
||||
In order to avoid using 32-bit size types for indexing strings and arrays,
|
||||
define this preprocessor symbol and provide the type rapidjson::SizeType
|
||||
before including RapidJSON:
|
||||
\code
|
||||
#define RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
namespace rapidjson { typedef ::std::size_t SizeType; }
|
||||
#include "rapidjson/..."
|
||||
\endcode
|
||||
|
||||
\see rapidjson::SizeType
|
||||
*/
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
#endif
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
//! Size type (for string lengths, array sizes, etc.)
|
||||
/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
|
||||
instead of using \c size_t. Users may override the SizeType by defining
|
||||
\ref RAPIDJSON_NO_SIZETYPEDEFINE.
|
||||
*/
|
||||
typedef unsigned SizeType;
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
#endif
|
||||
|
||||
// always import std::size_t to rapidjson namespace
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
using std::size_t;
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ASSERT
|
||||
|
||||
//! Assertion.
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
By default, rapidjson uses C \c assert() for internal assertions.
|
||||
User can override it by defining RAPIDJSON_ASSERT(x) macro.
|
||||
|
||||
\note Parsing errors are handled and can be customized by the
|
||||
\ref RAPIDJSON_ERRORS APIs.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ASSERT
|
||||
#include <cassert>
|
||||
#define RAPIDJSON_ASSERT(x) assert(x)
|
||||
#endif // RAPIDJSON_ASSERT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_STATIC_ASSERT
|
||||
|
||||
// Adopt from boost
|
||||
#ifndef RAPIDJSON_STATIC_ASSERT
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
template <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||
template<int x> struct StaticAssertTest {};
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
|
||||
#else
|
||||
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif
|
||||
//!@endcond
|
||||
|
||||
/*! \def RAPIDJSON_STATIC_ASSERT
|
||||
\brief (Internal) macro to check for conditions at compile-time
|
||||
\param x compile-time condition
|
||||
\hideinitializer
|
||||
*/
|
||||
#define RAPIDJSON_STATIC_ASSERT(x) \
|
||||
typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
|
||||
sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
|
||||
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
|
||||
#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
||||
#define RAPIDJSON_MULTILINEMACRO_END \
|
||||
} while((void)0, 0)
|
||||
|
||||
// adopted from Boost
|
||||
#define RAPIDJSON_VERSION_CODE(x,y,z) \
|
||||
(((x)*100000) + ((y)*100) + (z))
|
||||
|
||||
// token stringification
|
||||
#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
|
||||
#define RAPIDJSON_DO_STRINGIFY(x) #x
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define RAPIDJSON_GNUC \
|
||||
RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
|
||||
|
||||
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
|
||||
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
|
||||
#define RAPIDJSON_DIAG_OFF(x) \
|
||||
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
|
||||
|
||||
// push/pop support in Clang and GCC>=4.6
|
||||
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
|
||||
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
|
||||
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
|
||||
#else // GCC >= 4.2, < 4.6
|
||||
#define RAPIDJSON_DIAG_PUSH /* ignored */
|
||||
#define RAPIDJSON_DIAG_POP /* ignored */
|
||||
#endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
// pragma (MSVC specific)
|
||||
#define RAPIDJSON_PRAGMA(x) __pragma(x)
|
||||
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
|
||||
|
||||
#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
|
||||
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
|
||||
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
|
||||
|
||||
#else
|
||||
|
||||
#define RAPIDJSON_DIAG_OFF(x) /* ignored */
|
||||
#define RAPIDJSON_DIAG_PUSH /* ignored */
|
||||
#define RAPIDJSON_DIAG_POP /* ignored */
|
||||
|
||||
#endif // RAPIDJSON_DIAG_*
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// C++11 features
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#if defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) && \
|
||||
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1600)
|
||||
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#if defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
|
||||
#endif
|
||||
#endif
|
||||
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#define RAPIDJSON_NOEXCEPT noexcept
|
||||
#else
|
||||
#define RAPIDJSON_NOEXCEPT /* noexcept */
|
||||
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
|
||||
// no automatic detection, yet
|
||||
#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
|
||||
#endif
|
||||
|
||||
//!@endcond
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// new/delete
|
||||
|
||||
#ifndef RAPIDJSON_NEW
|
||||
///! customization point for global \c new
|
||||
#define RAPIDJSON_NEW(x) new x
|
||||
#endif
|
||||
#ifndef RAPIDJSON_DELETE
|
||||
///! customization point for global \c delete
|
||||
#define RAPIDJSON_DELETE(x) delete x
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocators and Encodings
|
||||
|
||||
#include "allocators.h"
|
||||
#include "encodings.h"
|
||||
|
||||
/*! \namespace rapidjson
|
||||
\brief main RapidJSON namespace
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stream
|
||||
|
||||
/*! \class rapidjson::Stream
|
||||
\brief Concept for reading and writing characters.
|
||||
|
||||
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
|
||||
|
||||
For write-only stream, only need to implement Put() and Flush().
|
||||
|
||||
\code
|
||||
concept Stream {
|
||||
typename Ch; //!< Character type of the stream.
|
||||
|
||||
//! Read the current character from stream without moving the read cursor.
|
||||
Ch Peek() const;
|
||||
|
||||
//! Read the current character from stream and moving the read cursor to next character.
|
||||
Ch Take();
|
||||
|
||||
//! Get the current read cursor.
|
||||
//! \return Number of characters read from start.
|
||||
size_t Tell();
|
||||
|
||||
//! Begin writing operation at the current read pointer.
|
||||
//! \return The begin writer pointer.
|
||||
Ch* PutBegin();
|
||||
|
||||
//! Write a character.
|
||||
void Put(Ch c);
|
||||
|
||||
//! Flush the buffer.
|
||||
void Flush();
|
||||
|
||||
//! End the writing operation.
|
||||
//! \param begin The begin write pointer returned by PutBegin().
|
||||
//! \return Number of characters written.
|
||||
size_t PutEnd(Ch* begin);
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
//! Provides additional information for stream.
|
||||
/*!
|
||||
By using traits pattern, this type provides a default configuration for stream.
|
||||
For custom stream, this type can be specialized for other configuration.
|
||||
See TEST(Reader, CustomStringStream) in readertest.cpp for example.
|
||||
*/
|
||||
template<typename Stream>
|
||||
struct StreamTraits {
|
||||
//! Whether to make local copy of stream for optimization during parsing.
|
||||
/*!
|
||||
By default, for safety, streams do not use local copy optimization.
|
||||
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
|
||||
*/
|
||||
enum { copyOptimization = 0 };
|
||||
};
|
||||
|
||||
//! Put N copies of a character to a stream.
|
||||
template<typename Stream, typename Ch>
|
||||
inline void PutN(Stream& stream, Ch c, size_t n) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
stream.Put(c);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// StringStream
|
||||
|
||||
//! Read-only string stream.
|
||||
/*! \note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringStream(const Ch *src) : src_(src), head_(src) {}
|
||||
|
||||
Ch Peek() const { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* head_; //!< Original head of the string.
|
||||
};
|
||||
|
||||
template <typename Encoding>
|
||||
struct StreamTraits<GenericStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! String stream with UTF8 encoding.
|
||||
typedef GenericStringStream<UTF8<> > StringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// InsituStringStream
|
||||
|
||||
//! A read-write string stream.
|
||||
/*! This string stream is particularly designed for in-situ parsing.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
|
||||
|
||||
// Read
|
||||
Ch Peek() { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() { return static_cast<size_t>(src_ - head_); }
|
||||
|
||||
// Write
|
||||
void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
|
||||
|
||||
Ch* PutBegin() { return dst_ = src_; }
|
||||
size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
|
||||
void Flush() {}
|
||||
|
||||
Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
|
||||
void Pop(size_t count) { dst_ -= count; }
|
||||
|
||||
Ch* src_;
|
||||
Ch* dst_;
|
||||
Ch* head_;
|
||||
};
|
||||
|
||||
template <typename Encoding>
|
||||
struct StreamTraits<GenericInsituStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! Insitu string stream with UTF8 encoding.
|
||||
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Type
|
||||
|
||||
//! Type of JSON value
|
||||
enum Type {
|
||||
kNullType = 0, //!< null
|
||||
kFalseType = 1, //!< false
|
||||
kTrueType = 2, //!< true
|
||||
kObjectType = 3, //!< object
|
||||
kArrayType = 4, //!< array
|
||||
kStringType = 5, //!< string
|
||||
kNumberType = 6 //!< number
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,93 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRINGBUFFER_H_
|
||||
#define RAPIDJSON_STRINGBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#include <utility> // std::move
|
||||
#endif
|
||||
|
||||
#include "internal/stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output stream.
|
||||
/*!
|
||||
\tparam Encoding Encoding of the stream.
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||
class GenericStringBuffer {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
|
||||
GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
|
||||
if (&rhs != this)
|
||||
stack_ = std::move(rhs.stack_);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.ShrinkToFit();
|
||||
stack_.template Pop<Ch>(1);
|
||||
}
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetString() const {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.template Pop<Ch>(1);
|
||||
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
GenericStringBuffer(const GenericStringBuffer&);
|
||||
GenericStringBuffer& operator=(const GenericStringBuffer&);
|
||||
};
|
||||
|
||||
//! String buffer with UTF8 encoding
|
||||
typedef GenericStringBuffer<UTF8<> > StringBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
||||
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STRINGBUFFER_H_
|
||||
@@ -0,0 +1,395 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_WRITER_H_
|
||||
#define RAPIDJSON_WRITER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/stack.h"
|
||||
#include "internal/strfunc.h"
|
||||
#include "internal/dtoa.h"
|
||||
#include "internal/itoa.h"
|
||||
#include "stringbuffer.h"
|
||||
#include <new> // placement new
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! JSON writer
|
||||
/*! Writer implements the concept Handler.
|
||||
It generates JSON text by events to an output os.
|
||||
|
||||
User may programmatically calls the functions of a writer to generate JSON text.
|
||||
|
||||
On the other side, a writer can also be passed to objects that generates events,
|
||||
|
||||
for example Reader::Parse() and Document::Accept().
|
||||
|
||||
\tparam OutputStream Type of output stream.
|
||||
\tparam SourceEncoding Encoding of source string.
|
||||
\tparam TargetEncoding Encoding of output stream.
|
||||
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||
\note implements Handler concept
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
|
||||
class Writer {
|
||||
public:
|
||||
typedef typename SourceEncoding::Ch Ch;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param stackAllocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
explicit
|
||||
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
|
||||
explicit
|
||||
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
|
||||
|
||||
//! Reset the writer with a new stream.
|
||||
/*!
|
||||
This function reset the writer with a new stream and default settings,
|
||||
in order to make a Writer object reusable for output multiple JSONs.
|
||||
|
||||
\param os New output stream.
|
||||
\code
|
||||
Writer<OutputStream> writer(os1);
|
||||
writer.StartObject();
|
||||
// ...
|
||||
writer.EndObject();
|
||||
|
||||
writer.Reset(os2);
|
||||
writer.StartObject();
|
||||
// ...
|
||||
writer.EndObject();
|
||||
\endcode
|
||||
*/
|
||||
void Reset(OutputStream& os) {
|
||||
os_ = &os;
|
||||
hasRoot_ = false;
|
||||
level_stack_.Clear();
|
||||
}
|
||||
|
||||
//! Checks whether the output is a complete JSON.
|
||||
/*!
|
||||
A complete JSON has a complete root object or array.
|
||||
*/
|
||||
bool IsComplete() const {
|
||||
return hasRoot_ && level_stack_.Empty();
|
||||
}
|
||||
|
||||
/*!@name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
//@{
|
||||
|
||||
bool Null() { Prefix(kNullType); return WriteNull(); }
|
||||
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
|
||||
bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
|
||||
bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
|
||||
bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
|
||||
bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
|
||||
|
||||
//! Writes the given \c double value to the stream
|
||||
/*!
|
||||
\param d The value to be written.
|
||||
\return Whether it is succeed.
|
||||
*/
|
||||
bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
Prefix(kStringType);
|
||||
return WriteString(str, length);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool String(const std::basic_string<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
Prefix(kObjectType);
|
||||
new (level_stack_.template Push<Level>()) Level(false);
|
||||
return WriteStartObject();
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
bool ret = WriteEndObject();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
Prefix(kArrayType);
|
||||
new (level_stack_.template Push<Level>()) Level(true);
|
||||
return WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType elementCount = 0) {
|
||||
(void)elementCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
bool ret = WriteEndArray();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_->Flush();
|
||||
return ret;
|
||||
}
|
||||
//@}
|
||||
|
||||
/*! @name Convenience extensions */
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
|
||||
protected:
|
||||
//! Information for each nested level
|
||||
struct Level {
|
||||
Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
|
||||
size_t valueCount; //!< number of values in this level
|
||||
bool inArray; //!< true if in array, otherwise in object
|
||||
};
|
||||
|
||||
static const size_t kDefaultLevelDepth = 32;
|
||||
|
||||
bool WriteNull() {
|
||||
os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true;
|
||||
}
|
||||
|
||||
bool WriteBool(bool b) {
|
||||
if (b) {
|
||||
os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e');
|
||||
}
|
||||
else {
|
||||
os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt(int i) {
|
||||
char buffer[11];
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint(unsigned u) {
|
||||
char buffer[10];
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt64(int64_t i64) {
|
||||
char buffer[21];
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint64(uint64_t u64) {
|
||||
char buffer[20];
|
||||
char* end = internal::u64toa(u64, buffer);
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteDouble(double d) {
|
||||
char buffer[25];
|
||||
char* end = internal::dtoa(d, buffer);
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
os_->Put(*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteString(const Ch* str, SizeType length) {
|
||||
static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static const char escape[256] = {
|
||||
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
|
||||
0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
|
||||
Z16, Z16, // 30~4F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
|
||||
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
|
||||
#undef Z16
|
||||
};
|
||||
|
||||
os_->Put('\"');
|
||||
GenericStringStream<SourceEncoding> is(str);
|
||||
while (is.Tell() < length) {
|
||||
const Ch c = is.Peek();
|
||||
if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) {
|
||||
// Unicode escaping
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
os_->Put('\\');
|
||||
os_->Put('u');
|
||||
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
|
||||
os_->Put(hexDigits[(codepoint >> 12) & 15]);
|
||||
os_->Put(hexDigits[(codepoint >> 8) & 15]);
|
||||
os_->Put(hexDigits[(codepoint >> 4) & 15]);
|
||||
os_->Put(hexDigits[(codepoint ) & 15]);
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
|
||||
// Surrogate pair
|
||||
unsigned s = codepoint - 0x010000;
|
||||
unsigned lead = (s >> 10) + 0xD800;
|
||||
unsigned trail = (s & 0x3FF) + 0xDC00;
|
||||
os_->Put(hexDigits[(lead >> 12) & 15]);
|
||||
os_->Put(hexDigits[(lead >> 8) & 15]);
|
||||
os_->Put(hexDigits[(lead >> 4) & 15]);
|
||||
os_->Put(hexDigits[(lead ) & 15]);
|
||||
os_->Put('\\');
|
||||
os_->Put('u');
|
||||
os_->Put(hexDigits[(trail >> 12) & 15]);
|
||||
os_->Put(hexDigits[(trail >> 8) & 15]);
|
||||
os_->Put(hexDigits[(trail >> 4) & 15]);
|
||||
os_->Put(hexDigits[(trail ) & 15]);
|
||||
}
|
||||
}
|
||||
else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) {
|
||||
is.Take();
|
||||
os_->Put('\\');
|
||||
os_->Put(escape[(unsigned char)c]);
|
||||
if (escape[(unsigned char)c] == 'u') {
|
||||
os_->Put('0');
|
||||
os_->Put('0');
|
||||
os_->Put(hexDigits[(unsigned char)c >> 4]);
|
||||
os_->Put(hexDigits[(unsigned char)c & 0xF]);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_))
|
||||
return false;
|
||||
}
|
||||
os_->Put('\"');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteStartObject() { os_->Put('{'); return true; }
|
||||
bool WriteEndObject() { os_->Put('}'); return true; }
|
||||
bool WriteStartArray() { os_->Put('['); return true; }
|
||||
bool WriteEndArray() { os_->Put(']'); return true; }
|
||||
|
||||
void Prefix(Type type) {
|
||||
(void)type;
|
||||
if (level_stack_.GetSize() != 0) { // this value is not at root
|
||||
Level* level = level_stack_.template Top<Level>();
|
||||
if (level->valueCount > 0) {
|
||||
if (level->inArray)
|
||||
os_->Put(','); // add comma if it is not the first element in array
|
||||
else // in object
|
||||
os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
|
||||
hasRoot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream* os_;
|
||||
internal::Stack<StackAllocator> level_stack_;
|
||||
bool hasRoot_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Writer(const Writer&);
|
||||
Writer& operator=(const Writer&);
|
||||
};
|
||||
|
||||
// Full specialization for StringStream to prevent memory copying
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt(int i) {
|
||||
char *buffer = os_->Push(11);
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
os_->Pop(11 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
|
||||
char *buffer = os_->Push(10);
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
os_->Pop(10 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
|
||||
char *buffer = os_->Push(21);
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
os_->Pop(21 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
|
||||
char *buffer = os_->Push(20);
|
||||
const char* end = internal::u64toa(u, buffer);
|
||||
os_->Pop(20 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
||||
char *buffer = os_->Push(25);
|
||||
char* end = internal::dtoa(d, buffer);
|
||||
os_->Pop(25 - (end - buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef _MSC_VER
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
-598
@@ -1,598 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
#include "modelFile/modelFile.h"
|
||||
#include "optimizedModel.h"
|
||||
#include "polygonOptimizer.h"
|
||||
#include "slicer.h"
|
||||
#include "layerPart.h"
|
||||
#include "inset.h"
|
||||
#include "skin.h"
|
||||
#include "infill.h"
|
||||
#include "bridge.h"
|
||||
#include "support.h"
|
||||
#include "pathOptimizer.h"
|
||||
#include "skirt.h"
|
||||
#include "raft.h"
|
||||
#include "comb.h"
|
||||
#include "gcodeExport.h"
|
||||
|
||||
#define VERSION "1.0"
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
int layerThickness;
|
||||
int initialLayerThickness;
|
||||
int filamentDiameter;
|
||||
int filamentFlow;
|
||||
int extrusionWidth;
|
||||
int insetCount;
|
||||
int downSkinCount;
|
||||
int upSkinCount;
|
||||
int sparseInfillLineDistance;
|
||||
int infillOverlap;
|
||||
int skirtDistance;
|
||||
int skirtLineCount;
|
||||
int retractionAmount;
|
||||
int retractionSpeed;
|
||||
|
||||
int initialSpeedupLayers;
|
||||
int initialLayerSpeed;
|
||||
int printSpeed;
|
||||
int infillSpeed;
|
||||
int moveSpeed;
|
||||
int fanOnLayerNr;
|
||||
|
||||
//Support material
|
||||
int supportAngle;
|
||||
int supportEverywhere;
|
||||
int supportLineWidth;
|
||||
|
||||
//Cool settings
|
||||
int minimalLayerTime;
|
||||
int minimalFeedrate;
|
||||
int coolHeadLift;
|
||||
int fanSpeedMin;
|
||||
int fanSpeedMax;
|
||||
|
||||
//Raft settings
|
||||
int raftMargin;
|
||||
int raftLineSpacing;
|
||||
int raftBaseThickness;
|
||||
int raftBaseLinewidth;
|
||||
int raftInterfaceThickness;
|
||||
int raftInterfaceLinewidth;
|
||||
|
||||
FMatrix3x3 matrix;
|
||||
Point objectPosition;
|
||||
int objectSink;
|
||||
|
||||
int fixHorrible;
|
||||
|
||||
Point extruderOffset[16];
|
||||
const char* startCode;
|
||||
const char* endCode;
|
||||
};
|
||||
|
||||
int verbose_level;
|
||||
int maxObjectHeight;
|
||||
|
||||
void processFile(const char* input_filename, Config& config, GCodeExport& gcode, bool firstFile)
|
||||
{
|
||||
for(unsigned int n=1; n<16;n++)
|
||||
gcode.setExtruderOffset(n, config.extruderOffset[n]);
|
||||
|
||||
double t = getTime();
|
||||
log("Loading %s from disk...\n", input_filename);
|
||||
SimpleModel* m = loadModel(input_filename, config.matrix);
|
||||
if (!m)
|
||||
{
|
||||
log("Failed to load model: %s\n", input_filename);
|
||||
return;
|
||||
}
|
||||
log("Loaded from disk in %5.3fs\n", timeElapsed(t));
|
||||
log("Analyzing and optimizing model...\n");
|
||||
OptimizedModel* om = new OptimizedModel(m, Point3(config.objectPosition.X, config.objectPosition.Y, -config.objectSink));
|
||||
for(unsigned int v = 0; v < m->volumes.size(); v++)
|
||||
{
|
||||
log(" Face counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size(), (int)om->volumes[v].faces.size(), float(om->volumes[v].faces.size()) / float(m->volumes[v].faces.size()) * 100);
|
||||
log(" Vertex counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size() * 3, (int)om->volumes[v].points.size(), float(om->volumes[v].points.size()) / float(m->volumes[v].faces.size() * 3) * 100);
|
||||
}
|
||||
delete m;
|
||||
log("Optimize model %5.3fs \n", timeElapsed(t));
|
||||
//om->saveDebugSTL("output.stl");
|
||||
|
||||
log("Slicing model...\n");
|
||||
vector<Slicer*> slicerList;
|
||||
for(unsigned int volumeIdx=0; volumeIdx < om->volumes.size(); volumeIdx++)
|
||||
{
|
||||
slicerList.push_back(new Slicer(&om->volumes[volumeIdx], config.initialLayerThickness / 2, config.layerThickness, config.fixHorrible));
|
||||
//slicerList[volumeIdx]->dumpSegments("C:\\models\\output.html");
|
||||
}
|
||||
log("Sliced model in %5.3fs\n", timeElapsed(t));
|
||||
|
||||
SliceDataStorage storage;
|
||||
if (config.supportAngle > -1)
|
||||
{
|
||||
fprintf(stdout,"Generating support map...\n");
|
||||
generateSupportGrid(storage.support, om, config.initialLayerThickness / 2, config.layerThickness);
|
||||
}
|
||||
storage.modelSize = om->modelSize;
|
||||
storage.modelMin = om->vMin;
|
||||
storage.modelMax = om->vMax;
|
||||
delete om;
|
||||
|
||||
log("Generating layer parts...\n");
|
||||
for(unsigned int volumeIdx=0; volumeIdx < slicerList.size(); volumeIdx++)
|
||||
{
|
||||
storage.volumes.push_back(SliceVolumeStorage());
|
||||
createLayerParts(storage.volumes[volumeIdx], slicerList[volumeIdx], config.fixHorrible);
|
||||
delete slicerList[volumeIdx];
|
||||
|
||||
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
|
||||
for(unsigned int volumeIdx2=0; volumeIdx2<volumeIdx; volumeIdx2++)
|
||||
{
|
||||
for(unsigned int layerNr=0; layerNr < storage.volumes[volumeIdx].layers.size(); layerNr++)
|
||||
{
|
||||
SliceLayer* layer1 = &storage.volumes[volumeIdx].layers[layerNr];
|
||||
SliceLayer* layer2 = &storage.volumes[volumeIdx2].layers[layerNr];
|
||||
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPolygons(layer1->parts[p1].outline, ClipperLib::ptSubject);
|
||||
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
|
||||
{
|
||||
clipper.AddPolygons(layer2->parts[p2].outline, ClipperLib::ptClip);
|
||||
}
|
||||
clipper.Execute(ClipperLib::ctDifference, layer1->parts[p1].outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log("Generated layer parts in %5.3fs\n", timeElapsed(t));
|
||||
//dumpLayerparts(storage, "c:/models/output.html");
|
||||
|
||||
const unsigned int totalLayers = storage.volumes[0].layers.size();
|
||||
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
|
||||
{
|
||||
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
|
||||
{
|
||||
generateInsets(&storage.volumes[volumeIdx].layers[layerNr], config.extrusionWidth, config.insetCount);
|
||||
}
|
||||
logProgress("inset",layerNr+1,totalLayers);
|
||||
}
|
||||
log("Generated inset in %5.3fs\n", timeElapsed(t));
|
||||
//dumpLayerparts(storage, "c:/models/output.html");
|
||||
|
||||
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
|
||||
{
|
||||
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
|
||||
{
|
||||
generateSkins(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount, config.infillOverlap);
|
||||
generateSparse(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount);
|
||||
}
|
||||
logProgress("skin",layerNr+1,totalLayers);
|
||||
}
|
||||
log("Generated up/down skin in %5.3fs\n", timeElapsed(t));
|
||||
generateSkirt(storage, config.skirtDistance, config.extrusionWidth, config.skirtLineCount);
|
||||
generateRaft(storage, config.raftMargin);
|
||||
|
||||
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
|
||||
{
|
||||
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
|
||||
{
|
||||
for(unsigned int partNr=0; partNr<storage.volumes[volumeIdx].layers[layerNr].parts.size(); partNr++)
|
||||
{
|
||||
if (layerNr > 0)
|
||||
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = bridgeAngle(&storage.volumes[volumeIdx].layers[layerNr].parts[partNr], &storage.volumes[volumeIdx].layers[layerNr-1]);
|
||||
else
|
||||
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcode.setRetractionSettings(config.retractionAmount, config.retractionSpeed);
|
||||
if (firstFile)
|
||||
{
|
||||
gcode.addCode(config.startCode);
|
||||
}else{
|
||||
gcode.resetExtrusionValue();
|
||||
gcode.addRetraction();
|
||||
gcode.setZ(maxObjectHeight + 5000);
|
||||
gcode.addMove(config.objectPosition, config.moveSpeed, 0);
|
||||
}
|
||||
gcode.addComment("total_layers=%d",totalLayers);
|
||||
|
||||
GCodePathConfig skirtConfig(config.printSpeed, config.extrusionWidth, "SKIRT");
|
||||
GCodePathConfig inset0Config(config.printSpeed, config.extrusionWidth, "WALL-OUTER");
|
||||
GCodePathConfig inset1Config(config.printSpeed, config.extrusionWidth, "WALL-INNER");
|
||||
GCodePathConfig fillConfig(config.infillSpeed, config.extrusionWidth, "FILL");
|
||||
GCodePathConfig supportConfig(config.printSpeed, config.supportLineWidth, "SUPPORT");
|
||||
|
||||
if (config.raftBaseThickness > 0 && config.raftInterfaceThickness > 0)
|
||||
{
|
||||
GCodePathConfig raftBaseConfig(config.initialLayerSpeed, config.raftBaseLinewidth, "SUPPORT");
|
||||
GCodePathConfig raftInterfaceConfig(config.initialLayerSpeed, config.raftInterfaceLinewidth, "SUPPORT");
|
||||
{
|
||||
gcode.addComment("LAYER:-2");
|
||||
gcode.addComment("RAFT");
|
||||
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
|
||||
gcode.setZ(config.raftBaseThickness);
|
||||
gcode.setExtrusion(config.raftBaseThickness, config.filamentDiameter, config.filamentFlow);
|
||||
gcodeLayer.addPolygonsByOptimizer(storage.raftOutline, &raftBaseConfig);
|
||||
|
||||
Polygons raftLines;
|
||||
generateLineInfill(storage.raftOutline, raftLines, config.raftBaseLinewidth, config.raftLineSpacing, config.infillOverlap, 0);
|
||||
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftBaseConfig);
|
||||
|
||||
gcodeLayer.writeGCode(false);
|
||||
}
|
||||
|
||||
{
|
||||
gcode.addComment("LAYER:-1");
|
||||
gcode.addComment("RAFT");
|
||||
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
|
||||
gcode.setZ(config.raftBaseThickness + config.raftInterfaceThickness);
|
||||
gcode.setExtrusion(config.raftInterfaceThickness, config.filamentDiameter, config.filamentFlow);
|
||||
|
||||
Polygons raftLines;
|
||||
generateLineInfill(storage.raftOutline, raftLines, config.raftInterfaceLinewidth, config.raftLineSpacing, config.infillOverlap, 90);
|
||||
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftInterfaceConfig);
|
||||
|
||||
gcodeLayer.writeGCode(false);
|
||||
}
|
||||
}
|
||||
|
||||
int volumeIdx = 0;
|
||||
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
|
||||
{
|
||||
logProgress("export", layerNr+1, totalLayers);
|
||||
|
||||
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
|
||||
gcode.addComment("LAYER:%d", layerNr);
|
||||
int32_t z = config.initialLayerThickness + layerNr * config.layerThickness;
|
||||
z += config.raftBaseThickness + config.raftInterfaceThickness;
|
||||
gcode.setZ(z);
|
||||
if (layerNr == 0)
|
||||
gcodeLayer.addPolygonsByOptimizer(storage.skirt, &skirtConfig);
|
||||
|
||||
for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++)
|
||||
{
|
||||
if (volumeCnt > 0)
|
||||
volumeIdx = (volumeIdx + 1) % storage.volumes.size();
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
|
||||
gcodeLayer.setExtruder(volumeIdx);
|
||||
|
||||
PathOptimizer partOrderOptimizer(gcode.getPositionXY());
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
partOrderOptimizer.addPolygon(layer->parts[partNr].insets[0][0]);
|
||||
}
|
||||
partOrderOptimizer.optimize();
|
||||
|
||||
for(unsigned int partCounter=0; partCounter<partOrderOptimizer.polyOrder.size(); partCounter++)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[partOrderOptimizer.polyOrder[partCounter]];
|
||||
|
||||
gcodeLayer.setCombBoundary(&part->insets[0]);
|
||||
for(int insetNr=part->insets.size()-1; insetNr>-1; insetNr--)
|
||||
{
|
||||
if (insetNr == 0)
|
||||
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset0Config);
|
||||
else
|
||||
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset1Config);
|
||||
}
|
||||
|
||||
Polygons fillPolygons;
|
||||
int fillAngle = 45;
|
||||
if (layerNr & 1) fillAngle += 90;
|
||||
//int sparseSteps[1] = {config.extrusionWidth};
|
||||
//generateConcentricInfill(part->skinOutline, fillPolygons, sparseSteps, 1);
|
||||
generateLineInfill(part->skinOutline, fillPolygons, config.extrusionWidth, config.extrusionWidth, config.infillOverlap, (part->bridgeAngle > -1) ? part->bridgeAngle : fillAngle);
|
||||
//int sparseSteps[2] = {config.extrusionWidth*5, config.extrusionWidth * 0.8};
|
||||
//generateConcentricInfill(part->sparseOutline, fillPolygons, sparseSteps, 2);
|
||||
if (config.sparseInfillLineDistance > 0)
|
||||
{
|
||||
if (config.sparseInfillLineDistance > config.extrusionWidth * 4)
|
||||
{
|
||||
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45);
|
||||
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45 + 90);
|
||||
}
|
||||
else
|
||||
{
|
||||
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance, config.infillOverlap, fillAngle);
|
||||
}
|
||||
}
|
||||
|
||||
gcodeLayer.addPolygonsByOptimizer(fillPolygons, &fillConfig);
|
||||
}
|
||||
gcodeLayer.setCombBoundary(NULL);
|
||||
}
|
||||
if (config.supportAngle > -1)
|
||||
{
|
||||
SupportPolyGenerator supportGenerator(storage.support, z, config.supportAngle, config.supportEverywhere > 0, true);
|
||||
gcodeLayer.addPolygonsByOptimizer(supportGenerator.polygons, &supportConfig);
|
||||
if (layerNr == 0)
|
||||
{
|
||||
SupportPolyGenerator supportGenerator2(storage.support, z, config.supportAngle, config.supportEverywhere > 0, false);
|
||||
gcodeLayer.addPolygonsByOptimizer(supportGenerator2.polygons, &supportConfig);
|
||||
}
|
||||
}
|
||||
|
||||
//Finish the layer by applying speed corrections for minimal layer times and slowdown for the initial layer.
|
||||
if (int(layerNr) < config.initialSpeedupLayers)
|
||||
{
|
||||
int n = config.initialSpeedupLayers;
|
||||
int layer0Factor = config.initialLayerSpeed * 100 / config.printSpeed;
|
||||
gcodeLayer.setSpeedFactor((layer0Factor * (n - layerNr) + 100 * (layerNr)) / n);
|
||||
}
|
||||
gcodeLayer.forceMinimalLayerTime(config.minimalLayerTime, config.minimalFeedrate);
|
||||
if (layerNr == 0)
|
||||
gcode.setExtrusion(config.initialLayerThickness, config.filamentDiameter, config.filamentFlow);
|
||||
else
|
||||
gcode.setExtrusion(config.layerThickness, config.filamentDiameter, config.filamentFlow);
|
||||
if (int(layerNr) >= config.fanOnLayerNr)
|
||||
{
|
||||
int speed = config.fanSpeedMin;
|
||||
if (gcodeLayer.getSpeedFactor() <= 50)
|
||||
{
|
||||
speed = config.fanSpeedMax;
|
||||
}else{
|
||||
int n = gcodeLayer.getSpeedFactor() - 50;
|
||||
speed = config.fanSpeedMin * n / 50 + config.fanSpeedMax * (50 - n) / 50;
|
||||
}
|
||||
gcode.addFanCommand(speed);
|
||||
}else{
|
||||
gcode.addFanCommand(0);
|
||||
}
|
||||
gcodeLayer.writeGCode(config.coolHeadLift > 0);
|
||||
}
|
||||
|
||||
/* support debug
|
||||
for(int32_t y=0; y<storage.support.gridHeight; y++)
|
||||
{
|
||||
for(int32_t x=0; x<storage.support.gridWidth; x++)
|
||||
{
|
||||
unsigned int n = x+y*storage.support.gridWidth;
|
||||
if (storage.support.grid[n].size() < 1) continue;
|
||||
int32_t z = storage.support.grid[n][0].z;
|
||||
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
|
||||
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, z), z);
|
||||
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
|
||||
}
|
||||
}
|
||||
//*/
|
||||
|
||||
log("Wrote layers in %5.2fs.\n", timeElapsed(t));
|
||||
gcode.tellFileSize();
|
||||
gcode.addFanCommand(0);
|
||||
|
||||
logProgress("process", 1, 1);
|
||||
log("Total time elapsed %5.2fs.\n", timeElapsed(t,true));
|
||||
|
||||
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
|
||||
maxObjectHeight = std::max(maxObjectHeight, storage.modelSize.z);
|
||||
}
|
||||
|
||||
void setConfig(Config& config, char* str)
|
||||
{
|
||||
char* valuePtr = strchr(str, '=');
|
||||
if (!valuePtr) return;
|
||||
*valuePtr++ = '\0';
|
||||
#define STRINGIFY(_s) #_s
|
||||
#define SETTING(longName, shortName) if (strcasecmp(str, STRINGIFY(longName)) == 0 || strcasecmp(str, STRINGIFY(shortName)) == 0) { config.longName = atoi(valuePtr); }
|
||||
SETTING(layerThickness, lt);
|
||||
SETTING(initialLayerThickness, ilt);
|
||||
SETTING(filamentDiameter, fd);
|
||||
SETTING(filamentFlow, ff);
|
||||
SETTING(extrusionWidth, ew);
|
||||
SETTING(insetCount, ic);
|
||||
SETTING(downSkinCount, dsc);
|
||||
SETTING(upSkinCount, usc);
|
||||
SETTING(sparseInfillLineDistance, sild);
|
||||
SETTING(infillOverlap, iover);
|
||||
SETTING(skirtDistance, sd);
|
||||
SETTING(skirtLineCount, slc);
|
||||
|
||||
SETTING(initialSpeedupLayers, isl);
|
||||
SETTING(initialLayerSpeed, ils);
|
||||
SETTING(printSpeed, ps);
|
||||
SETTING(infillSpeed, is);
|
||||
SETTING(moveSpeed, ms);
|
||||
SETTING(fanOnLayerNr, fl);
|
||||
|
||||
SETTING(supportAngle, supa);
|
||||
SETTING(supportEverywhere, supe);
|
||||
SETTING(supportLineWidth, sulw);
|
||||
|
||||
SETTING(retractionAmount, reta);
|
||||
SETTING(retractionSpeed, rets);
|
||||
SETTING(objectPosition.X, posx);
|
||||
SETTING(objectPosition.Y, posy);
|
||||
SETTING(objectSink, objsink);
|
||||
|
||||
SETTING(raftMargin, raftMar);
|
||||
SETTING(raftLineSpacing, raftLS);
|
||||
SETTING(raftBaseThickness, raftBaseT);
|
||||
SETTING(raftBaseLinewidth, raftBaseL);
|
||||
SETTING(raftInterfaceThickness, raftInterfaceT);
|
||||
SETTING(raftInterfaceLinewidth, raftInterfaceL);
|
||||
|
||||
SETTING(minimalLayerTime, minLayTime);
|
||||
SETTING(minimalFeedrate, minFeed);
|
||||
SETTING(coolHeadLift, coolLift);
|
||||
SETTING(fanSpeedMin, fanMin);
|
||||
SETTING(fanSpeedMax, fanMax);
|
||||
|
||||
SETTING(extruderOffset[1].X, eOff1X);
|
||||
SETTING(extruderOffset[1].Y, eOff1Y);
|
||||
SETTING(extruderOffset[2].X, eOff2X);
|
||||
SETTING(extruderOffset[2].Y, eOff2Y);
|
||||
SETTING(extruderOffset[3].X, eOff3X);
|
||||
SETTING(extruderOffset[3].Y, eOff3Y);
|
||||
#undef SETTING
|
||||
if (strcasecmp(str, "startCode") == 0)
|
||||
config.startCode = valuePtr;
|
||||
if (strcasecmp(str, "endCode") == 0)
|
||||
config.endCode = valuePtr;
|
||||
}
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
printf("TODO\n");
|
||||
}
|
||||
|
||||
void signal_FPE(int n)
|
||||
{
|
||||
printf("Floating point exception\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
//Lower the process priority on linux and mac.
|
||||
setpriority(PRIO_PROCESS, 0, 10);
|
||||
#endif
|
||||
signal(SIGFPE, signal_FPE);
|
||||
|
||||
GCodeExport gcode;
|
||||
Config config;
|
||||
int fileNr = 0;
|
||||
|
||||
config.filamentDiameter = 2890;
|
||||
config.initialLayerThickness = 300;
|
||||
config.layerThickness = 100;
|
||||
config.extrusionWidth = 400;
|
||||
config.insetCount = 2;
|
||||
config.downSkinCount = 6;
|
||||
config.upSkinCount = 6;
|
||||
config.initialSpeedupLayers = 4;
|
||||
config.initialLayerSpeed = 20;
|
||||
config.printSpeed = 50;
|
||||
config.infillSpeed = 50;
|
||||
config.moveSpeed = 200;
|
||||
config.fanOnLayerNr = 2;
|
||||
config.skirtDistance = 6000;
|
||||
config.skirtLineCount = 1;
|
||||
config.sparseInfillLineDistance = 100 * config.extrusionWidth / 20;
|
||||
config.infillOverlap = 15;
|
||||
config.objectPosition = Point(102500, 102500);
|
||||
config.objectSink = 0;
|
||||
config.supportAngle = -1;
|
||||
config.supportEverywhere = 0;
|
||||
config.supportLineWidth = config.extrusionWidth;
|
||||
config.retractionAmount = 4.5;
|
||||
config.retractionSpeed = 45;
|
||||
|
||||
config.minimalLayerTime = 5;
|
||||
config.minimalFeedrate = 10;
|
||||
config.coolHeadLift = 1;
|
||||
config.fanSpeedMin = 100;
|
||||
config.fanSpeedMax = 100;
|
||||
|
||||
config.raftMargin = 5000;
|
||||
config.raftLineSpacing = 1000;
|
||||
config.raftBaseThickness = 0;
|
||||
config.raftBaseLinewidth = 0;
|
||||
config.raftInterfaceThickness = 0;
|
||||
config.raftInterfaceLinewidth = 0;
|
||||
|
||||
config.fixHorrible = 0;
|
||||
|
||||
config.startCode =
|
||||
"M109 S210 ;Heatup to 210C\n"
|
||||
"G21 ;metric values\n"
|
||||
"G90 ;absolute positioning\n"
|
||||
"G28 ;Home\n"
|
||||
"G1 Z15.0 F300 ;move the platform down 15mm\n"
|
||||
"G92 E0 ;zero the extruded length\n"
|
||||
"G1 F200 E5 ;extrude 5mm of feed stock\n"
|
||||
"G92 E0 ;zero the extruded length again\n";
|
||||
config.endCode =
|
||||
"M104 S0 ;extruder heater off\n"
|
||||
"M140 S0 ;heated bed heater off (if you have it)\n"
|
||||
"G91 ;relative positioning\n"
|
||||
"G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n"
|
||||
"G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\n"
|
||||
"G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n"
|
||||
"M84 ;steppers off\n"
|
||||
"G90 ;absolute positioning\n";
|
||||
|
||||
fprintf(stdout,"Cura_SteamEngine version %s\n", VERSION);
|
||||
|
||||
for(int argn = 1; argn < argc; argn++)
|
||||
{
|
||||
char* str = argv[argn];
|
||||
if (str[0] == '-')
|
||||
{
|
||||
for(str++; *str; str++)
|
||||
{
|
||||
switch(*str)
|
||||
{
|
||||
case 'h':
|
||||
print_usage();
|
||||
exit(1);
|
||||
case 'v':
|
||||
verbose_level++;
|
||||
break;
|
||||
case 'b':
|
||||
argn++;
|
||||
binaryMeshBlob = fopen(argv[argn], "rb");
|
||||
break;
|
||||
case 'o':
|
||||
argn++;
|
||||
gcode.setFilename(argv[argn]);
|
||||
if (!gcode.isValid())
|
||||
{
|
||||
logError("Failed to open %s for output.\n", argv[argn]);
|
||||
exit(1);
|
||||
}
|
||||
gcode.addComment("Generated with Cura_SteamEngine %s", VERSION);
|
||||
break;
|
||||
case 's':
|
||||
argn++;
|
||||
setConfig(config, argv[argn]);
|
||||
break;
|
||||
case 'm':
|
||||
argn++;
|
||||
sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
|
||||
&config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
|
||||
&config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
|
||||
&config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
|
||||
break;
|
||||
default:
|
||||
logError("Unknown option: %c\n", *str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if (!gcode.isValid())
|
||||
{
|
||||
logError("No output file specified\n");
|
||||
return 1;
|
||||
}
|
||||
processFile(argv[argn], config, gcode, fileNr == 0);
|
||||
fileNr ++;
|
||||
}
|
||||
}
|
||||
if (gcode.isValid())
|
||||
{
|
||||
gcode.addFanCommand(0);
|
||||
gcode.addCode(config.endCode);
|
||||
log("Print time: %d\n", int(gcode.getTotalPrintTime()));
|
||||
log("Filament: %d\n", int(gcode.getTotalFilamentUsed()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
This file is a conversion from the ENGINE settings of 15.04 to the ENGINE setting of 2.0
|
||||
|
||||
This is NOT a conversion on the frontend internal setting names (15.04 has the dictionary of doom)
|
||||
|
||||
|
||||
autoCenter ? ==> center_object OR machine_center_is_zero ??
|
||||
coolHeadLift ==> cool_lift_head
|
||||
downSkinCount ==> bottom_layers
|
||||
enableCombing ==> retraction_combing
|
||||
enableOozeShield ==> ooze_shield_enabled
|
||||
endCode ==> machine_end_gcode
|
||||
extruderOffset[MAX_EXTRUDERS] = (machine_nozzle_offset_x, machine_nozzle_offset_y)
|
||||
extrusionWidth ==> infill_line_width, skirt_line_width, support_line_width
|
||||
fanFullOnLayerNr ==> cool_fan_full_layer
|
||||
fanSpeedMax ==> cool_fan_speed_max
|
||||
fanSpeedMin ==> cool_fan_speed_min
|
||||
filamentDiameter ==> material_diameter
|
||||
filamentFlow ==> material_flow
|
||||
fixHorrible ==> meshfix_union_all AND/OR meshfix_union_all_remove_holes AND/OR meshfix_extensive_stitching AND/OR magic_mesh_surface_mode
|
||||
gcodeFlavor ==> machine_gcode_flavor
|
||||
infillOverlap ==> infill_overlap
|
||||
infillPattern ==> infill_pattern
|
||||
infillSpeed ==> speed_infill
|
||||
initialLayerSpeed ==> speed_layer_0
|
||||
initialLayerThickness ==> layer_height_0
|
||||
initialSpeedupLayers ==> speed_slowdown_layers
|
||||
inset0Speed ==> speed_wall_0
|
||||
insetCount ==> wall_line_count
|
||||
insetXSpeed ==> speed_wall_x
|
||||
layer0extrusionWidth [ Doesn't exist anymore ]
|
||||
layerThickness ==> layer_height
|
||||
matrix [ Doesn't exist anymore ]
|
||||
minimalExtrusionBeforeRetraction
|
||||
minimalFeedrate ==> cool_min_speed
|
||||
minimalLayerTime ==> cool_min_layer_time
|
||||
moveSpeed ==> speed_travel
|
||||
multiVolumeOverlap ==> multiple_mesh_overlap
|
||||
nozzleSize ==> machine_nozzle_size
|
||||
objectPosition ==> mesh_position_x, mesh_position_y, mesh_position_z
|
||||
objectSink [ Doesn't exist in CuraEngine anymore ]
|
||||
perimeterBeforeInfill = not(infill_before_walls)
|
||||
postSwitchExtruderCode ==> machine_extruder_start_code
|
||||
preSwitchExtruderCode ==> machine_extruder_end_code
|
||||
printSpeed ==> speed_prime_tower, speed_support_lines, speed_support_roof, skirt_speed
|
||||
raftAirGap ==> raft_airgap
|
||||
raftAirGapLayer0 ?!?!?
|
||||
raftBaseLinewidth ==> raft_base_line_width
|
||||
raftBaseSpeed ==> raft_interface_speed, raft_base_speed
|
||||
raftBaseThickness ==> raft_base_thickness
|
||||
raftFanSpeed ==> raft_base_fan_speed, raft_interface_fan_speed, raft_surface_fan_speed
|
||||
raftInterfaceLineSpacing==> raft_interface_line_spacing
|
||||
raftInterfaceLinewidth ==> raft_interface_line_width
|
||||
raftInterfaceThickness ==> raft_interface_thickness
|
||||
raftLineSpacing ==> raft_base_line_spacing
|
||||
raftMargin ==> raft_margin
|
||||
raftSurfaceLayers ==> raft_surface_layers
|
||||
raftSurfaceLineSpacing ==> raft_surface_line_spacing
|
||||
raftSurfaceLinewidth ==> raft_surface_line_width
|
||||
raftSurfaceSpeed ==> raft_surface_speed
|
||||
raftSurfaceThickness ==> raft_surface_thickness
|
||||
retractionAmount ==> retraction_amount (set retraction_enable = true)
|
||||
retractionAmountExtruderSwitch ==> switch_extruder_retraction_amount
|
||||
retractionAmountPrime ==> retraction_extra_prime_amount
|
||||
retractionMinimalDistance ==> retraction_extrusion_window ( set retraction_count_max = 1 )
|
||||
retractionSpeed ==> retraction_retract_speed (, retraction_prime_speed ?), switch_extruder_retraction_speed
|
||||
retractionZHop ==> retraction_hop
|
||||
simpleMode ??!
|
||||
skinSpeed ==> speed_topbottom
|
||||
skirtDistance ==> skirt_gap
|
||||
skirtLineCount ==> brim_line_count, skirt_line_count
|
||||
skirtMinLength ==> skirt_minimal_length
|
||||
sparseInfillLineDistance ==> infill_line_distance
|
||||
spiralizeMode ==> magic_spiralize
|
||||
startCode ==> machine_start_gcode
|
||||
supportAngle ==> support_angle, support_enable=true if support_angle>0
|
||||
supportEverywhere ==> support_type
|
||||
supportExtruder ==> support_extruder_nr, support_extruder_nr_layer_0
|
||||
supportLineDistance ==> support_line_distance
|
||||
supportType ==> support_pattern
|
||||
supportXYDistance ==> support_xy_distance
|
||||
supportZDistance ==> support_z_distance
|
||||
upSkinCount ==> top_layers
|
||||
wipeTowerSize ==> prime_tower_size
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NEW:
|
||||
adhesion_extruder_nr
|
||||
adhesion_type
|
||||
alternate_extra_perimeter
|
||||
coasting_enable
|
||||
coasting_min_volume_move
|
||||
coasting_min_volume_retract
|
||||
coasting_speed_move
|
||||
coasting_speed_retract
|
||||
coasting_volume_move
|
||||
coasting_volume_retract
|
||||
cool_min_layer_time_fan_speed_max
|
||||
draft_shield_dist
|
||||
draft_shield_height
|
||||
extruder_nr
|
||||
fill_perimeter_gaps
|
||||
infill_sparse_thickness
|
||||
infill_wipe_dist
|
||||
machine_depth
|
||||
machine_extruder_count
|
||||
machine_extruder_end_pos_abs
|
||||
machine_extruder_end_pos_x
|
||||
machine_extruder_end_pos_y
|
||||
machine_extruder_start_pos_abs
|
||||
machine_extruder_start_pos_x
|
||||
machine_extruder_start_pos_y
|
||||
machine_heated_bed
|
||||
machine_nozzle_cool_down_speed
|
||||
machine_nozzle_expansion_angle
|
||||
machine_nozzle_head_distance
|
||||
machine_nozzle_heat_up_speed
|
||||
machine_nozzle_tip_outer_diameter
|
||||
machine_print_temp_wait
|
||||
machine_use_extruder_offset_to_offset_coords
|
||||
machine_width
|
||||
magic_fuzzy_skin_enabled
|
||||
magic_fuzzy_skin_point_dist
|
||||
magic_fuzzy_skin_thickness
|
||||
material_bed_temperature
|
||||
material_bed_temp_prepend
|
||||
material_bed_temp_wait
|
||||
material_extrusion_cool_down_speed
|
||||
material_flow_dependent_temperature
|
||||
material_flow_temp_graph
|
||||
material_print_temperature
|
||||
material_print_temp_prepend
|
||||
material_print_temp_wait
|
||||
material_standby_temperature
|
||||
meshfix_keep_open_polygons
|
||||
ooze_shield_angle
|
||||
ooze_shield_dist
|
||||
prime_tower_dir_outward
|
||||
prime_tower_distance
|
||||
prime_tower_flow
|
||||
prime_tower_line_width
|
||||
prime_tower_position_x
|
||||
prime_tower_position_y
|
||||
prime_tower_wipe_enabled
|
||||
remove_overlapping_walls_0_enabled
|
||||
remove_overlapping_walls_x_enabled
|
||||
retraction_min_travel
|
||||
skin_alternate_rotation
|
||||
skin_line_width
|
||||
skin_no_small_gaps_heuristic
|
||||
skin_outline_count
|
||||
support_area_smoothing
|
||||
support_bottom_distance
|
||||
support_bottom_stair_step_height
|
||||
support_conical_angle
|
||||
support_conical_enabled
|
||||
support_conical_min_width
|
||||
support_connect_zigzags
|
||||
support_join_distance
|
||||
support_minimal_diameter
|
||||
support_offset
|
||||
support_roof_enable
|
||||
support_roof_extruder_nr
|
||||
support_roof_height
|
||||
support_roof_line_distance
|
||||
support_roof_line_width
|
||||
support_roof_pattern
|
||||
support_top_distance
|
||||
support_tower_diameter
|
||||
support_tower_roof_angle
|
||||
switch_extruder_prime_speed
|
||||
top_bottom_pattern
|
||||
travel_avoid_distance
|
||||
travel_avoid_other_parts
|
||||
travel_compensate_overlapping_walls_enabled
|
||||
wall_line_width_0
|
||||
wall_line_width_x
|
||||
wireframe_bottom_delay
|
||||
wireframe_drag_along
|
||||
wireframe_enabled
|
||||
wireframe_fall_down
|
||||
wireframe_flat_delay
|
||||
wireframe_flow_connection
|
||||
wireframe_flow_flat
|
||||
wireframe_height
|
||||
wireframe_nozzle_clearance
|
||||
wireframe_printspeed_bottom
|
||||
wireframe_printspeed_down
|
||||
wireframe_printspeed_flat
|
||||
wireframe_printspeed_up
|
||||
wireframe_roof_drag_along
|
||||
wireframe_roof_fall_down
|
||||
wireframe_roof_inset
|
||||
wireframe_roof_outer_delay
|
||||
wireframe_straight_before_down
|
||||
wireframe_strategy
|
||||
wireframe_top_delay
|
||||
wireframe_top_jump
|
||||
wireframe_up_half_speed
|
||||
xy_offset
|
||||
z_seam_type
|
||||
@@ -0,0 +1,130 @@
|
||||
[ problem ]
|
||||
gantry_height introduced by Jaime while there already was machine_nozzle_gantry_distance
|
||||
|
||||
|
||||
[ RENAMES ]
|
||||
raft_base_linewidth > raft_base_line_width
|
||||
raft_interface_linewidth > raft_interface_line_width
|
||||
fill_overlap > infill_overlap
|
||||
fill_pattern > infill_pattern
|
||||
fill_sparse_combine > infill_sparse_combine
|
||||
fill_sparse_density > infill_sparse_density
|
||||
fill_sparse_thickness > infill_sparse_thickness
|
||||
support_fill_rate > support_infill_rate
|
||||
|
||||
|
||||
[ SPLITS ]
|
||||
raft_line_spacing > raft_base_line_spacing (, raft_interface_line_spacing, raft_surface_line_spacing)
|
||||
wall_overlap_avoid_enabled > remove_overlapping_walls_enabled (, remove_overlapping_walls_0_enabled, remove_overlapping_walls_x_enabled)
|
||||
|
||||
retraction_minimal_extrusion > retraction_extrusion_window (+ retraction_count_max = 1)
|
||||
|
||||
magic_mesh_surface_mode : false >> "Normal"
|
||||
magic_mesh_surface_mode : true >> "Surface"
|
||||
|
||||
|
||||
[ NEW ]
|
||||
alternate_extra_perimeter
|
||||
|
||||
coasting_enable
|
||||
coasting_min_volume
|
||||
coasting_min_volume_move
|
||||
coasting_min_volume_retract
|
||||
coasting_speed
|
||||
coasting_speed_move
|
||||
coasting_speed_retract
|
||||
coasting_volume
|
||||
coasting_volume_move
|
||||
coasting_volume_retract
|
||||
|
||||
fill_perimeter_gaps
|
||||
|
||||
draft_shield_dist
|
||||
draft_shield_enabled
|
||||
draft_shield_height
|
||||
draft_shield_height_limitation
|
||||
|
||||
infill_wipe_dist
|
||||
|
||||
line_width (was wall_line_width)
|
||||
|
||||
machine_extruder_count
|
||||
|
||||
machine_head_polygon
|
||||
machine_head_with_fans_polygon
|
||||
machine_heat_zone_length
|
||||
|
||||
magic_mesh_surface_mode
|
||||
|
||||
meshfix_extensive_stitching
|
||||
meshfix_keep_open_polygons
|
||||
meshfix_union_all
|
||||
meshfix_union_all_remove_holes
|
||||
|
||||
print_sequence
|
||||
|
||||
raft_base_line_spacing (from raft_line_spacing)
|
||||
raft_base_line_width
|
||||
raft_fan_speed
|
||||
raft_interface_fan_speed
|
||||
raft_interface_line_spacing
|
||||
raft_interface_speed
|
||||
raft_speed
|
||||
raft_surface_fan_speed
|
||||
raft_surface_line_spacing
|
||||
raft_surface_line_width
|
||||
raft_surface_speed
|
||||
raft_surface_thickness
|
||||
remove_overlapping_walls_0_enabled
|
||||
remove_overlapping_walls_enabled
|
||||
remove_overlapping_walls_x_enabled
|
||||
|
||||
retraction_count_max
|
||||
retraction_extrusion_window (from retraction_minimal_extrusion)
|
||||
retraction_extra_prime_amount
|
||||
|
||||
skin_alternate_rotation
|
||||
|
||||
speed_support_lines
|
||||
speed_support_roof
|
||||
|
||||
support_conical_angle
|
||||
support_conical_enabled
|
||||
support_conical_min_width
|
||||
support_offset
|
||||
support_roof_enable
|
||||
support_roof_height
|
||||
support_roof_line_width
|
||||
|
||||
travel_avoid_distance
|
||||
travel_avoid_other_parts
|
||||
travel_compensate_overlapping_walls_enabled
|
||||
|
||||
z_seam_type
|
||||
|
||||
|
||||
[ DUAL EXTRUSION ]
|
||||
extruder_nr
|
||||
machine_use_extruder_offset_to_offset_coords
|
||||
machine_nozzle_offset_x
|
||||
machine_nozzle_offset_y
|
||||
machine_extruder_start_code
|
||||
machine_extruder_start_pos_abs
|
||||
machine_extruder_start_pos_x
|
||||
machine_extruder_start_pos_y
|
||||
machine_extruder_end_pos_abs
|
||||
machine_extruder_end_pos_x
|
||||
machine_extruder_end_pos_y
|
||||
machine_extruder_end_code
|
||||
prime_tower_enable
|
||||
prime_tower_size
|
||||
prime_tower_position_x
|
||||
prime_tower_position_y
|
||||
prime_tower_flow
|
||||
prime_tower_wipe_enabled
|
||||
ooze_shield_enabled
|
||||
ooze_shield_angle
|
||||
ooze_shield_dist
|
||||
|
||||
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "modelFile/modelFile.h"
|
||||
|
||||
FILE* binaryMeshBlob = NULL;
|
||||
|
||||
/* Custom fgets function to support Mac line-ends in Ascii STL files. OpenSCAD produces this when used on Mac */
|
||||
void* fgets_(char* ptr, size_t len, FILE* f)
|
||||
{
|
||||
while(len && fread(ptr, 1, 1, f) > 0)
|
||||
{
|
||||
if (*ptr == '\n' || *ptr == '\r')
|
||||
{
|
||||
*ptr = '\0';
|
||||
return ptr;
|
||||
}
|
||||
ptr++;
|
||||
len++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SimpleModel* loadModelSTL_ascii(const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
SimpleModel* m = new SimpleModel();
|
||||
m->volumes.push_back(SimpleVolume());
|
||||
SimpleVolume* vol = &m->volumes[0];
|
||||
FILE* f = fopen(filename, "rt");
|
||||
char buffer[1024];
|
||||
FPoint3 vertex;
|
||||
int n = 0;
|
||||
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (sscanf(buffer, " vertex %lf %lf %lf", &vertex.x, &vertex.y, &vertex.z) == 3)
|
||||
{
|
||||
n++;
|
||||
switch(n)
|
||||
{
|
||||
case 1:
|
||||
v0 = matrix.apply(vertex);
|
||||
break;
|
||||
case 2:
|
||||
v1 = matrix.apply(vertex);
|
||||
break;
|
||||
case 3:
|
||||
v2 = matrix.apply(vertex);
|
||||
vol->addFace(v0, v1, v2);
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return m;
|
||||
}
|
||||
|
||||
SimpleModel* loadModelSTL_binary(const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
char buffer[80];
|
||||
uint32_t faceCount;
|
||||
//Skip the header
|
||||
if (fread(buffer, 80, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
//Read the face count
|
||||
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
//For each face read:
|
||||
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
|
||||
SimpleModel* m = new SimpleModel();
|
||||
m->volumes.push_back(SimpleVolume());
|
||||
SimpleVolume* vol = &m->volumes[0];
|
||||
for(unsigned int i=0;i<faceCount;i++)
|
||||
{
|
||||
if (fread(buffer, sizeof(float) * 3, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
float v[9];
|
||||
if (fread(v, sizeof(float) * 9, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
|
||||
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
|
||||
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
|
||||
vol->addFace(v0, v1, v2);
|
||||
if (fread(buffer, sizeof(uint16_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return m;
|
||||
}
|
||||
|
||||
SimpleModel* loadModelSTL(const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
char buffer[6];
|
||||
if (f == NULL)
|
||||
return NULL;
|
||||
|
||||
if (fread(buffer, 5, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
buffer[5] = '\0';
|
||||
if (strcasecmp(buffer, "SOLID") == 0)
|
||||
{
|
||||
return loadModelSTL_ascii(filename, matrix);
|
||||
}
|
||||
return loadModelSTL_binary(filename, matrix);
|
||||
}
|
||||
|
||||
SimpleModel* loadModel(const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && strcasecmp(ext, ".stl") == 0)
|
||||
{
|
||||
return loadModelSTL(filename, matrix);
|
||||
}
|
||||
if (filename[0] == '#' && binaryMeshBlob != NULL)
|
||||
{
|
||||
SimpleModel* m = new SimpleModel();
|
||||
|
||||
while(*filename == '#')
|
||||
{
|
||||
filename++;
|
||||
|
||||
m->volumes.push_back(SimpleVolume());
|
||||
SimpleVolume* vol = &m->volumes[m->volumes.size()-1];
|
||||
int32_t n, pNr = 0;
|
||||
if (fread(&n, 1, sizeof(int32_t), binaryMeshBlob) < 1)
|
||||
return NULL;
|
||||
printf("Reading mesh from binary blob with %i vertexes\n", n);
|
||||
Point3 v[3];
|
||||
while(n)
|
||||
{
|
||||
float f[3];
|
||||
if (fread(f, 3, sizeof(float), binaryMeshBlob) < 1)
|
||||
return NULL;
|
||||
FPoint3 fp(f[0], f[1], f[2]);
|
||||
v[pNr++] = matrix.apply(fp);
|
||||
if (pNr == 3)
|
||||
{
|
||||
vol->addFace(v[0], v[1], v[2]);
|
||||
pNr = 0;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef MODELFILE_H
|
||||
#define MODELFILE_H
|
||||
/**
|
||||
modelFile contains the model loaders for the slicer. The model loader turns any format that it can read into a list of triangles with 3 X/Y/Z points.
|
||||
|
||||
The format returned is a Model class with an array of faces, which have integer points with a resolution of 1 micron. Giving a maximum object size of 4 meters.
|
||||
**/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
#include "utils/floatpoint.h"
|
||||
|
||||
extern FILE* binaryMeshBlob;
|
||||
|
||||
#define SET_MIN(n, m) do { if ((m) < (n)) n = m; } while(0)
|
||||
#define SET_MAX(n, m) do { if ((m) > (n)) n = m; } while(0)
|
||||
|
||||
/* A SimpleFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers */
|
||||
class SimpleFace
|
||||
{
|
||||
public:
|
||||
Point3 v[3];
|
||||
|
||||
SimpleFace(Point3& v0, Point3& v1, Point3& v2) { v[0] = v0; v[1] = v1; v[2] = v2; }
|
||||
};
|
||||
|
||||
/* A SimpleVolume is the most basic reprisentation of a 3D model. It contains all the faces as SimpleTriangles, with nothing fancy. */
|
||||
class SimpleVolume
|
||||
{
|
||||
public:
|
||||
std::vector<SimpleFace> faces;
|
||||
|
||||
void addFace(Point3& v0, Point3& v1, Point3& v2)
|
||||
{
|
||||
faces.push_back(SimpleFace(v0, v1, v2));
|
||||
}
|
||||
|
||||
Point3 min()
|
||||
{
|
||||
Point3 ret = faces[0].v[0];
|
||||
for(unsigned int i=0; i<faces.size(); i++)
|
||||
{
|
||||
SET_MIN(ret.x, faces[i].v[0].x);
|
||||
SET_MIN(ret.y, faces[i].v[0].y);
|
||||
SET_MIN(ret.z, faces[i].v[0].z);
|
||||
SET_MIN(ret.x, faces[i].v[1].x);
|
||||
SET_MIN(ret.y, faces[i].v[1].y);
|
||||
SET_MIN(ret.z, faces[i].v[1].z);
|
||||
SET_MIN(ret.x, faces[i].v[2].x);
|
||||
SET_MIN(ret.y, faces[i].v[2].y);
|
||||
SET_MIN(ret.z, faces[i].v[2].z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 max()
|
||||
{
|
||||
Point3 ret = faces[0].v[0];
|
||||
for(unsigned int i=0; i<faces.size(); i++)
|
||||
{
|
||||
SET_MAX(ret.x, faces[i].v[0].x);
|
||||
SET_MAX(ret.y, faces[i].v[0].y);
|
||||
SET_MAX(ret.z, faces[i].v[0].z);
|
||||
SET_MAX(ret.x, faces[i].v[1].x);
|
||||
SET_MAX(ret.y, faces[i].v[1].y);
|
||||
SET_MAX(ret.z, faces[i].v[1].z);
|
||||
SET_MAX(ret.x, faces[i].v[2].x);
|
||||
SET_MAX(ret.y, faces[i].v[2].y);
|
||||
SET_MAX(ret.z, faces[i].v[2].z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
//A SimpleModel is a 3D model with 1 or more 3D volumes.
|
||||
class SimpleModel
|
||||
{
|
||||
public:
|
||||
std::vector<SimpleVolume> volumes;
|
||||
|
||||
Point3 min()
|
||||
{
|
||||
Point3 ret = volumes[0].min();
|
||||
for(unsigned int i=0; i<volumes.size(); i++)
|
||||
{
|
||||
Point3 v = volumes[i].min();
|
||||
SET_MIN(ret.x, v.x);
|
||||
SET_MIN(ret.y, v.y);
|
||||
SET_MIN(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 max()
|
||||
{
|
||||
Point3 ret = volumes[0].max();
|
||||
for(unsigned int i=0; i<volumes.size(); i++)
|
||||
{
|
||||
Point3 v = volumes[i].max();
|
||||
SET_MAX(ret.x, v.x);
|
||||
SET_MAX(ret.y, v.y);
|
||||
SET_MAX(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
SimpleModel* loadModel(const char* filename, FMatrix3x3& matrix);
|
||||
|
||||
#endif//MODELFILE_H
|
||||
@@ -1,209 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef OPTIMIZED_MODEL_H
|
||||
#define OPTIMIZED_MODEL_H
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class OptimizedFace
|
||||
{
|
||||
public:
|
||||
int index[3];
|
||||
int touching[3];
|
||||
};
|
||||
class OptimizedPoint3
|
||||
{
|
||||
public:
|
||||
Point3 p;
|
||||
std::vector<uint32_t> faceIndexList;
|
||||
|
||||
OptimizedPoint3(Point3 p): p(p) {}
|
||||
};
|
||||
|
||||
#define MELD_DIST 30
|
||||
class OptimizedModel;
|
||||
class OptimizedVolume
|
||||
{
|
||||
public:
|
||||
OptimizedModel* model;
|
||||
std::vector<OptimizedPoint3> points;
|
||||
std::vector<OptimizedFace> faces;
|
||||
|
||||
OptimizedVolume(SimpleVolume* volume, OptimizedModel* model)
|
||||
: model(model)
|
||||
{
|
||||
points.reserve(volume->faces.size() * 3);
|
||||
faces.reserve(volume->faces.size());
|
||||
|
||||
std::map<uint32_t, std::vector<uint32_t> > indexMap;
|
||||
|
||||
double t = getTime();
|
||||
for(uint32_t i=0; i<volume->faces.size(); i++)
|
||||
{
|
||||
OptimizedFace f;
|
||||
if((i%1000==0) && (getTime()-t)>2.0) logProgress("optimized", i + 1, volume->faces.size());
|
||||
for(uint32_t j=0; j<3; j++)
|
||||
{
|
||||
Point3 p = volume->faces[i].v[j];
|
||||
int hash = ((p.x + MELD_DIST/2) / MELD_DIST) ^ (((p.y + MELD_DIST/2) / MELD_DIST) << 10) ^ (((p.z + MELD_DIST/2) / MELD_DIST) << 20);
|
||||
uint32_t idx;
|
||||
bool add = true;
|
||||
for(unsigned int n = 0; n < indexMap[hash].size(); n++)
|
||||
{
|
||||
if ((points[indexMap[hash][n]].p - p).testLength(MELD_DIST))
|
||||
{
|
||||
idx = indexMap[hash][n];
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
indexMap[hash].push_back(points.size());
|
||||
idx = points.size();
|
||||
points.push_back(p);
|
||||
}
|
||||
f.index[j] = idx;
|
||||
}
|
||||
if (f.index[0] != f.index[1] && f.index[0] != f.index[2] && f.index[1] != f.index[2])
|
||||
{
|
||||
//Check if there is a face with the same points
|
||||
bool duplicate = false;
|
||||
for(unsigned int _idx0 = 0; _idx0 < points[f.index[0]].faceIndexList.size(); _idx0++)
|
||||
{
|
||||
for(unsigned int _idx1 = 0; _idx1 < points[f.index[1]].faceIndexList.size(); _idx1++)
|
||||
{
|
||||
for(unsigned int _idx2 = 0; _idx2 < points[f.index[2]].faceIndexList.size(); _idx2++)
|
||||
{
|
||||
if (points[f.index[0]].faceIndexList[_idx0] == points[f.index[1]].faceIndexList[_idx1] && points[f.index[0]].faceIndexList[_idx0] == points[f.index[2]].faceIndexList[_idx2])
|
||||
duplicate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!duplicate)
|
||||
{
|
||||
points[f.index[0]].faceIndexList.push_back(faces.size());
|
||||
points[f.index[1]].faceIndexList.push_back(faces.size());
|
||||
points[f.index[2]].faceIndexList.push_back(faces.size());
|
||||
faces.push_back(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
//fprintf(stdout, "\rAll faces are optimized in %5.1fs.\n",timeElapsed(t));
|
||||
|
||||
int openFacesCount = 0;
|
||||
for(unsigned int i=0;i<faces.size();i++)
|
||||
{
|
||||
OptimizedFace* f = &faces[i];
|
||||
f->touching[0] = getFaceIdxWithPoints(f->index[0], f->index[1], i);
|
||||
f->touching[1] = getFaceIdxWithPoints(f->index[1], f->index[2], i);
|
||||
f->touching[2] = getFaceIdxWithPoints(f->index[2], f->index[0], i);
|
||||
if (f->touching[0] == -1)
|
||||
openFacesCount++;
|
||||
if (f->touching[1] == -1)
|
||||
openFacesCount++;
|
||||
if (f->touching[2] == -1)
|
||||
openFacesCount++;
|
||||
}
|
||||
//fprintf(stdout, " Number of open faces: %i\n", openFacesCount);
|
||||
}
|
||||
|
||||
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
|
||||
{
|
||||
for(unsigned int i=0;i<points[idx0].faceIndexList.size();i++)
|
||||
{
|
||||
int f0 = points[idx0].faceIndexList[i];
|
||||
if (f0 == notFaceIdx) continue;
|
||||
for(unsigned int j=0;j<points[idx1].faceIndexList.size();j++)
|
||||
{
|
||||
int f1 = points[idx1].faceIndexList[j];
|
||||
if (f1 == notFaceIdx) continue;
|
||||
if (f0 == f1) return f0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
class OptimizedModel
|
||||
{
|
||||
public:
|
||||
vector<OptimizedVolume> volumes;
|
||||
Point3 modelSize;
|
||||
Point3 vMin, vMax;
|
||||
|
||||
OptimizedModel(SimpleModel* model, Point3 center)
|
||||
{
|
||||
for(unsigned int i=0; i<model->volumes.size(); i++)
|
||||
volumes.push_back(OptimizedVolume(&model->volumes[i], this));
|
||||
vMin = model->min();
|
||||
vMax = model->max();
|
||||
|
||||
Point3 vOffset((vMin.x + vMax.x) / 2, (vMin.y + vMax.y) / 2, vMin.z);
|
||||
vOffset -= center;
|
||||
for(unsigned int i=0; i<volumes.size(); i++)
|
||||
for(unsigned int n=0; n<volumes[i].points.size(); n++)
|
||||
volumes[i].points[n].p -= vOffset;
|
||||
|
||||
modelSize = vMax - vMin;
|
||||
vMin -= vOffset;
|
||||
vMax -= vOffset;
|
||||
}
|
||||
|
||||
void saveDebugSTL(const char* filename)
|
||||
{
|
||||
char buffer[80] = "Cura_Engine_STL_export";
|
||||
uint32_t n;
|
||||
uint16_t s;
|
||||
float flt;
|
||||
OptimizedVolume* vol = &volumes[0];
|
||||
FILE* f = fopen(filename, "wb");
|
||||
fwrite(buffer, 80, 1, f);
|
||||
n = vol->faces.size();
|
||||
fwrite(&n, sizeof(n), 1, f);
|
||||
for(unsigned int i=0;i<vol->faces.size();i++)
|
||||
{
|
||||
flt = 0;
|
||||
s = 0;
|
||||
fwrite(&flt, sizeof(flt), 1, f);
|
||||
fwrite(&flt, sizeof(flt), 1, f);
|
||||
fwrite(&flt, sizeof(flt), 1, f);
|
||||
|
||||
flt = vol->points[vol->faces[i].index[0]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[0]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[0]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[1]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[1]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[1]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[2]].p.x / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[2]].p.y / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
flt = vol->points[vol->faces[i].index[2]].p.z / 1000.0; fwrite(&flt, sizeof(flt), 1, f);
|
||||
|
||||
fwrite(&s, sizeof(s), 1, f);
|
||||
}
|
||||
fclose(f);
|
||||
//Export the open faces so you can view the with Cura (hacky)
|
||||
/*
|
||||
char gcodeFilename[1024];
|
||||
strcpy(gcodeFilename, filename);
|
||||
strcpy(strchr(gcodeFilename, '.'), ".gcode");
|
||||
f = fopen(gcodeFilename, "w");
|
||||
for(unsigned int i=0;i<faces.size();i++)
|
||||
{
|
||||
for(int j=0;j<3;j++)
|
||||
{
|
||||
if (faces[i].touching[j] == -1)
|
||||
{
|
||||
Point3 p0 = points[faces[i].index[j]].p;
|
||||
Point3 p1 = points[faces[i].index[(j+1)%3]].p;
|
||||
fprintf(f, ";Model error(open face): (%f, %f, %f) (%f, %f, %f)\n", p0.x / 1000.0, p0.y / 1000.0, p0.z / 1000.0, p1.x / 1000.0, p1.y / 1000.0, p1.z / 1000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
#endif//OPTIMIZED_MODEL_H
|
||||
@@ -1,114 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef PATHOPTIMIZER_H
|
||||
#define PATHOPTIMIZER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
float distanceSquared(const Point& p0, const Point& p1)
|
||||
{
|
||||
float dx = p0.X - p1.X;
|
||||
float dy = p0.Y - p1.Y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
class PathOptimizer
|
||||
{
|
||||
public:
|
||||
Point startPoint;
|
||||
std::vector<ClipperLib::Polygon*> polygons;
|
||||
std::vector<int> polyStart;
|
||||
std::vector<int> polyOrder;
|
||||
|
||||
PathOptimizer(ClipperLib::IntPoint startPoint)
|
||||
{
|
||||
this->startPoint = startPoint;
|
||||
}
|
||||
|
||||
void addPolygon(ClipperLib::Polygon& polygon)
|
||||
{
|
||||
this->polygons.push_back(&polygon);
|
||||
}
|
||||
|
||||
void addPolygons(ClipperLib::Polygons& polygons)
|
||||
{
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
this->polygons.push_back(&polygons[i]);
|
||||
}
|
||||
|
||||
void optimize()
|
||||
{
|
||||
std::vector<bool> picked;
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
||||
ClipperLib::Polygon* poly = polygons[i];
|
||||
for(unsigned int j=0; j<poly->size(); j++)
|
||||
{
|
||||
float dist = distanceSquared((*poly)[j], startPoint);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = j;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
polyStart.push_back(best);
|
||||
picked.push_back(false);
|
||||
}
|
||||
|
||||
Point p0 = startPoint;
|
||||
for(unsigned int n=0; n<polygons.size(); n++)
|
||||
{
|
||||
int best = -1;
|
||||
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
||||
for(unsigned int i=0;i<polygons.size(); i++)
|
||||
{
|
||||
if (picked[i] || (*polygons[i]).size() < 1)
|
||||
continue;
|
||||
float dist = distanceSquared((*polygons[i])[polyStart[i]], p0);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
if (best > -1)
|
||||
{
|
||||
if (polygons[best]->size() == 2)
|
||||
{
|
||||
p0 = (*polygons[best])[(polyStart[best] + 1) % 2];
|
||||
}else{
|
||||
p0 = (*polygons[best])[polyStart[best]];
|
||||
}
|
||||
picked[best] = true;
|
||||
polyOrder.push_back(best);
|
||||
}
|
||||
}
|
||||
|
||||
p0 = startPoint;
|
||||
for(unsigned int n=0; n<polyOrder.size(); n++)
|
||||
{
|
||||
int nr = polyOrder[n];
|
||||
int best = -1;
|
||||
float bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
||||
for(unsigned int i=0;i<polygons[nr]->size(); i++)
|
||||
{
|
||||
float dist = distanceSquared((*polygons[nr])[i], p0);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
best = i;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
polyStart[nr] = best;
|
||||
if ((*polygons[nr]).size() <= 2)
|
||||
{
|
||||
p0 = (*polygons[nr])[(best + 1) % 2];
|
||||
}else{
|
||||
p0 = (*polygons[nr])[best];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif//PATHOPTIMIZER_H
|
||||
@@ -1,50 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef POLYGON_OPTIMIZER_H
|
||||
#define POLYGON_OPTIMIZER_H
|
||||
|
||||
void optimizePolygon(ClipperLib::Polygon& poly)
|
||||
{
|
||||
Point p0 = poly[poly.size()-1];
|
||||
for(unsigned int i=0;i<poly.size();i++)
|
||||
{
|
||||
Point p1 = poly[i];
|
||||
if (shorterThen(p0 - p1, 10))
|
||||
{
|
||||
poly.erase(poly.begin() + i);
|
||||
i --;
|
||||
}else{
|
||||
Point p2;
|
||||
if (i < poly.size() - 1)
|
||||
p2 = poly[i+1];
|
||||
else
|
||||
p2 = poly[0];
|
||||
|
||||
Point diff0 = normal(p1 - p0, 1000000);
|
||||
Point diff2 = normal(p1 - p2, 1000000);
|
||||
|
||||
int64_t d = dot(diff0, diff2);
|
||||
if (d < -999999000000LL)
|
||||
{
|
||||
poly.erase(poly.begin() + i);
|
||||
i --;
|
||||
}else{
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void optimizePolygons(Polygons& polys)
|
||||
{
|
||||
for(unsigned int n=0;n<polys.size();n++)
|
||||
{
|
||||
optimizePolygon(polys[n]);
|
||||
if (polys[n].size() < 3)
|
||||
{
|
||||
polys.erase(polys.begin() + n);
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif//POLYGON_OPTIMIZER_H
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
#ifndef RAFT_H
|
||||
#define RAFT_H
|
||||
|
||||
void generateRaft(SliceDataStorage& storage, int distance)
|
||||
{
|
||||
ClipperLib::Clipper raftUnion;
|
||||
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
|
||||
{
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
|
||||
for(unsigned int i=0; i<layer->parts.size(); i++)
|
||||
{
|
||||
Polygons raft;
|
||||
ClipperLib::OffsetPolygons(layer->parts[i].outline, raft, distance, ClipperLib::jtSquare, 2, false);
|
||||
raftUnion.AddPolygon(raft[0], ClipperLib::ptSubject);
|
||||
}
|
||||
}
|
||||
Polygons raftResult;
|
||||
raftUnion.Execute(ClipperLib::ctUnion, raftResult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
for(unsigned int n=0; n<raftResult.size(); n++)
|
||||
storage.raftOutline.push_back(raftResult[n]);
|
||||
}
|
||||
|
||||
#endif//RAFT_H
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SKIN_H
|
||||
#define SKIN_H
|
||||
|
||||
void generateSkins(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int infillOverlap)
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[partNr];
|
||||
Polygons temp;
|
||||
|
||||
ClipperLib::OffsetPolygons(part->insets[part->insets.size() - 1], temp, -extrusionWidth/2, ClipperLib::jtSquare, 2, false);
|
||||
|
||||
ClipperLib::Clipper downskinClipper;
|
||||
ClipperLib::Clipper upskinClipper;
|
||||
downskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
upskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
|
||||
if (part->insets.size() > 1)
|
||||
{
|
||||
ClipperLib::Clipper thinWallClipper;
|
||||
|
||||
ClipperLib::OffsetPolygons(part->insets[0], temp, -extrusionWidth / 2 - extrusionWidth * infillOverlap / 100, ClipperLib::jtSquare, 2, false);
|
||||
thinWallClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
|
||||
ClipperLib::OffsetPolygons(part->insets[1], temp, extrusionWidth * 6 / 10, ClipperLib::jtSquare, 2, false);
|
||||
thinWallClipper.AddPolygons(temp, ClipperLib::ptClip);
|
||||
|
||||
thinWallClipper.Execute(ClipperLib::ctDifference, temp);
|
||||
downskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
upskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
}
|
||||
|
||||
if (int(layerNr - downSkinCount) >= 0)
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
downskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
|
||||
}
|
||||
}
|
||||
if (int(layerNr + upSkinCount) < (int)storage.layers.size())
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
upskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
|
||||
}
|
||||
}
|
||||
|
||||
ClipperLib::Polygons downSkin;
|
||||
ClipperLib::Polygons upSkin;
|
||||
downskinClipper.Execute(ClipperLib::ctDifference, downSkin);
|
||||
upskinClipper.Execute(ClipperLib::ctDifference, upSkin);
|
||||
|
||||
{
|
||||
ClipperLib::Clipper skinCombineClipper;
|
||||
skinCombineClipper.AddPolygons(downSkin, ClipperLib::ptSubject);
|
||||
skinCombineClipper.AddPolygons(upSkin, ClipperLib::ptClip);
|
||||
skinCombineClipper.Execute(ClipperLib::ctUnion, part->skinOutline);
|
||||
}
|
||||
|
||||
double minAreaSize = (2 * M_PI * (double(extrusionWidth) / 1000.0) * (double(extrusionWidth) / 1000.0)) * 0.3;
|
||||
for(unsigned int i=0; i<part->skinOutline.size(); i++)
|
||||
{
|
||||
double area = fabs(ClipperLib::Area(part->skinOutline[i])) / 1000.0 / 1000.0;
|
||||
if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */
|
||||
{
|
||||
part->skinOutline.erase(part->skinOutline.begin() + i);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
|
||||
{
|
||||
SliceLayer* layer = &storage.layers[layerNr];
|
||||
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
SliceLayerPart* part = &layer->parts[partNr];
|
||||
|
||||
Polygons temp;
|
||||
ClipperLib::OffsetPolygons(part->insets[part->insets.size() - 1], temp, -extrusionWidth/2, ClipperLib::jtSquare, 2, false);
|
||||
|
||||
ClipperLib::Clipper downskinClipper;
|
||||
ClipperLib::Clipper upskinClipper;
|
||||
downskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
upskinClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
if (int(layerNr - downSkinCount) >= 0)
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
{
|
||||
if (layer2->parts[partNr2].insets.size() > 1)
|
||||
{
|
||||
downskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2], ClipperLib::ptClip);
|
||||
}else{
|
||||
downskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (int(layerNr + upSkinCount) < (int)storage.layers.size())
|
||||
{
|
||||
SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount];
|
||||
for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++)
|
||||
{
|
||||
if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox))
|
||||
{
|
||||
if (layer2->parts[partNr2].insets.size() > 1)
|
||||
{
|
||||
upskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2], ClipperLib::ptClip);
|
||||
}else{
|
||||
upskinClipper.AddPolygons(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1], ClipperLib::ptClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClipperLib::Polygons downSkin;
|
||||
ClipperLib::Polygons upSkin;
|
||||
ClipperLib::Polygons result;
|
||||
downskinClipper.Execute(ClipperLib::ctDifference, downSkin);
|
||||
upskinClipper.Execute(ClipperLib::ctDifference, upSkin);
|
||||
|
||||
{
|
||||
ClipperLib::Clipper skinClipper;
|
||||
skinClipper.AddPolygons(downSkin, ClipperLib::ptSubject);
|
||||
skinClipper.AddPolygons(upSkin, ClipperLib::ptClip);
|
||||
skinClipper.Execute(ClipperLib::ctUnion, result);
|
||||
}
|
||||
|
||||
double minAreaSize = 3.0;//(2 * M_PI * (double(config.extrusionWidth) / 1000.0) * (double(config.extrusionWidth) / 1000.0)) * 3;
|
||||
for(unsigned int i=0; i<result.size(); i++)
|
||||
{
|
||||
double area = fabs(ClipperLib::Area(result[i])) / 1000.0 / 1000.0;
|
||||
if (area < minAreaSize) /* Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill" */
|
||||
{
|
||||
result.erase(result.begin() + i);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
ClipperLib::Clipper sparseClipper;
|
||||
sparseClipper.AddPolygons(temp, ClipperLib::ptSubject);
|
||||
sparseClipper.AddPolygons(result, ClipperLib::ptClip);
|
||||
sparseClipper.Execute(ClipperLib::ctDifference, part->sparseOutline);
|
||||
}
|
||||
}
|
||||
|
||||
#endif//SKIN_H
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SKIRT_H
|
||||
#define SKIRT_H
|
||||
|
||||
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count)
|
||||
{
|
||||
for(int skirtNr=0; skirtNr<count;skirtNr++)
|
||||
{
|
||||
ClipperLib::Clipper skirtUnion;
|
||||
for(unsigned int volumeIdx = 0; volumeIdx < storage.volumes.size(); volumeIdx++)
|
||||
{
|
||||
SliceLayer* layer = &storage.volumes[volumeIdx].layers[0];
|
||||
for(unsigned int i=0; i<layer->parts.size(); i++)
|
||||
{
|
||||
Polygons skirt;
|
||||
ClipperLib::OffsetPolygons(layer->parts[i].outline, skirt, distance + extrusionWidth * skirtNr + extrusionWidth / 2, ClipperLib::jtSquare, 2, false);
|
||||
skirtUnion.AddPolygon(skirt[0], ClipperLib::ptSubject);
|
||||
}
|
||||
}
|
||||
Polygons skirtResult;
|
||||
skirtUnion.Execute(ClipperLib::ctUnion, skirtResult, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
for(unsigned int n=0; n<skirtResult.size(); n++)
|
||||
storage.skirt.push_back(skirtResult[n]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif//SKIRT_H
|
||||
@@ -1,73 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICE_DATA_STORAGE_H
|
||||
#define SLICE_DATA_STORAGE_H
|
||||
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
#include "utils/intpoint.h"
|
||||
using ClipperLib::Polygons;
|
||||
|
||||
/*
|
||||
SliceData
|
||||
+ Layers[]
|
||||
+ LayerParts[]
|
||||
+ OutlinePolygons[]
|
||||
+ Insets[]
|
||||
+ Polygons[]
|
||||
+ SkinPolygons[]
|
||||
*/
|
||||
|
||||
class SliceLayerPart
|
||||
{
|
||||
public:
|
||||
AABB boundaryBox;
|
||||
Polygons outline;
|
||||
vector<Polygons> insets;
|
||||
Polygons skinOutline;
|
||||
Polygons sparseOutline;
|
||||
int bridgeAngle;
|
||||
};
|
||||
|
||||
class SliceLayer
|
||||
{
|
||||
public:
|
||||
vector<SliceLayerPart> parts;
|
||||
};
|
||||
|
||||
/******************/
|
||||
class SupportPoint
|
||||
{
|
||||
public:
|
||||
int32_t z;
|
||||
double cosAngle;
|
||||
|
||||
SupportPoint(int32_t z, double cosAngle) : z(z), cosAngle(cosAngle) {}
|
||||
};
|
||||
class SupportStorage
|
||||
{
|
||||
public:
|
||||
Point gridOffset;
|
||||
int32_t gridScale;
|
||||
int32_t gridWidth, gridHeight;
|
||||
vector<SupportPoint>* grid;
|
||||
};
|
||||
/******************/
|
||||
|
||||
class SliceVolumeStorage
|
||||
{
|
||||
public:
|
||||
vector<SliceLayer> layers;
|
||||
};
|
||||
|
||||
class SliceDataStorage
|
||||
{
|
||||
public:
|
||||
Point3 modelSize, modelMin, modelMax;
|
||||
Polygons skirt;
|
||||
Polygons raftOutline;
|
||||
vector<SliceVolumeStorage> volumes;
|
||||
|
||||
SupportStorage support;
|
||||
};
|
||||
|
||||
#endif//SLICE_DATA_STORAGE_H
|
||||
-280
@@ -1,280 +0,0 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef SLICER_H
|
||||
#define SLICER_H
|
||||
|
||||
/*
|
||||
The Slicer creates layers of polygons from an optimized 3D model.
|
||||
The result of the Slicer is a list of polygons without any order or structure.
|
||||
*/
|
||||
|
||||
class SlicerPolygon
|
||||
{
|
||||
public:
|
||||
std::vector<Point> points;
|
||||
bool closed;
|
||||
};
|
||||
|
||||
class SlicerSegment
|
||||
{
|
||||
public:
|
||||
Point start, end;
|
||||
int faceIndex;
|
||||
bool addedToPolygon;
|
||||
};
|
||||
|
||||
class SlicerLayer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerSegment> segmentList;
|
||||
std::map<int, int> faceToSegmentIndex;
|
||||
|
||||
std::vector<SlicerPolygon> polygonList;
|
||||
|
||||
void makePolygons(OptimizedVolume* ov, bool fixHorrible)
|
||||
{
|
||||
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
|
||||
{
|
||||
if (segmentList[startSegment].addedToPolygon)
|
||||
continue;
|
||||
|
||||
SlicerPolygon poly;
|
||||
poly.closed = false;
|
||||
poly.points.push_back(segmentList[startSegment].start);
|
||||
|
||||
unsigned int segmentIndex = startSegment;
|
||||
bool canClose;
|
||||
while(true)
|
||||
{
|
||||
canClose = false;
|
||||
segmentList[segmentIndex].addedToPolygon = true;
|
||||
Point p0 = segmentList[segmentIndex].end;
|
||||
poly.points.push_back(p0);
|
||||
int nextIndex = -1;
|
||||
OptimizedFace* face = &ov->faces[segmentList[segmentIndex].faceIndex];
|
||||
for(unsigned int i=0;i<3;i++)
|
||||
{
|
||||
if (face->touching[i] > -1 && faceToSegmentIndex.find(face->touching[i]) != faceToSegmentIndex.end())
|
||||
{
|
||||
Point p1 = segmentList[faceToSegmentIndex[face->touching[i]]].start;
|
||||
Point diff = p0 - p1;
|
||||
if (shorterThen(diff, 10))
|
||||
{
|
||||
if (faceToSegmentIndex[face->touching[i]] == (int)startSegment)
|
||||
canClose = true;
|
||||
if (segmentList[faceToSegmentIndex[face->touching[i]]].addedToPolygon)
|
||||
continue;
|
||||
nextIndex = faceToSegmentIndex[face->touching[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextIndex == -1)
|
||||
break;
|
||||
segmentIndex = nextIndex;
|
||||
}
|
||||
if (canClose)
|
||||
poly.closed = true;
|
||||
polygonList.push_back(poly);
|
||||
}
|
||||
|
||||
//Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons
|
||||
while(1)
|
||||
{
|
||||
int64_t bestScore = 10000 * 10000;
|
||||
unsigned int bestA = -1;
|
||||
unsigned int bestB = -1;
|
||||
for(unsigned int i=0;i<polygonList.size();i++)
|
||||
{
|
||||
if (polygonList[i].closed) continue;
|
||||
for(unsigned int j=0;j<polygonList.size();j++)
|
||||
{
|
||||
if (polygonList[j].closed) continue;
|
||||
|
||||
Point diff = polygonList[i].points[polygonList[i].points.size()-1] - polygonList[j].points[0];
|
||||
int64_t distSquared = vSize2(diff);
|
||||
if (distSquared < bestScore)
|
||||
{
|
||||
bestScore = distSquared;
|
||||
bestA = i;
|
||||
bestB = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestScore >= 10000 * 10000)
|
||||
break;
|
||||
|
||||
if (bestA == bestB)
|
||||
{
|
||||
polygonList[bestA].closed = true;
|
||||
}else{
|
||||
for(unsigned int n=0; n<polygonList[bestB].points.size(); n++)
|
||||
polygonList[bestA].points.push_back(polygonList[bestB].points[n]);
|
||||
|
||||
polygonList.erase(polygonList.begin() + bestB);
|
||||
}
|
||||
}
|
||||
|
||||
int q=0;
|
||||
for(unsigned int i=0;i<polygonList.size();i++)
|
||||
{
|
||||
if (polygonList[i].closed) continue;
|
||||
if (!q) printf("***\n");
|
||||
printf("S: %f %f\n", float(polygonList[i].points[0].X), float(polygonList[i].points[0].Y));
|
||||
printf("E: %f %f\n", float(polygonList[i].points[polygonList[i].points.size()-1].X), float(polygonList[i].points[polygonList[i].points.size()-1].Y));
|
||||
q = 1;
|
||||
}
|
||||
//if (q) exit(1);
|
||||
|
||||
//Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
|
||||
int snapDistance = 1000;
|
||||
for(unsigned int i=0;i<polygonList.size();i++)
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
for(unsigned int n=1; n<polygonList[i].points.size(); n++)
|
||||
{
|
||||
length += vSize(polygonList[i].points[n] - polygonList[i].points[n-1]);
|
||||
if (length > snapDistance)
|
||||
break;
|
||||
}
|
||||
if (length < snapDistance || !polygonList[i].closed)
|
||||
{
|
||||
polygonList.erase(polygonList.begin() + i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
//Finally optimize all the polygons. Every point removed saves time in the long run.
|
||||
for(unsigned int i=0;i<polygonList.size();i++)
|
||||
{
|
||||
optimizePolygon(polygonList[i].points);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Slicer
|
||||
{
|
||||
public:
|
||||
std::vector<SlicerLayer> layers;
|
||||
Point3 modelSize, modelMin;
|
||||
|
||||
Slicer(OptimizedVolume* ov, int32_t initial, int32_t thickness, bool fixHorrible)
|
||||
{
|
||||
modelSize = ov->model->modelSize;
|
||||
modelMin = ov->model->vMin;
|
||||
|
||||
int layerCount = (modelSize.z - initial) / thickness + 1;
|
||||
fprintf(stdout, "Layer count: %i\n", layerCount);
|
||||
layers.resize(layerCount);
|
||||
|
||||
for(unsigned int i=0; i<ov->faces.size(); i++)
|
||||
{
|
||||
Point3 p0 = ov->points[ov->faces[i].index[0]].p;
|
||||
Point3 p1 = ov->points[ov->faces[i].index[1]].p;
|
||||
Point3 p2 = ov->points[ov->faces[i].index[2]].p;
|
||||
int32_t minZ = p0.z;
|
||||
int32_t maxZ = p0.z;
|
||||
if (p1.z < minZ) minZ = p1.z;
|
||||
if (p2.z < minZ) minZ = p2.z;
|
||||
if (p1.z > maxZ) maxZ = p1.z;
|
||||
if (p2.z > maxZ) maxZ = p2.z;
|
||||
|
||||
for(int32_t layerNr = (minZ - initial) / thickness; layerNr <= (maxZ - initial) / thickness; layerNr++)
|
||||
{
|
||||
int32_t z = layerNr * thickness + initial;
|
||||
if (z < minZ) continue;
|
||||
if (layerNr < 0) continue;
|
||||
|
||||
SlicerSegment s;
|
||||
if (p0.z < z && p1.z >= z && p2.z >= z)
|
||||
s = project2D(p0, p2, p1, z);
|
||||
else if (p0.z > z && p1.z < z && p2.z < z)
|
||||
s = project2D(p0, p1, p2, z);
|
||||
|
||||
else if (p1.z < z && p0.z >= z && p2.z >= z)
|
||||
s = project2D(p1, p0, p2, z);
|
||||
else if (p1.z > z && p0.z < z && p2.z < z)
|
||||
s = project2D(p1, p2, p0, z);
|
||||
|
||||
else if (p2.z < z && p1.z >= z && p0.z >= z)
|
||||
s = project2D(p2, p1, p0, z);
|
||||
else if (p2.z > z && p1.z < z && p0.z < z)
|
||||
s = project2D(p2, p0, p1, z);
|
||||
else
|
||||
{
|
||||
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
|
||||
// on the slice would create two segments
|
||||
continue;
|
||||
}
|
||||
layers[layerNr].faceToSegmentIndex[i] = layers[layerNr].segmentList.size();
|
||||
s.faceIndex = i;
|
||||
s.addedToPolygon = false;
|
||||
layers[layerNr].segmentList.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
double t = getTime();
|
||||
int percDone;
|
||||
for(unsigned int layerNr=0; layerNr<layers.size(); layerNr++)
|
||||
{
|
||||
percDone = 100*layerNr/layers.size();
|
||||
if((getTime()-t)>2.0) fprintf(stdout, "\rProcessing layers... (%d percent)",percDone);
|
||||
layers[layerNr].makePolygons(ov, fixHorrible);
|
||||
}
|
||||
fprintf(stdout, "\rProcessed all layers in %5.1fs \n",timeElapsed(t));
|
||||
}
|
||||
|
||||
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z)
|
||||
{
|
||||
SlicerSegment seg;
|
||||
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
|
||||
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
|
||||
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
|
||||
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
|
||||
return seg;
|
||||
}
|
||||
|
||||
void dumpSegments(const char* filename)
|
||||
{
|
||||
float scale = std::max(modelSize.x, modelSize.y) / 1500;
|
||||
FILE* f = fopen(filename, "w");
|
||||
fprintf(f, "<!DOCTYPE html><html><body>\n");
|
||||
for(unsigned int i=0; i<layers.size(); i++)
|
||||
{
|
||||
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%ipx;height:%ipx'>\n", int(modelSize.x / scale), int(modelSize.y / scale));
|
||||
fprintf(f, "<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:1\">\n");
|
||||
fprintf(f, "<path d=\"");
|
||||
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
|
||||
{
|
||||
SlicerPolygon* p = &layers[i].polygonList[j];
|
||||
if (!p->closed) continue;
|
||||
for(unsigned int n=0; n<p->points.size(); n++)
|
||||
{
|
||||
if (n == 0)
|
||||
fprintf(f, "M");
|
||||
else
|
||||
fprintf(f, "L");
|
||||
fprintf(f, "%f,%f ", float(p->points[n].X - modelMin.x)/scale, float(p->points[n].Y - modelMin.y)/scale);
|
||||
}
|
||||
fprintf(f, "Z\n");
|
||||
}
|
||||
fprintf(f, "\"/>");
|
||||
fprintf(f, "</g>\n");
|
||||
for(unsigned int j=0; j<layers[i].polygonList.size(); j++)
|
||||
{
|
||||
SlicerPolygon* p = &layers[i].polygonList[j];
|
||||
if (p->closed) continue;
|
||||
fprintf(f, "<polyline points=\"");
|
||||
for(unsigned int n=0; n<p->points.size(); n++)
|
||||
{
|
||||
fprintf(f, "%f,%f ", float(p->points[n].X - modelMin.x)/scale, float(p->points[n].Y - modelMin.y)/scale);
|
||||
}
|
||||
fprintf(f, "\" style=\"fill: none; stroke:red;stroke-width:1\" />\n");
|
||||
}
|
||||
fprintf(f, "</svg>\n");
|
||||
}
|
||||
fprintf(f, "</body></html>");
|
||||
fclose(f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif//SLICER_H
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef EXTRUDER_TRAIN_H
|
||||
#define EXTRUDER_TRAIN_H
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class ExtruderTrain : public SettingsBase
|
||||
{
|
||||
int extruder_nr;
|
||||
public:
|
||||
int getExtruderNr() { return extruder_nr; }
|
||||
|
||||
ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
|
||||
: SettingsBase(settings)
|
||||
, extruder_nr(extruder_nr)
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
#endif // EXTRUDER_TRAIN_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef FAN_SPEED_LAYER_TIME_H
|
||||
#define FAN_SPEED_LAYER_TIME_H
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
struct FanSpeedLayerTimeSettings
|
||||
{
|
||||
public:
|
||||
double cool_min_layer_time;
|
||||
double cool_min_layer_time_fan_speed_max;
|
||||
double cool_fan_speed_min;
|
||||
double cool_fan_speed_max;
|
||||
double cool_min_speed;
|
||||
int cool_fan_full_layer;
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // FAN_SPEED_LAYER_TIME_H
|
||||
@@ -0,0 +1,967 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "FffGcodeWriter.h"
|
||||
#include "FffProcessor.h"
|
||||
#include "Progress.h"
|
||||
#include "wallOverlap.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
PrimeTower primetower;
|
||||
|
||||
gcode.preSetup(storage.meshgroup);
|
||||
|
||||
if (meshgroup_number == 1)
|
||||
{
|
||||
gcode.resetTotalPrintTimeAndFilament();
|
||||
}
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
|
||||
setConfigFanSpeedLayerTime();
|
||||
|
||||
setConfigCoasting(storage);
|
||||
|
||||
setConfigRetraction(storage);
|
||||
|
||||
initConfigs(storage);
|
||||
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{ // skirt
|
||||
storage.skirt_config[extruder].setLayerHeight(getSettingInMicrons("layer_height_0"));
|
||||
}
|
||||
|
||||
|
||||
layer_plan_buffer.setPreheatConfig(*storage.meshgroup);
|
||||
|
||||
if (meshgroup_number == 1)
|
||||
{
|
||||
processStartingCode(storage);
|
||||
}
|
||||
else
|
||||
{
|
||||
processNextMeshGroupCode(storage);
|
||||
}
|
||||
meshgroup_number++;
|
||||
|
||||
size_t total_layers = 0;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
total_layers = std::max(total_layers, mesh.layers.size());
|
||||
}
|
||||
|
||||
gcode.writeLayerCountComment(total_layers);
|
||||
|
||||
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
|
||||
if (has_raft)
|
||||
{
|
||||
processRaft(storage, total_layers);
|
||||
}
|
||||
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
last_prime_tower_poly_printed[extruder] = -1; // layer 0 has its prime tower printed during the brim (?)
|
||||
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
processLayer(storage, layer_nr, total_layers, has_raft);
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper);
|
||||
|
||||
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
|
||||
max_object_height = std::max(max_object_height, storage.model_max.z);
|
||||
|
||||
layer_plan_buffer.flush();
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigFanSpeedLayerTime()
|
||||
{
|
||||
fan_speed_layer_time_settings.cool_min_layer_time = getSettingInSeconds("cool_min_layer_time");
|
||||
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = getSettingInSeconds("cool_min_layer_time_fan_speed_max");
|
||||
fan_speed_layer_time_settings.cool_fan_speed_min = getSettingInPercentage("cool_fan_speed_min");
|
||||
fan_speed_layer_time_settings.cool_fan_speed_max = getSettingInPercentage("cool_fan_speed_max");
|
||||
fan_speed_layer_time_settings.cool_min_speed = getSettingInMillimetersPerSecond("cool_min_speed");
|
||||
fan_speed_layer_time_settings.cool_fan_full_layer = getSettingAsCount("cool_fan_full_layer");
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
|
||||
{
|
||||
for (int extr = 0; extr < storage.meshgroup->getExtruderCount(); extr++)
|
||||
{
|
||||
storage.coasting_config.emplace_back();
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extr);
|
||||
CoastingConfig& coasting_config = storage.coasting_config.back();
|
||||
coasting_config.coasting_enable = train->getSettingBoolean("coasting_enable");
|
||||
coasting_config.coasting_volume = train->getSettingInCubicMillimeters("coasting_volume");
|
||||
coasting_config.coasting_min_volume = train->getSettingInCubicMillimeters("coasting_min_volume");
|
||||
coasting_config.coasting_speed = train->getSettingInPercentage("coasting_speed") / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setConfigRetraction(SliceDataStorage& storage)
|
||||
{
|
||||
storage.retraction_config.distance = (storage.getSettingBoolean("retraction_enable"))? INT2MM(getSettingInMicrons("retraction_amount")) : 0;
|
||||
storage.retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
storage.retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
storage.retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
storage.retraction_config.zHop = getSettingInMicrons("retraction_hop");
|
||||
storage.retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
storage.retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
|
||||
storage.retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
|
||||
|
||||
int extruder_count = storage.meshgroup->getExtruderCount();
|
||||
for (int extruder = 0; extruder < extruder_count; extruder++)
|
||||
{
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder];
|
||||
retraction_config.distance = (train->getSettingBoolean("retraction_enable"))? INT2MM(train->getSettingInMicrons("retraction_amount")) : 0;
|
||||
retraction_config.prime_volume = train->getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
retraction_config.speed = train->getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
retraction_config.primeSpeed = train->getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
retraction_config.zHop = train->getSettingInMicrons("retraction_hop");
|
||||
retraction_config.retraction_min_travel_distance = train->getSettingInMicrons("retraction_min_travel");
|
||||
retraction_config.retraction_extrusion_window = INT2MM(train->getSettingInMicrons("retraction_extrusion_window"));
|
||||
retraction_config.retraction_count_max = train->getSettingAsCount("retraction_count_max");
|
||||
}
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.retraction_config.distance = (mesh.getSettingBoolean("retraction_enable"))? INT2MM(mesh.getSettingInMicrons("retraction_amount")) : 0;
|
||||
mesh.retraction_config.prime_volume = mesh.getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
mesh.retraction_config.speed = mesh.getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
mesh.retraction_config.primeSpeed = mesh.getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
mesh.retraction_config.zHop = mesh.getSettingInMicrons("retraction_hop");
|
||||
mesh.retraction_config.retraction_min_travel_distance = mesh.getSettingInMicrons("retraction_min_travel");
|
||||
mesh.retraction_config.retraction_extrusion_window = INT2MM(mesh.getSettingInMicrons("retraction_extrusion_window"));
|
||||
mesh.retraction_config.retraction_count_max = mesh.getSettingAsCount("retraction_count_max");
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
|
||||
{
|
||||
storage.travel_config.init(getSettingInMillimetersPerSecond("speed_travel"), 0, 0);
|
||||
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
{ // skirt
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
|
||||
storage.skirt_config[extruder].init(train->getSettingInMillimetersPerSecond("skirt_speed"), train->getSettingInMicrons("skirt_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
|
||||
{ // support
|
||||
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_extruder_nr"));
|
||||
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_lines"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
|
||||
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
}
|
||||
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
|
||||
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
|
||||
|
||||
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
|
||||
{
|
||||
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
|
||||
}
|
||||
}
|
||||
|
||||
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
std::string prefix = gcode.getFileHeader();
|
||||
gcode.writeCode(prefix.c_str());
|
||||
}
|
||||
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (getSettingBoolean("material_bed_temp_prepend"))
|
||||
{
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
{
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
|
||||
}
|
||||
}
|
||||
|
||||
if (getSettingBoolean("material_print_temp_prepend"))
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
{
|
||||
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"));
|
||||
}
|
||||
}
|
||||
if (getSettingBoolean("material_print_temp_wait"))
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
{
|
||||
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
|
||||
|
||||
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
|
||||
if (gcode.getFlavor() == EGCodeFlavor::BFB)
|
||||
{
|
||||
gcode.writeComment("enable auto-retraction");
|
||||
std::ostringstream tmp;
|
||||
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000);
|
||||
gcode.writeLine(tmp.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
|
||||
{
|
||||
gcode.writeFanCommand(0);
|
||||
gcode.resetExtrusionValue();
|
||||
gcode.setZ(max_object_height + 5000);
|
||||
gcode.writeMove(gcode.getPositionXY(), getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
last_position_planned = Point(storage.model_min.x, storage.model_min.y);
|
||||
gcode.writeMove(last_position_planned, getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
|
||||
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
|
||||
|
||||
bool retraction_combing = true;
|
||||
|
||||
int n_raft_surface_layers = train->getSettingAsCount("raft_surface_layers");
|
||||
|
||||
int z = 0;
|
||||
|
||||
{ // set configs
|
||||
storage.raft_base_config.init(train->getSettingInMillimetersPerSecond("raft_base_speed"), train->getSettingInMicrons("raft_base_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_base_config.setLayerHeight(train->getSettingInMicrons("raft_base_thickness"));
|
||||
|
||||
storage.raft_interface_config.init(train->getSettingInMillimetersPerSecond("raft_interface_speed"), train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_interface_config.setLayerHeight(train->getSettingInMicrons("raft_interface_thickness"));
|
||||
|
||||
storage.raft_surface_config.init(train->getSettingInMillimetersPerSecond("raft_surface_speed"), train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInPercentage("material_flow"));
|
||||
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
|
||||
}
|
||||
|
||||
// some infill config for all lines infill generation below
|
||||
Polygons* in_between = nullptr;
|
||||
int offset_from_poly_outline = 0;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
double fill_overlap = 0; // raft line shouldn't be expanded - there is no boundary polygon printed
|
||||
Polygons raft_polygons; // should remain empty, since we only have the lines pattern for the raft...
|
||||
|
||||
{ // raft base layer
|
||||
|
||||
int layer_nr = -n_raft_surface_layers - 2;
|
||||
int layer_height = getSettingInMicrons("raft_base_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_base_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
|
||||
gcode_layer.setExtruder(extruder_nr);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
|
||||
|
||||
Polygons raftLines;
|
||||
double fill_angle = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raftLines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_base_fan_speed"));
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
{ // raft interface layer
|
||||
int layer_nr = -n_raft_surface_layers - 1;
|
||||
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_interface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
|
||||
Polygons raftLines;
|
||||
int offset_from_poly_outline = 0;
|
||||
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raftLines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config, SpaceFillType::Lines);
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_interface_fan_speed"));
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
int layer_height = train->getSettingInMicrons("raft_surface_thickness");
|
||||
|
||||
for (int raftSurfaceLayer=1; raftSurfaceLayer <= n_raft_surface_layers; raftSurfaceLayer++)
|
||||
{ // raft surface layers
|
||||
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
|
||||
z += layer_height;
|
||||
int64_t comb_offset = train->getSettingInMicrons("raft_surface_line_spacing");
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, comb_offset, train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
|
||||
}
|
||||
|
||||
Polygons raft_lines;
|
||||
int offset_from_poly_outline = 0;
|
||||
double fill_angle = 90 * raftSurfaceLayer;
|
||||
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle);
|
||||
infill_comp.generate(raft_polygons, raft_lines, in_between);
|
||||
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config, SpaceFillType::Lines);
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_surface_fan_speed"));
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers);
|
||||
|
||||
int layer_thickness = getSettingInMicrons("layer_height");
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
}
|
||||
|
||||
ExtruderTrain* current_extruder_train = storage.meshgroup->getExtruderTrain(current_extruder_planned);
|
||||
|
||||
int64_t comb_offset_from_outlines = current_extruder_train->getSettingInMicrons((current_extruder_train->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 2; // TODO: only used when there is no second wall.
|
||||
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
|
||||
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
|
||||
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
int start_extruder = 0; // TODO: make settable
|
||||
gcode_layer.setExtruder(start_extruder);
|
||||
processSkirt(storage, gcode_layer, start_extruder);
|
||||
}
|
||||
|
||||
int extruder_nr_before = gcode_layer.getExtruder();
|
||||
addSupportToGCode(storage, gcode_layer, layer_nr, extruder_nr_before, true);
|
||||
|
||||
processOozeShield(storage, gcode_layer, layer_nr);
|
||||
|
||||
processDraftShield(storage, gcode_layer, layer_nr);
|
||||
|
||||
//Figure out in which order to print the meshes, do this by looking at the current extruder and preferer the meshes that use that extruder.
|
||||
std::vector<unsigned int> mesh_order = calculateMeshOrder(storage, gcode_layer.getExtruder());
|
||||
gcode_layer.setIsInside(true);
|
||||
for(unsigned int mesh_idx : mesh_order)
|
||||
{
|
||||
SliceMeshStorage* mesh = &storage.meshes[mesh_idx];
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
|
||||
{
|
||||
gcode_layer.setIsInside(false);
|
||||
addMeshLayerToGCode_meshSurfaceMode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.setIsInside(true); // needed when the last mesh was spiralized
|
||||
addMeshLayerToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
gcode_layer.setIsInside(false);
|
||||
|
||||
addSupportToGCode(storage, gcode_layer, layer_nr, extruder_nr_before, false);
|
||||
|
||||
{ // add prime tower if it hasn't already been added
|
||||
// print the prime tower if it hasn't been printed yet
|
||||
int prev_extruder = gcode_layer.getExtruder(); // most likely the same extruder as we are extruding with now
|
||||
addPrimeTower(storage, gcode_layer, layer_nr, prev_extruder);
|
||||
}
|
||||
|
||||
last_position_planned = gcode_layer.getLastPosition();
|
||||
current_extruder_planned = gcode_layer.getExtruder();
|
||||
|
||||
gcode_layer.processFanSpeedAndMinimalLayerTime();
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSkirt(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int extruder_nr)
|
||||
{
|
||||
gcode_layer.setIsInside(false);
|
||||
Polygons& skirt = storage.skirt[extruder_nr];
|
||||
if (skirt.size() > 0)
|
||||
{
|
||||
gcode_layer.addTravel(skirt[skirt.size()-1].closestPointTo(gcode_layer.getLastPosition()));
|
||||
}
|
||||
gcode_layer.addPolygonsByOptimizer(skirt, &storage.skirt_config[extruder_nr]);
|
||||
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processOozeShield(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int layer_nr)
|
||||
{
|
||||
if (storage.oozeShield.size() > 0)
|
||||
{
|
||||
gcode_layer.setIsInside(false);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.oozeShield[layer_nr], &storage.skirt_config[0]); // TODO: skirt config idx should correspond to ooze shield extruder nr
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int layer_nr)
|
||||
{
|
||||
if (storage.draft_protection_shield.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int draft_shield_height = getSettingInMicrons("draft_shield_height");
|
||||
int layer_height_0 = getSettingInMicrons("layer_height_0");
|
||||
int layer_height = getSettingInMicrons("layer_height");
|
||||
|
||||
int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
|
||||
|
||||
if (int(layer_nr) > max_screen_layer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_config[0]); // TODO: skirt config idx should correspond to draft shield extruder nr
|
||||
}
|
||||
|
||||
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int current_extruder)
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
std::list<unsigned int> add_list;
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
||||
add_list.push_back(mesh_idx);
|
||||
|
||||
int add_extruder_nr = current_extruder;
|
||||
while(add_list.size() > 0)
|
||||
{
|
||||
for(auto add_it = add_list.begin(); add_it != add_list.end(); )
|
||||
{
|
||||
if (storage.meshes[*add_it].getSettingAsIndex("extruder_nr") == add_extruder_nr)
|
||||
{
|
||||
ret.push_back(*add_it);
|
||||
add_it = add_list.erase(add_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++add_it;
|
||||
}
|
||||
}
|
||||
if (add_list.size() > 0)
|
||||
add_extruder_nr = storage.meshes[*add_list.begin()].getSettingAsIndex("extruder_nr");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (layer_nr > mesh->layer_nr_max_filled_layer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
|
||||
|
||||
SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
|
||||
Polygons polygons;
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
polygons.add(layer->parts[partNr].outline);
|
||||
}
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
gcode_layer.addPolygonsByOptimizer(polygons, &mesh->inset0_config, nullptr, z_seam_type, mesh->getSettingBoolean("magic_spiralize"));
|
||||
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
Polygons lines;
|
||||
for(PolygonRef polyline : layer->openPolyLines)
|
||||
{
|
||||
for(unsigned int point_idx = 1; point_idx<polyline.size(); point_idx++)
|
||||
{
|
||||
Polygon p;
|
||||
p.add(polyline[point_idx-1]);
|
||||
p.add(polyline[point_idx]);
|
||||
lines.add(p);
|
||||
}
|
||||
}
|
||||
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
|
||||
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (layer_nr > mesh->layer_nr_max_filled_layer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SliceLayer* layer = &mesh->layers[layer_nr];
|
||||
|
||||
if (layer->parts.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, mesh->getSettingAsIndex("extruder_nr"));
|
||||
|
||||
|
||||
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
|
||||
PathOrderOptimizer part_order_optimizer(last_position_planned, z_seam_type);
|
||||
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
|
||||
{
|
||||
part_order_optimizer.addPolygon(layer->parts[partNr].insets[0][0]);
|
||||
}
|
||||
part_order_optimizer.optimize();
|
||||
|
||||
bool skin_alternate_rotation = mesh->getSettingBoolean("skin_alternate_rotation") && ( mesh->getSettingAsCount("top_layers") >= 4 || mesh->getSettingAsCount("bottom_layers") >= 4 );
|
||||
|
||||
for(int order_idx : part_order_optimizer.polyOrder)
|
||||
{
|
||||
SliceLayerPart& part = layer->parts[order_idx];
|
||||
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
int infill_angle = 45;
|
||||
if ((infill_pattern==EFillMethod::LINES || infill_pattern==EFillMethod::ZIG_ZAG) && layer_nr & 1)
|
||||
{
|
||||
infill_angle += 90;
|
||||
}
|
||||
int infill_line_width = mesh->infill_config[0].getLineWidth();
|
||||
|
||||
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
|
||||
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
|
||||
|
||||
if (mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
}
|
||||
|
||||
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
|
||||
|
||||
if (!mesh->getSettingBoolean("infill_before_walls"))
|
||||
{
|
||||
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
|
||||
}
|
||||
|
||||
EFillMethod skin_pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int skin_angle = 45;
|
||||
if ((skin_pattern == EFillMethod::LINES || skin_pattern == EFillMethod::ZIG_ZAG) && layer_nr & 1)
|
||||
{
|
||||
skin_angle += 90; // should coincide with infill_angle (if both skin and infill are lines) so that the first top layer is orthogonal to the last infill layer
|
||||
}
|
||||
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
|
||||
skin_angle -= 45;
|
||||
|
||||
int64_t skin_overlap = infill_overlap;
|
||||
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle, mesh->skin_config.getLineWidth());
|
||||
|
||||
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
|
||||
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
|
||||
{
|
||||
gcode_layer.moveInsideCombBoundary(mesh->getSettingInMicrons((mesh->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
|
||||
}
|
||||
}
|
||||
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
addMeshOpenPolyLinesToGCode(storage, mesh, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
if (infill_line_distance > 0)
|
||||
{
|
||||
//Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers)
|
||||
for(unsigned int n=1; n<part.infill_area.size(); n++)
|
||||
{
|
||||
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
Infill infill_comp(infill_pattern, part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
infill_comp.generate(infill_polygons, infill_lines, nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[n]);
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
|
||||
if (infill_line_distance == 0 || part.infill_area.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Combine the 1 layer thick infill with the top/bottom skin and print that as one thing.
|
||||
Polygons infill_polygons;
|
||||
Polygons infill_lines;
|
||||
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("infill_pattern");
|
||||
Infill infill_comp(pattern, part.infill_area[0], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
|
||||
infill_comp.generate(infill_polygons, infill_lines, nullptr);
|
||||
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
|
||||
{
|
||||
bool compensate_overlap = mesh->getSettingBoolean("travel_compensate_overlapping_walls_enabled");
|
||||
if (mesh->getSettingAsCount("wall_line_count") > 0)
|
||||
{
|
||||
bool spiralize = false;
|
||||
if (mesh->getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
if (static_cast<int>(layer_nr) >= mesh->getSettingAsCount("bottom_layers"))
|
||||
{
|
||||
spiralize = true;
|
||||
}
|
||||
if (static_cast<int>(layer_nr) == mesh->getSettingAsCount("bottom_layers") && part.insets.size() > 0)
|
||||
{ // on the last normal layer first make the outer wall normally and then start a second outer wall from the same hight, but gradually moving upward
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->insetX_config, nullptr, EZSeamType::SHORTEST, false);
|
||||
}
|
||||
}
|
||||
for(int inset_number=part.insets.size()-1; inset_number>-1; inset_number--)
|
||||
{
|
||||
if (inset_number == 0)
|
||||
{
|
||||
if (!compensate_overlap)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[0], &mesh->inset0_config, nullptr, z_seam_type, spiralize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Polygons& outer_wall = part.insets[0];
|
||||
WallOverlapComputation wall_overlap_computation(outer_wall, mesh->getSettingInMicrons("wall_line_width_0"));
|
||||
gcode_layer.addPolygonsByOptimizer(outer_wall, &mesh->inset0_config, &wall_overlap_computation, z_seam_type, spiralize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(part.insets[inset_number], &mesh->insetX_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
|
||||
{
|
||||
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
|
||||
{
|
||||
Polygons skin_polygons;
|
||||
Polygons skin_lines;
|
||||
|
||||
EFillMethod pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
|
||||
int bridge = -1;
|
||||
if (layer_nr > 0)
|
||||
bridge = bridgeAngle(skin_part.outline, &mesh->layers[layer_nr-1]);
|
||||
if (bridge > -1)
|
||||
{
|
||||
pattern = EFillMethod::LINES;
|
||||
}
|
||||
Polygons* inner_skin_outline = nullptr;
|
||||
int offset_from_inner_skin_outline = 0;
|
||||
if (pattern != EFillMethod::CONCENTRIC)
|
||||
{
|
||||
for (Polygons& skin_perimeter : skin_part.insets)
|
||||
{
|
||||
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->skin_config); // add polygons to gcode in inward order
|
||||
}
|
||||
if (skin_part.insets.size() > 0)
|
||||
{
|
||||
inner_skin_outline = &skin_part.insets.back();
|
||||
offset_from_inner_skin_outline = -extrusion_width/2;
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int line_distance = extrusion_width;
|
||||
int outline_offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, skin_part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
|
||||
infill_comp.generate(result_polygons, skin_lines, in_between);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inner_skin_outline == nullptr)
|
||||
{
|
||||
inner_skin_outline = &skin_part.outline;
|
||||
}
|
||||
|
||||
Infill infill_comp(pattern, *inner_skin_outline, offset_from_inner_skin_outline, mesh->getSettingBoolean("remove_overlapping_walls_x_enabled"), extrusion_width, extrusion_width, infill_overlap, infill_angle, false, false);
|
||||
infill_comp.generate(skin_polygons, skin_lines, &part.perimeterGaps);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
|
||||
|
||||
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
// handle gaps between perimeters etc.
|
||||
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
|
||||
{
|
||||
Polygons perimeter_gap_lines;
|
||||
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int line_distance = extrusion_width;
|
||||
int outline_offset = 0;
|
||||
Infill infill_comp(EFillMethod::LINES, part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
|
||||
infill_comp.generate(result_polygons, perimeter_gap_lines, in_between);
|
||||
|
||||
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr_before, bool before_rest)
|
||||
{
|
||||
if (!storage.support.generated || layer_nr > storage.support.layer_nr_max_filled_layer)
|
||||
return;
|
||||
|
||||
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
|
||||
bool print_support_before_rest = support_extruder_nr == extruder_nr_before
|
||||
|| support_roof_extruder_nr == extruder_nr_before;
|
||||
// TODO: always print support after rest when only one nozzle is used for the whole meshgroup
|
||||
|
||||
if (print_support_before_rest != before_rest)
|
||||
return;
|
||||
|
||||
gcode_layer.setIsInside(false);
|
||||
|
||||
int current_extruder_nr = gcode_layer.getExtruder();
|
||||
|
||||
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
|
||||
{
|
||||
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
|
||||
{
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
|| storage.support.supportLayers[layer_nr].supportAreas.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int support_line_distance = getSettingInMicrons("support_line_distance");
|
||||
int extrusion_width = storage.support_config.getLineWidth();
|
||||
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
|
||||
if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
|
||||
|
||||
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
|
||||
|
||||
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
|
||||
|
||||
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
|
||||
|
||||
std::vector<PolygonsPart> support_islands = support.splitIntoParts();
|
||||
|
||||
PathOrderOptimizer island_order_optimizer(gcode_layer.getLastPosition());
|
||||
for(unsigned int n=0; n<support_islands.size(); n++)
|
||||
{
|
||||
island_order_optimizer.addPolygon(support_islands[n][0]);
|
||||
}
|
||||
island_order_optimizer.optimize();
|
||||
|
||||
for(unsigned int n=0; n<support_islands.size(); n++)
|
||||
{
|
||||
PolygonsPart& island = support_islands[island_order_optimizer.polyOrder[n]];
|
||||
|
||||
double infill_overlap = 0; // support infill should not be expanded outward
|
||||
|
||||
int offset_from_outline = 0;
|
||||
bool remove_overlapping_perimeters = false;
|
||||
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
Polygons boundary;
|
||||
PolygonUtils::offsetSafe(island, -extrusion_width / 2, extrusion_width, boundary, remove_overlapping_perimeters);
|
||||
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
|
||||
offset_from_outline = -extrusion_width;
|
||||
infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap"); // support lines area should be expanded outward to overlap with the boundary polygon
|
||||
}
|
||||
Infill infill_comp(support_pattern, island, offset_from_outline, remove_overlapping_perimeters, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
|
||||
Polygons support_polygons;
|
||||
Polygons support_lines;
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
|
||||
{
|
||||
if (!storage.support.generated
|
||||
|| layer_nr > storage.support.layer_nr_max_filled_layer
|
||||
|| storage.support.supportLayers[layer_nr].roofs.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EFillMethod pattern = getSettingAsFillMethod("support_roof_pattern");
|
||||
int support_line_distance = getSettingInMicrons("support_roof_line_distance");
|
||||
|
||||
int roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
|
||||
setExtruder_addPrime(storage, gcode_layer, layer_nr, roof_extruder_nr);
|
||||
|
||||
double fillAngle;
|
||||
if (pattern == EFillMethod::CONCENTRIC)
|
||||
{
|
||||
fillAngle = 0;
|
||||
}
|
||||
else if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height") || pattern == EFillMethod::TRIANGLES)
|
||||
{
|
||||
fillAngle = 90; // perpendicular to support lines
|
||||
}
|
||||
else
|
||||
{
|
||||
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
|
||||
}
|
||||
double infill_overlap = 0; // the roofs should never be expanded outwards
|
||||
int outline_offset = 0;
|
||||
|
||||
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, false, storage.support_roof_config.getLineWidth(), support_line_distance, infill_overlap, fillAngle, false, true);
|
||||
Polygons support_polygons;
|
||||
Polygons support_lines;
|
||||
infill_comp.generate(support_polygons, support_lines, nullptr);
|
||||
|
||||
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_roof_config);
|
||||
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
|
||||
{
|
||||
if (extruder_nr == -1) // an object with extruder_nr==-1 means it will be printed with any current nozzle
|
||||
return;
|
||||
|
||||
int previous_extruder = gcode_layer.getExtruder();
|
||||
if (previous_extruder == extruder_nr) { return; }
|
||||
bool extruder_changed = gcode_layer.setExtruder(extruder_nr);
|
||||
|
||||
if (extruder_changed)
|
||||
{
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
processSkirt(storage, gcode_layer, extruder_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addPrimeTower(storage, gcode_layer, layer_nr, previous_extruder);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder)
|
||||
{
|
||||
|
||||
if (getSettingInMicrons("prime_tower_size") < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool prime_tower_dir_outward = getSettingBoolean("prime_tower_dir_outward");
|
||||
bool wipe = getSettingBoolean("prime_tower_wipe_enabled");
|
||||
|
||||
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
}
|
||||
|
||||
void FffGcodeWriter::finalize()
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
std::string prefix = gcode.getFileHeader(gcode.getTotalPrintTime(), gcode.getTotalFilamentUsed(0), gcode.getTotalFilamentUsed(1));
|
||||
CommandSocket::getInstance()->sendGCodePrefix(prefix);
|
||||
}
|
||||
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
|
||||
gcode.writeTemperatureCommand(e, 0, false);
|
||||
|
||||
gcode.writeComment("End of Gcode");
|
||||
/*
|
||||
the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB.
|
||||
gcode.writeCode("M25 ;Stop reading from this point on.");
|
||||
gcode.writeComment("Cura profile string:");
|
||||
gcode.writeComment(FffProcessor::getInstance()->getAllLocalSettingsString() + FffProcessor::getInstance()->getProfileString());
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,307 @@
|
||||
#ifndef GCODE_WRITER_H
|
||||
#define GCODE_WRITER_H
|
||||
|
||||
|
||||
#include <fstream>
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "raft.h"
|
||||
#include "infill.h"
|
||||
#include "bridge.h"
|
||||
#include "pathOrderOptimizer.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "commandSocket.h"
|
||||
#include "PrimeTower.h"
|
||||
#include "FanSpeedLayerTime.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Secondary stage in Fused Filament Fabrication processing: The generated polygons are used in the gcode generation.
|
||||
* Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines,
|
||||
* while other polygons signify the contours which should be printed.
|
||||
*
|
||||
* The main function of this class is FffGcodeWriter::writeGCode().
|
||||
*/
|
||||
class FffGcodeWriter : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
|
||||
private:
|
||||
int max_object_height;
|
||||
int meshgroup_number; //!< used for sequential printing of objects
|
||||
|
||||
LayerPlanBuffer layer_plan_buffer;
|
||||
|
||||
GCodeExport gcode;
|
||||
std::ofstream output_file;
|
||||
|
||||
/*!
|
||||
* Layer number of the last layer in which a prime tower has been printed per extruder train.
|
||||
*
|
||||
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
|
||||
*/
|
||||
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
|
||||
|
||||
FanSpeedLayerTimeSettings fan_speed_layer_time_settings;
|
||||
|
||||
Point last_position_planned; //!< The position of the head before planning the next layer
|
||||
int current_extruder_planned; //!< The extruder train in use before planning the next layer
|
||||
public:
|
||||
FffGcodeWriter(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
, layer_plan_buffer(this, gcode)
|
||||
, last_position_planned(no_point)
|
||||
, current_extruder_planned(0) // TODO: make configurable
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
max_object_height = 0;
|
||||
}
|
||||
void resetFileNumber()
|
||||
{
|
||||
meshgroup_number = 1;
|
||||
}
|
||||
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
output_file.open(filename);
|
||||
if (output_file.is_open())
|
||||
{
|
||||
gcode.setOutputStream(&output_file);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setTargetStream(std::ostream* stream)
|
||||
{
|
||||
gcode.setOutputStream(stream);
|
||||
}
|
||||
|
||||
double getTotalFilamentUsed(int e)
|
||||
{
|
||||
return gcode.getTotalFilamentUsed(e);
|
||||
}
|
||||
|
||||
double getTotalPrintTime()
|
||||
{
|
||||
return gcode.getTotalPrintTime();
|
||||
}
|
||||
|
||||
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
|
||||
|
||||
private:
|
||||
void setConfigFanSpeedLayerTime();
|
||||
|
||||
void setConfigCoasting(SliceDataStorage& storage);
|
||||
|
||||
//Setup the retraction parameters.
|
||||
void setConfigRetraction(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* initialize GcodePathConfig config parameters which don't change over all layers
|
||||
*/
|
||||
void initConfigs(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Set temperatures and perform initial priming.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
*/
|
||||
void processStartingCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Move up and over the just printed model to print the next model.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
*/
|
||||
void processNextMeshGroupCode(SliceDataStorage& storage);
|
||||
|
||||
/*!
|
||||
* Add raft gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param total_layers The total number of layers.
|
||||
*/
|
||||
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Add a layer to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param total_layers The total number of layers.
|
||||
* \param has_raft Whether a raft is used for this print.
|
||||
*/
|
||||
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
|
||||
|
||||
/*!
|
||||
* Add the skirt to the gcode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param extruder_nr The extrudewr train for which to process the skirt
|
||||
*/
|
||||
void processSkirt(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Adds the ooze shield to the print.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Adds the draft protection screen to the print.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void processDraftShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Calculate in which order to print the meshes.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param current_extruder The current extruder with which we last printed
|
||||
* \return A vector of mesh indices ordered on print order.
|
||||
*/
|
||||
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the GCode in mesh surface mode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh to add to the gcode.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add the open polylines from a single layer from a single mesh-volume to the GCode for mesh surface mode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add a single layer from a single mesh-volume to the GCode.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param mesh The mesh to add to the gcode.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*
|
||||
*/
|
||||
void addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Add thicker (multiple layers) sparse infill for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
|
||||
/*!
|
||||
* Add normal sparse infill for a given part in a layer.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_line_distance The distance between the infill lines
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
|
||||
|
||||
/*!
|
||||
* Generate the insets for the walls of a given layer part.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
|
||||
*/
|
||||
void processInsets(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type);
|
||||
|
||||
|
||||
/*!
|
||||
* Add the gcode of the top/bottom skin of the given part.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param mesh The mesh for which to add to the gcode.
|
||||
* \param part The part for which to create gcode
|
||||
* \param layer_nr The current layer number.
|
||||
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
|
||||
* \param fillAngle The angle in the XY plane at which the infill is generated.
|
||||
* \param extrusionWidth extrusionWidth
|
||||
*/
|
||||
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width);
|
||||
|
||||
/*!
|
||||
* Add the support to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
|
||||
* \param before_rest Whether the function has been called before adding the rest to the gcode, or after.
|
||||
*/
|
||||
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
|
||||
/*!
|
||||
* Add the support lines/walls to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
/*!
|
||||
* Add the support roofs to the gcode of the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
*/
|
||||
void addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
|
||||
|
||||
/*!
|
||||
* Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last.
|
||||
*
|
||||
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
|
||||
*
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param extruder_nr The extruder to which to switch
|
||||
*/
|
||||
void setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr);
|
||||
|
||||
/*!
|
||||
* Add the prime tower gcode for the current layer.
|
||||
* \param storage Input: where the slice data is stored.
|
||||
* \param gcodeLayer The initial planning of the gcode of the layer.
|
||||
* \param layer_nr The index of the layer to write the gcode of.
|
||||
* \param prev_extruder The current extruder with which we last printed.
|
||||
*/
|
||||
void addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder);
|
||||
|
||||
/*!
|
||||
* Add the end gcode and set all temperatures to zero.
|
||||
*/
|
||||
void finalize();
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif // GCODE_WRITER_H
|
||||
@@ -0,0 +1,474 @@
|
||||
#include "FffPolygonGenerator.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "slicer.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "MeshGroup.h"
|
||||
#include "support.h"
|
||||
#include "multiVolumes.h"
|
||||
#include "layerPart.h"
|
||||
#include "inset.h"
|
||||
#include "skirt.h"
|
||||
#include "skin.h"
|
||||
#include "infill.h"
|
||||
#include "raft.h"
|
||||
#include "debug.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
|
||||
{
|
||||
if (!sliceModel(meshgroup, timeKeeper, storage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
slices2polygons(storage, timeKeeper);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
|
||||
{
|
||||
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
|
||||
|
||||
storage.model_min = meshgroup->min();
|
||||
storage.model_max = meshgroup->max();
|
||||
storage.model_size = storage.model_max - storage.model_min;
|
||||
|
||||
log("Slicing model...\n");
|
||||
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
|
||||
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
|
||||
{
|
||||
logError("Initial layer height %i is disallowed.",initial_layer_thickness);
|
||||
return false;
|
||||
}
|
||||
int layer_thickness = meshgroup->getSettingInMicrons("layer_height");
|
||||
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
|
||||
{
|
||||
logError("Layer height %i is disallowed.",layer_thickness);
|
||||
return false;
|
||||
}
|
||||
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
|
||||
{
|
||||
initial_layer_thickness = layer_thickness;
|
||||
}
|
||||
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
|
||||
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
|
||||
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
|
||||
{
|
||||
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
|
||||
return true; //This is NOT an error state!
|
||||
}
|
||||
|
||||
std::vector<Slicer*> slicerList;
|
||||
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
|
||||
{
|
||||
Mesh& mesh = meshgroup->meshes[mesh_idx];
|
||||
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
slicerList.push_back(slicer);
|
||||
/*
|
||||
for(SlicerLayer& layer : slicer->layers)
|
||||
{
|
||||
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
|
||||
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
|
||||
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
|
||||
}
|
||||
*/
|
||||
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
|
||||
}
|
||||
|
||||
log("Layer count: %i\n", layer_count);
|
||||
|
||||
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
|
||||
//carveMultipleVolumes(storage.meshes);
|
||||
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
|
||||
|
||||
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
|
||||
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
|
||||
{
|
||||
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
|
||||
SliceMeshStorage& meshStorage = storage.meshes.back();
|
||||
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
|
||||
createLayerParts(meshStorage, slicerList[meshIdx], mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
|
||||
delete slicerList[meshIdx];
|
||||
|
||||
bool has_raft = meshStorage.getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
|
||||
//Add the raft offset to each layer.
|
||||
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = meshStorage.layers[layer_nr];
|
||||
meshStorage.layers[layer_nr].printZ +=
|
||||
meshStorage.getSettingInMicrons("layer_height_0")
|
||||
- initial_slice_z;
|
||||
if (has_raft)
|
||||
{
|
||||
layer.printZ +=
|
||||
meshStorage.getSettingInMicrons("raft_base_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
|
||||
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
|
||||
+ meshStorage.getSettingInMicrons("raft_airgap")
|
||||
- meshStorage.getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
|
||||
if (layer_nr == 0)
|
||||
{
|
||||
layer.printZ += meshStorage.getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
|
||||
{
|
||||
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
|
||||
}
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
|
||||
}
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
|
||||
{
|
||||
size_t total_layers = 0;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
|
||||
}
|
||||
|
||||
//layerparts2HTML(storage, "output/output.html");
|
||||
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
|
||||
{
|
||||
processInsets(storage, layer_number);
|
||||
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
|
||||
}
|
||||
|
||||
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
|
||||
|
||||
if (total_layers < 1)
|
||||
{
|
||||
log("Stopping process because there are no layers.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
|
||||
|
||||
AreaSupport::generateSupportAreas(storage, total_layers);
|
||||
/*
|
||||
if (storage.support.generated)
|
||||
{
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
|
||||
int mesh_max_bottom_layer_count = 0;
|
||||
if (getSettingBoolean("magic_spiralize"))
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
|
||||
}
|
||||
}
|
||||
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
|
||||
{
|
||||
if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
|
||||
{
|
||||
processSkinsAndInfill(storage, layer_number);
|
||||
}
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
|
||||
}
|
||||
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
|
||||
combineInfillLayers(mesh,combined_infill_layers);
|
||||
}
|
||||
|
||||
storage.primeTower.computePrimeTowerMax(storage);
|
||||
storage.primeTower.generatePaths(storage, total_layers);
|
||||
|
||||
processOozeShield(storage, total_layers);
|
||||
|
||||
processDraftShield(storage, total_layers);
|
||||
|
||||
processPlatformAdhesion(storage);
|
||||
|
||||
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
|
||||
{
|
||||
processFuzzyWalls(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
SliceLayer* layer = &mesh.layers[layer_nr];
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
|
||||
{
|
||||
int inset_count = mesh.getSettingAsCount("wall_line_count");
|
||||
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
|
||||
inset_count += 5;
|
||||
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
|
||||
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
|
||||
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
|
||||
inset_count += layer_nr % 2;
|
||||
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
}
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
|
||||
{
|
||||
for (PolygonRef polyline : layer->openPolyLines)
|
||||
{
|
||||
Polygons segments;
|
||||
for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++)
|
||||
{
|
||||
PolygonRef segment = segments.newPoly();
|
||||
segment.add(polyline[point_idx-1]);
|
||||
segment.add(polyline[point_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers)
|
||||
{
|
||||
int n_empty_first_layers = 0;
|
||||
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
|
||||
{
|
||||
bool layer_is_empty = true;
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_idx];
|
||||
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
|
||||
{
|
||||
layer_is_empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (layer_is_empty)
|
||||
{
|
||||
n_empty_first_layers++;
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_empty_first_layers > 0)
|
||||
{
|
||||
log("Removing %d layers because they are empty\n", n_empty_first_layers);
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
std::vector<SliceLayer>& layers = mesh.layers;
|
||||
layers.erase(layers.begin(), layers.begin() + n_empty_first_layers);
|
||||
for (SliceLayer& layer : layers)
|
||||
{
|
||||
layer.printZ -= n_empty_first_layers * layer_height;
|
||||
}
|
||||
}
|
||||
total_layers -= n_empty_first_layers;
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsigned int layer_nr)
|
||||
{
|
||||
for(SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
|
||||
|
||||
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
|
||||
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
|
||||
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
|
||||
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
|
||||
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
|
||||
{
|
||||
int infill_skin_overlap = 0;
|
||||
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
|
||||
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
|
||||
{
|
||||
infill_skin_overlap = skin_extrusion_width / 2;
|
||||
}
|
||||
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
|
||||
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
|
||||
}
|
||||
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
|
||||
{
|
||||
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (!getSettingBoolean("ooze_shield_enabled"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
|
||||
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
|
||||
}
|
||||
|
||||
int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
|
||||
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius);
|
||||
}
|
||||
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
|
||||
for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
|
||||
{
|
||||
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
|
||||
}
|
||||
for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
|
||||
{
|
||||
storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
|
||||
}
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
int draft_shield_height = getSettingInMicrons("draft_shield_height");
|
||||
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
|
||||
int layer_height_0 = getSettingInMicrons("layer_height_0");
|
||||
int layer_height = getSettingInMicrons("layer_height");
|
||||
|
||||
if (draft_shield_height < layer_height_0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
|
||||
|
||||
int layer_skip = 500 / layer_height + 1;
|
||||
|
||||
Polygons& draft_shield = storage.draft_protection_shield;
|
||||
for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip)
|
||||
{
|
||||
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
|
||||
}
|
||||
|
||||
storage.draft_protection_shield = draft_shield.convexHull(draft_shield_dist);
|
||||
}
|
||||
|
||||
void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
|
||||
{
|
||||
switch(getSettingAsPlatformAdhesion("adhesion_type"))
|
||||
{
|
||||
case EPlatformAdhesion::SKIRT:
|
||||
if (getSettingInMicrons("draft_shield_height") == 0)
|
||||
{ // draft screen replaces skirt
|
||||
generateSkirt(storage, getSettingInMicrons("skirt_gap"), getSettingAsCount("skirt_line_count"), getSettingInMicrons("skirt_minimal_length"));
|
||||
}
|
||||
break;
|
||||
case EPlatformAdhesion::BRIM:
|
||||
generateSkirt(storage, 0, getSettingAsCount("brim_line_count"), getSettingInMicrons("skirt_minimal_length"));
|
||||
break;
|
||||
case EPlatformAdhesion::RAFT:
|
||||
generateRaft(storage, getSettingInMicrons("raft_margin"));
|
||||
break;
|
||||
}
|
||||
|
||||
Polygons skirt_sent = storage.skirt[0];
|
||||
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
|
||||
skirt_sent.add(storage.skirt[extruder]);
|
||||
}
|
||||
|
||||
|
||||
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
|
||||
{
|
||||
if (mesh.getSettingAsCount("wall_line_count") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
|
||||
int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
|
||||
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
|
||||
int64_t range_random_point_dist = avg_dist_between_points / 2;
|
||||
for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
|
||||
{
|
||||
SliceLayer& layer = mesh.layers[layer_nr];
|
||||
for (SliceLayerPart& part : layer.parts)
|
||||
{
|
||||
Polygons results;
|
||||
Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0];
|
||||
for (PolygonRef poly : skin)
|
||||
{
|
||||
// generate points in between p0 and p1
|
||||
PolygonRef result = results.newPoly();
|
||||
|
||||
int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
|
||||
Point* p0 = &poly.back();
|
||||
for (Point& p1 : poly)
|
||||
{ // 'a' is the (next) new point between p0 and p1
|
||||
Point p0p1 = p1 - *p0;
|
||||
int64_t p0p1_size = vSize(p0p1);
|
||||
int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
|
||||
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
|
||||
{
|
||||
int r = rand() % (fuzziness * 2) - fuzziness;
|
||||
Point perp_to_p0p1 = turn90CCW(p0p1);
|
||||
Point fuzz = normal(perp_to_p0p1, r);
|
||||
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
|
||||
result.add(pa);
|
||||
dist_last_point = p0pa_dist;
|
||||
}
|
||||
dist_left_over = p0p1_size - dist_last_point;
|
||||
|
||||
p0 = &p1;
|
||||
}
|
||||
while (result.size() < 3 )
|
||||
{
|
||||
unsigned int point_idx = poly.size() - 2;
|
||||
result.add(poly[point_idx]);
|
||||
if (point_idx == 0) { break; }
|
||||
point_idx--;
|
||||
}
|
||||
if (result.size() < 3)
|
||||
{
|
||||
result.clear();
|
||||
for (Point& p : poly)
|
||||
result.add(p);
|
||||
}
|
||||
}
|
||||
skin = results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef FFF_AREA_GENERATOR_H
|
||||
#define FFF_AREA_GENERATOR_H
|
||||
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "settings.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Primary stage in Fused Filament Fabrication processing: Polygons are generated.
|
||||
* The model is sliced and each slice consists of polygons representing the outlines: the boundaries between inside and outside the object.
|
||||
* After slicing, the layers are processed; for example the wall insets are generated, and the areas which are to be filled with support and infill, which are all represented by polygons.
|
||||
* In this stage nothing other than areas and circular paths are generated, which are both represented by polygons.
|
||||
* No infill lines or support pattern etc. is generated.
|
||||
*
|
||||
* The main function of this class is FffPolygonGenerator::generateAreas().
|
||||
*/
|
||||
class FffPolygonGenerator : public SettingsMessenger, NoCopy
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Basic constructor
|
||||
*/
|
||||
FffPolygonGenerator(SettingsBase* settings_)
|
||||
: SettingsMessenger(settings_)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
|
||||
*
|
||||
* \param object The object to slice.
|
||||
* \param timeKeeper Object which keeps track of timings of each stage.
|
||||
* \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline.
|
||||
*/
|
||||
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
|
||||
|
||||
private:
|
||||
|
||||
/*!
|
||||
* Slice the \p object and store the outlines in the \p storage.
|
||||
*
|
||||
* \param object The object to slice.
|
||||
* \param timeKeeper Object which keeps track of timings of each stage.
|
||||
* \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline.
|
||||
*
|
||||
* \return Whether the process succeeded (always true).
|
||||
*/
|
||||
bool sliceModel(MeshGroup* object, TimeKeeper& timeKeeper, SliceDataStorage& storage); /// slices the model
|
||||
|
||||
/*!
|
||||
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, support area polygons, etc.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param timeKeeper Object which keeps track of timings of each stage.
|
||||
*/
|
||||
void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper);
|
||||
|
||||
/*!
|
||||
* Remove all bottom layers which are empty.
|
||||
* \param storage Input and Ouput parameter: stores all layers
|
||||
* \param layer_height The height of each layer
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Generate the inset polygons which form the walls.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param layer_nr The layer for which to generate the insets.
|
||||
*/
|
||||
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Generate the outline of the ooze shield.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void processOozeShield(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
/*!
|
||||
* Generate the skin areas.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param layer_nr The layer for which to generate the skin areas.
|
||||
*/
|
||||
void processSkinsAndInfill(SliceDataStorage& storage, unsigned int layer_nr);
|
||||
|
||||
/*!
|
||||
* Generate the polygons where the draft screen should be.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
|
||||
/*!
|
||||
* Generate the skirt/brim/raft areas/insets.
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
*/
|
||||
void processPlatformAdhesion(SliceDataStorage& storage);
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Make the outer wall 'fuzzy'
|
||||
*/
|
||||
void processFuzzyWalls(SliceMeshStorage& mesh);
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
}//namespace cura
|
||||
#endif // FFF_AREA_GENERATOR_H
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "FffProcessor.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
FffProcessor FffProcessor::instance; // definition must be in cpp
|
||||
|
||||
|
||||
std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
if (first_meshgroup)
|
||||
{
|
||||
sstream << getAllLocalSettingsString(); // global settings
|
||||
sstream << " -g";
|
||||
}
|
||||
else
|
||||
{
|
||||
sstream << " --next";
|
||||
}
|
||||
sstream << meshgroup.getAllLocalSettingsString();
|
||||
for (int extruder_nr = 0; extruder_nr < meshgroup.getExtruderCount(); extruder_nr++)
|
||||
{
|
||||
ExtruderTrain* train = meshgroup.getExtruderTrain(extruder_nr);
|
||||
sstream << " -e" << extruder_nr << train->getAllLocalSettingsString();
|
||||
}
|
||||
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
|
||||
{
|
||||
Mesh& mesh = meshgroup.meshes[mesh_idx];
|
||||
sstream << " -e" << mesh.getSettingAsCount("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
|
||||
}
|
||||
sstream << "\n";
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
bool FffProcessor::processFiles(const std::vector< std::string >& files)
|
||||
{
|
||||
time_keeper.restart();
|
||||
MeshGroup* meshgroup = new MeshGroup(this);
|
||||
|
||||
for(std::string filename : files)
|
||||
{
|
||||
log("Loading %s from disk...\n", filename.c_str());
|
||||
|
||||
FMatrix3x3 matrix;
|
||||
if (!loadMeshIntoMeshGroup(meshgroup, filename.c_str(), matrix))
|
||||
{
|
||||
logError("Failed to load model: %s\n", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
meshgroup->finalize();
|
||||
|
||||
log("Loaded from disk in %5.3fs\n", time_keeper.restart());
|
||||
return processMeshGroup(meshgroup);
|
||||
}
|
||||
|
||||
bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
|
||||
{
|
||||
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, first_meshgroup).c_str()); }
|
||||
time_keeper.restart();
|
||||
if (!meshgroup)
|
||||
return false;
|
||||
|
||||
TimeKeeper time_keeper_total;
|
||||
|
||||
if (meshgroup->meshes.empty())
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (meshgroup->getSettingBoolean("wireframe_enabled"))
|
||||
{
|
||||
log("starting Neith Weaver...\n");
|
||||
|
||||
Weaver w(this);
|
||||
w.weave(meshgroup);
|
||||
|
||||
log("starting Neith Gcode generation...\n");
|
||||
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
|
||||
gcoder.writeGCode();
|
||||
log("finished Neith Gcode generation...\n");
|
||||
|
||||
} else
|
||||
{
|
||||
SliceDataStorage storage(meshgroup);
|
||||
|
||||
if (!polygon_generator.generateAreas(storage, meshgroup, time_keeper))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
|
||||
gcode_writer.writeGCode(storage, time_keeper);
|
||||
}
|
||||
|
||||
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
CommandSocket::getInstance()->sendLayerData();
|
||||
}
|
||||
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
|
||||
|
||||
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
|
||||
first_meshgroup = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef FFF_PROCESSOR_H
|
||||
#define FFF_PROCESSOR_H
|
||||
|
||||
#include "settings.h"
|
||||
#include "FffGcodeWriter.h"
|
||||
#include "FffPolygonGenerator.h"
|
||||
#include "commandSocket.h"
|
||||
#include "Weaver.h"
|
||||
#include "Wireframe2gcode.h"
|
||||
#include "Progress.h"
|
||||
#include "utils/gettime.h"
|
||||
#include "utils/NoCopy.h"
|
||||
|
||||
#define SHOW_ALL_SETTINGS true
|
||||
|
||||
namespace cura {
|
||||
|
||||
//FusedFilamentFabrication processor. Singleton class
|
||||
class FffProcessor : public SettingsBase , NoCopy
|
||||
{
|
||||
private:
|
||||
static FffProcessor instance;
|
||||
|
||||
FffProcessor()
|
||||
: polygon_generator(this)
|
||||
, gcode_writer(this)
|
||||
, first_meshgroup(true)
|
||||
{
|
||||
}
|
||||
public:
|
||||
static FffProcessor* getInstance()
|
||||
{
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
FffPolygonGenerator polygon_generator;
|
||||
FffGcodeWriter gcode_writer;
|
||||
|
||||
bool first_meshgroup;
|
||||
|
||||
std::string profile_string = "";
|
||||
|
||||
std::string getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup);
|
||||
|
||||
public:
|
||||
std::string getProfileString() { return profile_string; }
|
||||
|
||||
TimeKeeper time_keeper; // TODO: use singleton time keeper
|
||||
|
||||
void resetFileNumber()
|
||||
{
|
||||
gcode_writer.resetFileNumber();
|
||||
}
|
||||
|
||||
bool setTargetFile(const char* filename)
|
||||
{
|
||||
return gcode_writer.setTargetFile(filename);
|
||||
}
|
||||
|
||||
void setTargetStream(std::ostream* stream)
|
||||
{
|
||||
return gcode_writer.setTargetStream(stream);
|
||||
}
|
||||
|
||||
double getTotalFilamentUsed(int e)
|
||||
{
|
||||
return gcode_writer.getTotalFilamentUsed(e);
|
||||
}
|
||||
|
||||
double getTotalPrintTime()
|
||||
{
|
||||
return gcode_writer.getTotalPrintTime();
|
||||
}
|
||||
|
||||
void finalize()
|
||||
{
|
||||
gcode_writer.finalize();
|
||||
}
|
||||
|
||||
bool processFiles(const std::vector<std::string> &files);
|
||||
|
||||
bool processMeshGroup(MeshGroup* meshgroup);
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//FFF_PROCESSOR_H
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef FLOW_TEMP_GRAPH
|
||||
#define FLOW_TEMP_GRAPH
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Class representing a graph matching a flow to a temperature.
|
||||
* The graph generally consists of several linear line segments between points at which the temperature and flow are matched.
|
||||
*/
|
||||
class FlowTempGraph
|
||||
{
|
||||
public:
|
||||
struct Datum
|
||||
{
|
||||
double flow; //!< The flow in mm^3/s
|
||||
double temp; //!< The temperature in *C
|
||||
Datum(double flow, double temp)
|
||||
: flow(flow)
|
||||
, temp(temp)
|
||||
{}
|
||||
};
|
||||
std::vector<Datum> data; //!< The points of the graph between which the graph is linearly interpolated
|
||||
|
||||
FlowTempGraph()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* Get the temperature corresponding to a specific flow.
|
||||
*
|
||||
* For flows outside of the chart, the temperature at the minimal or maximal flow is returned.
|
||||
* When the graph is empty, the @p material_print_temperature is returned.
|
||||
*
|
||||
* \param flow the flow in mm^3/s
|
||||
* \param material_print_temperature The default printing temp (backward compatibility for when the graph fails)
|
||||
* \return the corresponding temp
|
||||
*/
|
||||
double getTemp(double flow, double material_print_temperature, bool flow_dependent_temperature)
|
||||
{
|
||||
if (!flow_dependent_temperature || data.size() == 0)
|
||||
{
|
||||
return material_print_temperature;
|
||||
}
|
||||
if (data.size() == 1)
|
||||
{
|
||||
return data.front().temp;
|
||||
}
|
||||
if (flow < data.front().flow)
|
||||
{
|
||||
logWarning("Warning! Flow too low!\n"); // TODO
|
||||
return data.front().temp;
|
||||
}
|
||||
Datum* last_datum = &data.front();
|
||||
for (unsigned int datum_idx = 1; datum_idx < data.size(); datum_idx++)
|
||||
{
|
||||
Datum& datum = data[datum_idx];
|
||||
if (datum.flow >= flow)
|
||||
{
|
||||
return last_datum->temp + (datum.temp - last_datum->temp) * (flow - last_datum->flow) / (datum.flow - last_datum->flow);
|
||||
}
|
||||
last_datum = &datum;
|
||||
}
|
||||
|
||||
logWarning("Warning! Flow too high!\n"); // TODO
|
||||
return data.back().temp;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // FLOW_TEMP_GRAPH
|
||||
@@ -0,0 +1,223 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
|
||||
#include "LayerPlanBuffer.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "utils/logoutput.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
|
||||
void LayerPlanBuffer::flush()
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
insertPreheatCommands(); // insert preheat commands of the very last layer
|
||||
}
|
||||
while (!buffer.empty())
|
||||
{
|
||||
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
buffer.pop_front();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
|
||||
{
|
||||
double acc_time = 0.0;
|
||||
for (unsigned int path_idx = 0; path_idx < extruder_plan_before.paths.size(); path_idx++)
|
||||
{
|
||||
GCodePath& path = extruder_plan_before.paths[path_idx];
|
||||
acc_time += path.estimates.getTotalTime();
|
||||
if (acc_time > time_after_extruder_plan_start)
|
||||
{
|
||||
// logError("Inserting %f\t seconds too early!\n", acc_time - time_after_extruder_plan_start);
|
||||
extruder_plan_before.insertCommand(path_idx, extruder, temp, false, acc_time - time_after_extruder_plan_start);
|
||||
return;
|
||||
}
|
||||
}
|
||||
extruder_plan_before.insertCommand(extruder_plan_before.paths.size(), extruder, temp, false); // insert at end of extruder plan if time_after_extruder_plan_start > extruder_plan.time
|
||||
// = special insert after all extruder plans
|
||||
}
|
||||
|
||||
double LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
|
||||
int extruder = extruder_plan.extruder;
|
||||
double required_temp = extruder_plan.required_temp;
|
||||
|
||||
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
|
||||
bool first_it = true;
|
||||
double in_between_time = 0.0;
|
||||
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
|
||||
{
|
||||
GCodePlanner& layer = *layers[layer_idx];
|
||||
if (!first_it)
|
||||
{
|
||||
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
|
||||
}
|
||||
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
|
||||
if (extruder_plan.extruder == extruder)
|
||||
{
|
||||
return preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
|
||||
}
|
||||
in_between_time += extruder_plan.estimates.getTotalTime();
|
||||
}
|
||||
first_it = false;
|
||||
}
|
||||
// The last extruder plan with the same extruder falls outside of the buffer
|
||||
// assume the nozzle has cooled down to strandby temperature already.
|
||||
return preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(preheat_config.getStandbyTemp(extruder), extruder, required_temp, false);
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
|
||||
{
|
||||
// time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
|
||||
double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
|
||||
double time_after_extruder_plan_start = prev_extruder_plan.estimates.getTotalTime() - time_before_extruder_plan_end;
|
||||
if (time_after_extruder_plan_start < 0)
|
||||
{
|
||||
time_after_extruder_plan_start = 0; // don't override the extruder plan with same extruder of the previous layer
|
||||
}
|
||||
|
||||
insertPreheatCommand(prev_extruder_plan, time_after_extruder_plan_start, extruder, required_temp);
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
|
||||
int extruder = extruder_plan.extruder;
|
||||
double required_temp = extruder_plan.required_temp;
|
||||
|
||||
extruder_plan.insertCommand(0, extruder, required_temp, true); // just after the extruder switch, wait for the destination temperature to be reached
|
||||
|
||||
double time_before_extruder_plan_to_insert = timeBeforeExtruderPlanToInsert(layers, layer_plan_idx, extruder_plan_idx);
|
||||
|
||||
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
|
||||
bool first_it = true; // Whether it's the first iteration of the for loop below
|
||||
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
|
||||
{
|
||||
GCodePlanner& layer = *layers[layer_idx];
|
||||
if (!first_it)
|
||||
{
|
||||
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
|
||||
}
|
||||
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
|
||||
{
|
||||
ExtruderPlan& extruder_plan_before = layer.extruder_plans[extruder_plan_before_idx];
|
||||
assert (extruder_plan_before.extruder != extruder);
|
||||
|
||||
double time_here = extruder_plan_before.estimates.getTotalTime();
|
||||
if (time_here > time_before_extruder_plan_to_insert)
|
||||
{
|
||||
insertPreheatCommand(extruder_plan_before, time_here - time_before_extruder_plan_to_insert, extruder, required_temp);
|
||||
return;
|
||||
}
|
||||
time_before_extruder_plan_to_insert -= time_here;
|
||||
|
||||
}
|
||||
first_it = false;
|
||||
}
|
||||
|
||||
// time_before_extruder_plan_to_insert falls before all plans in the buffer
|
||||
ExtruderPlan& first_extruder_plan = layers[0]->extruder_plans[0];
|
||||
first_extruder_plan.insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
|
||||
int extruder = extruder_plan.extruder;
|
||||
double required_temp = extruder_plan.required_temp;
|
||||
|
||||
|
||||
ExtruderPlan* prev_extruder_plan = nullptr;
|
||||
if (extruder_plan_idx == 0)
|
||||
{
|
||||
if (layer_plan_idx == 0)
|
||||
{ // the very first extruder plan
|
||||
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
|
||||
{ // set temperature of the first nozzle, turn other nozzles down
|
||||
if (extruder_idx == extruder)
|
||||
{
|
||||
// extruder_plan.insertCommand(0, extruder, required_temp, true);
|
||||
// the first used extruder should already be set to the required temp in the start gcode
|
||||
}
|
||||
else
|
||||
{
|
||||
extruder_plan.insertCommand(0, extruder_idx, preheat_config.getStandbyTemp(extruder_idx), false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev_extruder_plan = &layers[layer_plan_idx - 1]->extruder_plans.back();
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_extruder_plan = &layers[layer_plan_idx]->extruder_plans[extruder_plan_idx - 1];
|
||||
}
|
||||
assert(prev_extruder_plan != nullptr);
|
||||
|
||||
int prev_extruder = prev_extruder_plan->extruder;
|
||||
|
||||
if (prev_extruder != extruder)
|
||||
{ // set previous extruder to standby temperature
|
||||
prev_extruder_plan->insertCommand(prev_extruder_plan->paths.size(), prev_extruder, preheat_config.getStandbyTemp(prev_extruder), false);
|
||||
}
|
||||
|
||||
if (prev_extruder == extruder)
|
||||
{
|
||||
if (preheat_config.usesFlowDependentTemp(extruder))
|
||||
{
|
||||
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPreheatCommand_multiExtrusion(layers, layer_plan_idx, extruder_plan_idx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LayerPlanBuffer::insertPreheatCommands()
|
||||
{
|
||||
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
|
||||
{ // disregard empty layer
|
||||
buffer.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<GCodePlanner*> layers;
|
||||
layers.reserve(buffer.size());
|
||||
for (GCodePlanner& layer_plan : buffer)
|
||||
{
|
||||
layers.push_back(&layer_plan);
|
||||
}
|
||||
|
||||
unsigned int layer_idx = layers.size() - 1;
|
||||
|
||||
// insert commands for all extruder plans on this layer
|
||||
GCodePlanner& layer_plan = *layers[layer_idx];
|
||||
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
|
||||
{
|
||||
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
|
||||
double time = extruder_plan.estimates.getTotalUnretractedTime();
|
||||
if (time <= 0.0
|
||||
|| extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
double avg_flow = extruder_plan.estimates.getMaterial() / time; // TODO: subtract retracted travel time
|
||||
extruder_plan.required_temp = preheat_config.getTemp(extruder_plan.extruder, avg_flow);
|
||||
|
||||
insertPreheatCommand(layers, layer_idx, extruder_plan_idx);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cura
|
||||
@@ -0,0 +1,131 @@
|
||||
#ifndef LAYER_PLAN_BUFFER_H
|
||||
#define LAYER_PLAN_BUFFER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "settings.h"
|
||||
#include "commandSocket.h"
|
||||
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "MeshGroup.h"
|
||||
|
||||
#include "Preheat.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class LayerPlanBuffer : SettingsMessenger
|
||||
{
|
||||
GCodeExport& gcode;
|
||||
|
||||
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
|
||||
|
||||
static constexpr unsigned int buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value
|
||||
// this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted.
|
||||
|
||||
public:
|
||||
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
|
||||
|
||||
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
|
||||
: SettingsMessenger(settings)
|
||||
, gcode(gcode)
|
||||
{ }
|
||||
|
||||
void setPreheatConfig(MeshGroup& settings)
|
||||
{
|
||||
preheat_config.setConfig(settings);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Place a new layer plan (GcodePlanner) by constructing it with the given arguments.
|
||||
* Pop back the oldest layer plan is it exceeds the buffer size and write it to gcode.
|
||||
*/
|
||||
template<typename... Args>
|
||||
GCodePlanner& emplace_back(Args&&... constructor_args)
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
insertPreheatCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
|
||||
}
|
||||
buffer.emplace_back(constructor_args...);
|
||||
if (buffer.size() > buffer_size)
|
||||
{
|
||||
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->flushGcode();
|
||||
buffer.pop_front();
|
||||
}
|
||||
return buffer.back();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Write all remaining layer plans (GCodePlanner) to gcode and empty the buffer.
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/*!
|
||||
* Insert the preheat command for @p extruder into @p extruder_plan_before
|
||||
*
|
||||
* \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command
|
||||
* \param time_after_extruder_plan_start The time after the start of the extruder plan, before which to insert the preheat command
|
||||
* \param extruder The extruder for which to set the temperature
|
||||
* \param temp The temperature of the preheat command
|
||||
*/
|
||||
void insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp);
|
||||
|
||||
/*!
|
||||
* Compute the time needed to preheat, based either on the time the extruder has been on standby
|
||||
* or based on the temp of the previous extruder plan which has the same extruder nr.
|
||||
*
|
||||
* \param layers The layers in the buffer, moved to a vector
|
||||
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
|
||||
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
|
||||
* \return the time needed to preheat
|
||||
*/
|
||||
double timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
|
||||
|
||||
/*!
|
||||
* For two consecutive extruder plans of the same extruder (so on different layers),
|
||||
* preheat the extruder to the temperature corresponding to the average flow of the second extruder plan.
|
||||
*
|
||||
* The preheat commands are inserted such that the middle of the temperature change coincides with the start of the next layer.
|
||||
*
|
||||
* \param prev_extruder_plan The former extruder plan (of the former layer)
|
||||
* \param extruder The extruder for which too set the temperature
|
||||
* \param required_temp The required temperature for the second extruder plan
|
||||
*/
|
||||
void insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp);
|
||||
|
||||
/*!
|
||||
* Insert the preheat command for an extruder plan which is preceded by an extruder plan with a different extruder.
|
||||
* Find the time window in which this extruder hasn't been used
|
||||
* and compute at what time the preheat command needs to be inserted.
|
||||
* Then insert the preheat command in the right extruder plan.
|
||||
*
|
||||
* \param layers The layers in the buffer, moved to a vector
|
||||
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
|
||||
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
|
||||
*/
|
||||
void insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
|
||||
|
||||
/*!
|
||||
* Insert the preheat command for the extruder plan corersponding to @p extruder_plan_idx of the layer corresponding to @p layer_plan_idx.
|
||||
*
|
||||
* \param layers The layers of the buffer, moved to a temporary vector (from lower to upper layers)
|
||||
* \param layer_plan_idx The index of the layer plan for which to generate a preheat command
|
||||
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to generate the preheat command
|
||||
*/
|
||||
void insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
|
||||
|
||||
/*!
|
||||
* Insert the preheat commands for the last added layer (unless that layer was empty)
|
||||
*/
|
||||
void insertPreheatCommands();
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // LAYER_PLAN_BUFFER_H
|
||||
@@ -0,0 +1,230 @@
|
||||
#include "MergeInfillLines.h"
|
||||
|
||||
#include <algorithm> // min
|
||||
|
||||
#include "utils/linearAlg2D.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath& last_path, int64_t new_line_width)
|
||||
{
|
||||
double old_line_width = INT2MM(last_path.config->getLineWidth());
|
||||
double new_line_width_mm = INT2MM(new_line_width);
|
||||
double speed_mod = old_line_width / new_line_width_mm;
|
||||
double extrusion_mod = new_line_width_mm / old_line_width;
|
||||
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
|
||||
sendPolygon(last_path.config->type, gcode.getPositionXY(), to, last_path.getLineWidth());
|
||||
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
|
||||
}
|
||||
|
||||
bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
|
||||
{ //Check for lots of small moves and combine them into one large line
|
||||
Point prev_middle;
|
||||
Point last_middle;
|
||||
int64_t line_width;
|
||||
|
||||
if (isConvertible(path_idx, prev_middle, last_middle, line_width, false))
|
||||
{
|
||||
// path_idx + 3 is the index of the second extrusion move to be converted in combination with the first
|
||||
{
|
||||
GCodePath& move_path = paths[path_idx];
|
||||
for(unsigned int point_idx = 0; point_idx < move_path.points.size() - 1; point_idx++)
|
||||
{
|
||||
gcode.writeMove(move_path.points[point_idx], speed, move_path.getExtrusionMM3perMM());
|
||||
}
|
||||
gcode.writeMove(prev_middle, travelConfig.getSpeed(), 0);
|
||||
GCodePath& last_path = paths[path_idx + 3];
|
||||
|
||||
writeCompensatedMove(last_middle, speed, last_path, line_width);
|
||||
}
|
||||
|
||||
path_idx += 2;
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
for (; isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
|
||||
{
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
GCodePath& last_path = paths[path_idx + 3];
|
||||
writeCompensatedMove(last_middle, speed, last_path, line_width);
|
||||
}
|
||||
path_idx = path_idx + 1; // means that the next path considered is the travel path after the converted extrusion path corresponding to the updated path_idx
|
||||
extruder_plan.handleInserts(path_idx, gcode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
|
||||
{
|
||||
unsigned int idx = path_idx_first_move;
|
||||
if (idx + 3 > paths.size()-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ( paths[idx+0].config != &travelConfig // must be travel
|
||||
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|
||||
|| paths[idx+1].config == &travelConfig // must be extrusion
|
||||
// || paths[idx+2].points.size() > 1 // travel must be direct
|
||||
|| paths[idx+2].config != &travelConfig // must be travel
|
||||
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|
||||
|| paths[idx+3].config == &travelConfig // must be extrusion
|
||||
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
|
||||
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
|
||||
{ // both extrusion moves must be of lines space filling type!
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t line_width = paths[idx+1].config->getLineWidth();
|
||||
|
||||
Point& a = paths[idx+0].points.back(); // first extruded line from
|
||||
Point& b = paths[idx+1].points.back(); // first extruded line to
|
||||
Point& c = paths[idx+2].points.back(); // second extruded line from
|
||||
Point& d = paths[idx+3].points.back(); // second extruded line to
|
||||
|
||||
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
|
||||
}
|
||||
|
||||
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
|
||||
{
|
||||
use_second_middle_as_first = false;
|
||||
int64_t max_line_width = nozzle_size * 3 / 2;
|
||||
|
||||
Point ab = b - a;
|
||||
Point cd = d - c;
|
||||
|
||||
if (b == c)
|
||||
{
|
||||
return false; // the line segments are connected!
|
||||
}
|
||||
|
||||
int64_t ab_size = vSize(ab);
|
||||
int64_t cd_size = vSize(cd);
|
||||
|
||||
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
|
||||
{
|
||||
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
|
||||
}
|
||||
|
||||
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
|
||||
int64_t prod = dot(ab,cd);
|
||||
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
|
||||
{
|
||||
return false; // extrusion moves not in the same or opposite diraction
|
||||
}
|
||||
|
||||
// make lines in the same direction by flipping one
|
||||
if (prod < 0)
|
||||
{
|
||||
ab = ab * -1;
|
||||
}
|
||||
else if (prod == 0)
|
||||
{
|
||||
return false; // lines are orthogonal!
|
||||
}
|
||||
else if (b == d || a == c)
|
||||
{
|
||||
return false; // the line segments are connected!
|
||||
}
|
||||
|
||||
first_middle = (use_second_middle_as_first)?
|
||||
second_middle :
|
||||
(a + b) / 2;
|
||||
second_middle = (c + d) / 2;
|
||||
|
||||
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
|
||||
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
|
||||
if (dir_vector_perp_length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (dir_vector_perp_length > 5 * nozzle_size)
|
||||
{
|
||||
return false; // infill lines too far apart
|
||||
}
|
||||
|
||||
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
|
||||
|
||||
// compute the resulting line width
|
||||
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
|
||||
if (resulting_line_width > max_line_width)
|
||||
{
|
||||
return false; // combined lines would be too wide
|
||||
}
|
||||
if (resulting_line_width == 0)
|
||||
{
|
||||
return false; // dot is zero, so lines are in each others extension, not next to eachother
|
||||
}
|
||||
|
||||
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
|
||||
Point ac = c - first_middle;
|
||||
Point infill_vector_perp = turn90CCW(infill_vector);
|
||||
int64_t perp_proj = dot(ac, infill_vector_perp);
|
||||
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
|
||||
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
|
||||
{
|
||||
return false; // lines are too far apart or too close together
|
||||
}
|
||||
|
||||
// check whether the two line segments are adjacent.
|
||||
// full infill in a narrow area might result in line segments with arbitrary distance between them
|
||||
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
|
||||
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
|
||||
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
void MergeInfillLines::merge(Point& from, Point& p0, Point& p1)
|
||||
{ //Check for lots of small moves and combine them into one large line
|
||||
if (path->points.size() == 1 && path->config != &travelConfig); // && shorterThen(from - path->points[0], path->config->getLineWidth() * 2))
|
||||
{
|
||||
Point p0 = path->points[0];
|
||||
unsigned int path_idx_last = path_idx + 1; // index of the last short move
|
||||
while(path_idx_last < paths.size() && paths[path_idx_last].points.size() == 1 && shorterThen(p0 - paths[path_idx_last].points[0], path->config->getLineWidth() * 2))
|
||||
{
|
||||
p0 = paths[path_idx_last].points[0];
|
||||
path_idx_last ++;
|
||||
}
|
||||
if (paths[path_idx_last-1].config == &travelConfig)
|
||||
path_idx_last --;
|
||||
|
||||
if (path_idx_last > path_idx + 2)
|
||||
{
|
||||
p0 = from;
|
||||
for(unsigned int path_idx_short = path_idx; path_idx_short < path_idx_last-1; path_idx_short+=2)
|
||||
{
|
||||
int64_t oldLen = vSize(p0 - paths[path_idx_short].points[0]);
|
||||
Point newPoint = (paths[path_idx_short].points[0] + paths[path_idx_short+1].points[0]) / 2;
|
||||
int64_t newLen = vSize(from - newPoint);
|
||||
if (newLen > 0)
|
||||
{
|
||||
if (oldLen > 0)
|
||||
gcode.writeMove(newPoint, speed * oldLen / newLen, path->getExtrusionMM3perMM() * newLen / oldLen);
|
||||
else
|
||||
gcode.writeMove(newPoint, speed, path->getExtrusionMM3perMM());
|
||||
}
|
||||
}
|
||||
gcode.writeMove(paths[path_idx_last-1].points[0], speed, path->getExtrusionMM3perMM());
|
||||
path_idx = path_idx_last - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef MERGE_INFILL_LINES_H
|
||||
#define MERGE_INFILL_LINES_H
|
||||
|
||||
#include "utils/intpoint.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
class MergeInfillLines
|
||||
{
|
||||
// void merge(Point& from, Point& p0, Point& p1);
|
||||
GCodeExport& gcode; //!< Where to write the combined line to
|
||||
int layer_nr; //!< The current layer number
|
||||
std::vector<GCodePath>& paths; //!< The paths currently under consideration
|
||||
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
|
||||
|
||||
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
|
||||
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
|
||||
|
||||
/*!
|
||||
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
|
||||
* \param first_middle Output parameter: the middle of the first extrusion move
|
||||
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
|
||||
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
|
||||
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
*/
|
||||
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
|
||||
|
||||
/*!
|
||||
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
|
||||
*
|
||||
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
|
||||
*
|
||||
* \param a first from
|
||||
* \param b first to
|
||||
* \param c second from
|
||||
* \param d second to
|
||||
* \param line_width The line width of the moves
|
||||
* \param first_middle Output parameter: the middle of the first extrusion move
|
||||
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
|
||||
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
|
||||
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
|
||||
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
|
||||
*/
|
||||
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
|
||||
|
||||
/*!
|
||||
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
|
||||
*
|
||||
* \param to The point to move to
|
||||
* \param speed The original speed
|
||||
* \param old_path The original path
|
||||
* \param new_line_width The width of the convewrted line (approximately the length of the original line)
|
||||
*/
|
||||
void writeCompensatedMove(Point& to, double speed, GCodePath& old_path, int64_t new_line_width);
|
||||
public:
|
||||
/*!
|
||||
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
|
||||
*/
|
||||
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
|
||||
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
|
||||
|
||||
/*!
|
||||
* Check for lots of small moves and combine them into one large line.
|
||||
* Updates \p path_idx to the next path which is not combined.
|
||||
*
|
||||
* \param gcode Where to write the combined line to
|
||||
* \param paths The paths currently under consideration
|
||||
* \param travelConfig The travel settings used to see whether a path is a travel path or an extrusion path
|
||||
* \param nozzle_size The diameter of the hole in the nozzle
|
||||
* \param speed A factor used to scale the movement speed
|
||||
* \param path_idx Input/Output parameter: The current index in \p paths where to start combining and the current index after combining as output parameter.
|
||||
* \return Whether lines have been merged and normal path-to-gcode generation can be skipped for the current resulting \p path_idx .
|
||||
*/
|
||||
bool mergeInfillLines(double speed, unsigned int& path_idx);
|
||||
|
||||
/*!
|
||||
* send a polygon through the command socket from the previous point to the given point
|
||||
*/
|
||||
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
|
||||
{
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
// we should send this travel as a non-retraction move
|
||||
cura::Polygons pathPoly;
|
||||
PolygonRef path = pathPoly.newPoly();
|
||||
path.add(from);
|
||||
path.add(to);
|
||||
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
#endif // MERGE_INFILL_LINES_H
|
||||
@@ -0,0 +1,186 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/string.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
FILE* binaryMeshBlob = nullptr;
|
||||
|
||||
/* Custom fgets function to support Mac line-ends in Ascii STL files. OpenSCAD produces this when used on Mac */
|
||||
void* fgets_(char* ptr, size_t len, FILE* f)
|
||||
{
|
||||
while(len && fread(ptr, 1, 1, f) > 0)
|
||||
{
|
||||
if (*ptr == '\n' || *ptr == '\r')
|
||||
{
|
||||
*ptr = '\0';
|
||||
return ptr;
|
||||
}
|
||||
ptr++;
|
||||
len--;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rt");
|
||||
char buffer[1024];
|
||||
FPoint3 vertex;
|
||||
int n = 0;
|
||||
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
|
||||
while(fgets_(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (sscanf(buffer, " vertex %f %f %f", &vertex.x, &vertex.y, &vertex.z) == 3)
|
||||
{
|
||||
n++;
|
||||
switch(n)
|
||||
{
|
||||
case 1:
|
||||
v0 = matrix.apply(vertex);
|
||||
break;
|
||||
case 2:
|
||||
v1 = matrix.apply(vertex);
|
||||
break;
|
||||
case 3:
|
||||
v2 = matrix.apply(vertex);
|
||||
mesh->addFace(v0, v1, v2);
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
mesh->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "rb");
|
||||
|
||||
fseek(f, 0L, SEEK_END);
|
||||
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
|
||||
rewind(f); //Seek back to start.
|
||||
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
|
||||
|
||||
char buffer[80];
|
||||
//Skip the header
|
||||
if (fread(buffer, 80, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t reported_face_count;
|
||||
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
|
||||
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
if (reported_face_count != face_count)
|
||||
{
|
||||
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
|
||||
}
|
||||
|
||||
//For each face read:
|
||||
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
|
||||
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
|
||||
mesh->faces.reserve(face_count);
|
||||
mesh->vertices.reserve(face_count);
|
||||
for (unsigned int i = 0; i < face_count; i++)
|
||||
{
|
||||
if (fread(buffer, 50, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
float *v= ((float*)buffer)+3;
|
||||
|
||||
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
|
||||
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
|
||||
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
|
||||
mesh->addFace(v0, v1, v2);
|
||||
}
|
||||
fclose(f);
|
||||
mesh->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
|
||||
{
|
||||
FILE* f = fopen(filename, "r");
|
||||
if (f == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Skip any whitespace at the beginning of the file.
|
||||
unsigned long long num_whitespace = 0; //Number of whitespace characters.
|
||||
unsigned char whitespace;
|
||||
if (fread(&whitespace, 1, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
while(isspace(whitespace))
|
||||
{
|
||||
num_whitespace++;
|
||||
if (fread(&whitespace, 1, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
|
||||
|
||||
char buffer[6];
|
||||
if (fread(buffer, 5, 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
buffer[5] = '\0';
|
||||
if (stringcasecompare(buffer, "solid") == 0)
|
||||
{
|
||||
bool load_success = loadMeshSTL_ascii(mesh, filename, matrix);
|
||||
if (!load_success)
|
||||
return false;
|
||||
|
||||
// This logic is used to handle the case where the file starts with
|
||||
// "solid" but is a binary file.
|
||||
if (mesh->faces.size() < 1)
|
||||
{
|
||||
mesh->clear();
|
||||
return loadMeshSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return loadMeshSTL_binary(mesh, filename, matrix);
|
||||
}
|
||||
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
|
||||
{
|
||||
const char* ext = strrchr(filename, '.');
|
||||
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
|
||||
{
|
||||
Mesh mesh = object_parent_settings ? Mesh(object_parent_settings) : Mesh(meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
|
||||
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
|
||||
{
|
||||
meshgroup->meshes.push_back(mesh);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,147 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef MESH_GROUP_H
|
||||
#define MESH_GROUP_H
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "mesh.h"
|
||||
#include "ExtruderTrain.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* A MeshGroup is a collection with 1 or more 3D meshes.
|
||||
*
|
||||
* One MeshGroup is a whole which is printed at once.
|
||||
* Generally there is one single MeshGroup, though when using one-at-a-time printing, multiple MeshGroups are processed consecutively.
|
||||
*/
|
||||
class MeshGroup : public SettingsBase, NoCopy
|
||||
{
|
||||
ExtruderTrain* extruders[MAX_EXTRUDERS] = {nullptr};
|
||||
int extruder_count;
|
||||
public:
|
||||
int getExtruderCount()
|
||||
{
|
||||
if (extruder_count == -1)
|
||||
{
|
||||
extruder_count = getSettingAsCount("machine_extruder_count");
|
||||
}
|
||||
return extruder_count;
|
||||
}
|
||||
|
||||
MeshGroup(SettingsBaseVirtual* settings_base)
|
||||
: SettingsBase(settings_base)
|
||||
, extruder_count(-1)
|
||||
{}
|
||||
|
||||
~MeshGroup()
|
||||
{
|
||||
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
|
||||
{
|
||||
if (extruders[extruder])
|
||||
{
|
||||
delete extruders[extruder];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a new extruder train for the @p extruder_nr, or return the one which already exists.
|
||||
*/
|
||||
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr)
|
||||
{
|
||||
if (!extruders[extruder_nr])
|
||||
{
|
||||
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
|
||||
}
|
||||
return extruders[extruder_nr];
|
||||
}
|
||||
|
||||
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
|
||||
{
|
||||
assert(extruders[extruder_nr]);
|
||||
return extruders[extruder_nr];
|
||||
}
|
||||
|
||||
std::vector<Mesh> meshes;
|
||||
|
||||
Point3 min() //! minimal corner of bounding box
|
||||
{
|
||||
if (meshes.size() < 1)
|
||||
{
|
||||
return Point3(0, 0, 0);
|
||||
}
|
||||
Point3 ret = meshes[0].min();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
{
|
||||
Point3 v = meshes[i].min();
|
||||
ret.x = std::min(ret.x, v.x);
|
||||
ret.y = std::min(ret.y, v.y);
|
||||
ret.z = std::min(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
Point3 max() //! maximal corner of bounding box
|
||||
{
|
||||
if (meshes.size() < 1)
|
||||
{
|
||||
return Point3(0, 0, 0);
|
||||
}
|
||||
Point3 ret = meshes[0].max();
|
||||
for(unsigned int i=1; i<meshes.size(); i++)
|
||||
{
|
||||
Point3 v = meshes[i].max();
|
||||
ret.x = std::max(ret.x, v.x);
|
||||
ret.y = std::max(ret.y, v.y);
|
||||
ret.z = std::max(ret.z, v.z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for(Mesh& m : meshes)
|
||||
{
|
||||
m.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void finalize()
|
||||
{
|
||||
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
|
||||
Point3 meshgroup_offset(0, 0, 0);
|
||||
if (!getSettingBoolean("machine_center_is_zero"))
|
||||
{
|
||||
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
|
||||
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
|
||||
}
|
||||
|
||||
// If a mesh position was given, put the mesh at this position in 3D space.
|
||||
for(Mesh& mesh : meshes)
|
||||
{
|
||||
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
|
||||
if (mesh.getSettingBoolean("center_object"))
|
||||
{
|
||||
Point3 object_min = mesh.min();
|
||||
Point3 object_max = mesh.max();
|
||||
Point3 object_size = object_max - object_min;
|
||||
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
|
||||
}
|
||||
mesh.offset(mesh_offset + meshgroup_offset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Load a Mesh from file and store it in the \p meshgroup.
|
||||
*
|
||||
* \param meshgroup The meshgroup where to store the mesh
|
||||
* \param filename The filename of the mesh file
|
||||
* \param transformation The transformation applied to all vertices
|
||||
* \param object_parent_settings (optional) The parent settings object of the new mesh. Defaults to \p meshgroup if none is given.
|
||||
* \return whether the file could be loaded
|
||||
*/
|
||||
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
|
||||
|
||||
}//namespace cura
|
||||
#endif//MESH_GROUP_H
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
#ifndef PREHEAT_H
|
||||
#define PREHEAT_H
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm> // max
|
||||
|
||||
#include "utils/logoutput.h"
|
||||
#include "MeshGroup.h"
|
||||
|
||||
#include "FlowTempGraph.h"
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature.
|
||||
*/
|
||||
class Preheat
|
||||
{
|
||||
/*!
|
||||
* The nozzle and material temperature settings for an extruder train.
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
double time_to_heatup_1_degree; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature)
|
||||
double time_to_cooldown_1_degree; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature)
|
||||
|
||||
double heatup_cooldown_time_mod_while_printing; //!< The time to be added to Preheat::time_to_heatup_1_degree and subtracted from Preheat::time_to_cooldown_1_degree to get the timings while printing
|
||||
|
||||
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
|
||||
|
||||
double material_print_temperature; //!< default print temp (backward compatilibily)
|
||||
|
||||
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
|
||||
|
||||
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
|
||||
};
|
||||
|
||||
std::vector<Config> config_per_extruder;//!< the nozzle and material temperature settings for each extruder train.
|
||||
public:
|
||||
/*!
|
||||
* Get the standby temperature of an extruder train
|
||||
* \param extruder the extruder train for which to get the standby tmep
|
||||
* \return the standby temp
|
||||
*/
|
||||
double getStandbyTemp(int extruder)
|
||||
{
|
||||
return config_per_extruder[extruder].standby_temp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the nozzle and material temperature settings for each extruder train.
|
||||
*/
|
||||
void setConfig(MeshGroup& settings)
|
||||
{
|
||||
for (int extruder_nr = 0; extruder_nr < settings.getExtruderCount(); extruder_nr++)
|
||||
{
|
||||
assert(settings.getExtruderTrain(extruder_nr) != nullptr);
|
||||
ExtruderTrain& extruder_train = *settings.getExtruderTrain(extruder_nr);
|
||||
config_per_extruder.emplace_back();
|
||||
Config& config = config_per_extruder.back();
|
||||
config.time_to_cooldown_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed"); // 0.5
|
||||
config.time_to_heatup_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed"); // 0.5
|
||||
config.heatup_cooldown_time_mod_while_printing = 1.0 / extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed"); // 0.1
|
||||
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature"); // 150
|
||||
|
||||
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature"); // 220
|
||||
|
||||
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
|
||||
|
||||
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
|
||||
}
|
||||
}
|
||||
|
||||
bool usesFlowDependentTemp(int extruder_nr)
|
||||
{
|
||||
return config_per_extruder[extruder_nr].flow_dependent_temperature;
|
||||
}
|
||||
private:
|
||||
/*!
|
||||
* Calculate time to heat up from standby temperature to a given temperature.
|
||||
* Assumes @p temp is higher than the standby temperature.
|
||||
*
|
||||
* \param extruder The extruder for which to get the time
|
||||
* \param temp The temperature to be reached
|
||||
*/
|
||||
double timeToHeatFromStandbyToPrintTemp(unsigned int extruder, double temp)
|
||||
{
|
||||
return (temp - config_per_extruder[extruder].standby_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Get the optimal temperature corresponding to a given average flow.
|
||||
* \param extruder The extruder train
|
||||
* \param flow The flow for which to get the optimal temperature
|
||||
* \return The corresponding optimal temperature
|
||||
*/
|
||||
double getTemp(unsigned int extruder, double flow)
|
||||
{
|
||||
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Decide when to start warming up again after starting to cool down towards the standby temperature.
|
||||
* Two cases are considered:
|
||||
* the case where the standby temperature is reached \__/ .
|
||||
* and the case where it isn't \/ .
|
||||
*
|
||||
* IT is assumed that the printer is not printing during this cool down and warm up time.
|
||||
*
|
||||
* Assumes from_temp is approximately the same as @p temp
|
||||
*
|
||||
* \param window_time The time window within which the cooldown and heat up must take place.
|
||||
* \param extruder The extruder used
|
||||
* \param temp The temperature to which to heat
|
||||
* \return The time before the end of the @p time_window to insert the preheat command
|
||||
*/
|
||||
double timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
|
||||
{
|
||||
double time_ratio_cooldown_heatup = config_per_extruder[extruder].time_to_cooldown_1_degree / config_per_extruder[extruder].time_to_heatup_1_degree;
|
||||
double time_to_heat_from_standby_to_print_temp = timeToHeatFromStandbyToPrintTemp(extruder, temp);
|
||||
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
|
||||
if (time_needed_to_reach_standby_temp < time_window)
|
||||
{
|
||||
return time_to_heat_from_standby_to_print_temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return time_window * config_per_extruder[extruder].time_to_heatup_1_degree / (config_per_extruder[extruder].time_to_cooldown_1_degree + config_per_extruder[extruder].time_to_heatup_1_degree);
|
||||
}
|
||||
}
|
||||
/*!
|
||||
* Calculate time needed to warm up the nozzle from a given temp to a given temp.
|
||||
* If the printer is printing in the mean time the warming up will take longer.
|
||||
*
|
||||
*
|
||||
* \param from_temp The temperature at which the nozzle was before
|
||||
* \param extruder The extruder used
|
||||
* \param temp The temperature to which to heat
|
||||
* \param printing Whether the printer is printing in the time to heat up the nozzle
|
||||
* \return The time needed to reach the desired temperature (@p temp)
|
||||
*/
|
||||
double timeBeforeEndToInsertPreheatCommand_warmUp(double from_temp, unsigned int extruder, double temp, bool printing)
|
||||
{
|
||||
if (temp > from_temp)
|
||||
{
|
||||
if (printing)
|
||||
{
|
||||
return (temp - from_temp) * (config_per_extruder[extruder].time_to_heatup_1_degree + config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (temp - from_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printing)
|
||||
{
|
||||
return (from_temp - temp) * config_per_extruder[extruder].time_to_cooldown_1_degree;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (from_temp - temp) * std::max(0.0, config_per_extruder[extruder].time_to_cooldown_1_degree - config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // PREHEAT_H
|
||||
@@ -0,0 +1,311 @@
|
||||
#include "PrimeTower.h"
|
||||
|
||||
#include "ExtruderTrain.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "gcodeExport.h"
|
||||
#include "gcodePlanner.h"
|
||||
#include "infill.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
PrimeTower::PrimeTower()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder)
|
||||
{
|
||||
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
|
||||
}
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
|
||||
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::setConfigs(MeshGroup* meshgroup, int layer_thickness)
|
||||
{
|
||||
|
||||
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
|
||||
|
||||
for (int extr = 0; extr < extruder_count; extr++)
|
||||
{
|
||||
GCodePathConfig& conf = config_per_extruder[extr];
|
||||
conf.setLayerHeight(layer_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
|
||||
{ // compute storage.max_object_height_second_to_last_extruder, which is used to determine the highest point in the prime tower
|
||||
|
||||
extruder_count = storage.getSettingAsCount("machine_extruder_count");
|
||||
|
||||
int max_object_height_per_extruder[extruder_count];
|
||||
std::fill_n(max_object_height_per_extruder, extruder_count, -1); // unitialize all as -1
|
||||
{ // compute max_object_height_per_extruder
|
||||
for (SliceMeshStorage& mesh : storage.meshes)
|
||||
{
|
||||
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
|
||||
max_object_height_per_extruder[extr_nr] =
|
||||
std::max( max_object_height_per_extruder[extr_nr]
|
||||
, mesh.layer_nr_max_filled_layer );
|
||||
}
|
||||
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_extruder_nr] =
|
||||
std::max( max_object_height_per_extruder[support_extruder_nr]
|
||||
, storage.support.layer_nr_max_filled_layer );
|
||||
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
|
||||
max_object_height_per_extruder[support_roof_extruder_nr] =
|
||||
std::max( max_object_height_per_extruder[support_roof_extruder_nr]
|
||||
, storage.support.layer_nr_max_filled_layer );
|
||||
}
|
||||
{ // // compute max_object_height_second_to_last_extruder
|
||||
int extruder_max_object_height = 0;
|
||||
for (int extruder_nr = 1; extruder_nr < extruder_count; extruder_nr++)
|
||||
{
|
||||
if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_max_object_height])
|
||||
{
|
||||
extruder_max_object_height = extruder_nr;
|
||||
}
|
||||
}
|
||||
int extruder_second_max_object_height = -1;
|
||||
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
|
||||
{
|
||||
if (extruder_nr == extruder_max_object_height) { continue; }
|
||||
if (extruder_second_max_object_height == -1 || max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
|
||||
{
|
||||
extruder_second_max_object_height = extruder_nr;
|
||||
}
|
||||
}
|
||||
if (extruder_second_max_object_height < 0)
|
||||
{
|
||||
storage.max_object_height_second_to_last_extruder = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.max_object_height_second_to_last_extruder = max_object_height_per_extruder[extruder_second_max_object_height];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
|
||||
{
|
||||
PolygonRef p = storage.primeTower.ground_poly.newPoly();
|
||||
int tower_size = storage.getSettingInMicrons("prime_tower_size");
|
||||
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
|
||||
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
|
||||
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
|
||||
p.add(Point(x + tower_distance, y + tower_distance));
|
||||
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
|
||||
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
|
||||
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
|
||||
|
||||
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
|
||||
}
|
||||
|
||||
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
{
|
||||
generatePaths3(storage);
|
||||
}
|
||||
}
|
||||
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
|
||||
{
|
||||
|
||||
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
|
||||
{
|
||||
PolygonRef p = storage.primeTower.ground_poly.newPoly();
|
||||
int tower_size = storage.getSettingInMicrons("prime_tower_size");
|
||||
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
|
||||
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
|
||||
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
|
||||
p.add(Point(x + tower_distance, y + tower_distance));
|
||||
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
|
||||
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
|
||||
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
|
||||
|
||||
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrimeTower::generatePaths2(SliceDataStorage& storage) // half baked attempt at spiral shaped prime tower pattern
|
||||
{
|
||||
// extruder_count = storage.getSettingAsCount("machine_extruder_count");
|
||||
//
|
||||
// int64_t line_dists[extruder_count + 1]; // distance between the lines of different extruders, and half the line dist for beginning and ending
|
||||
// int64_t total_width = 0;
|
||||
// {
|
||||
// int64_t last_line_width = 0;
|
||||
// for (int extr = 0; extr < extruder_count; extr++)
|
||||
// {
|
||||
// int64_t line_width = config_per_extruder[extr].getLineWidth();
|
||||
// line_dists[extr] = (line_width + last_line_width) / 2;
|
||||
// total_width += line_width;
|
||||
// last_line_width = line_width;
|
||||
// }
|
||||
// line_dists[extruder_count] = last_line_width / 2;
|
||||
// }
|
||||
//
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PrimeTower::generatePaths3(SliceDataStorage& storage)
|
||||
{
|
||||
|
||||
int n_patterns = 2; // alternating patterns between layers
|
||||
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
|
||||
|
||||
generateGroundpoly(storage);
|
||||
|
||||
for (int extruder = 0; extruder < extruder_count; extruder++)
|
||||
{
|
||||
int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
|
||||
patterns_per_extruder.emplace_back(n_patterns);
|
||||
std::vector<Polygons>& patterns = patterns_per_extruder.back();
|
||||
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
|
||||
{
|
||||
Polygons result_polygons; // should remain empty, since we generate lines pattern!
|
||||
Polygons* in_between = nullptr;
|
||||
bool avoidOverlappingPerimeters = false;
|
||||
int outline_offset = -line_width/2;
|
||||
int line_distance = line_width;
|
||||
double fill_angle = 45 + pattern_idx * 90;
|
||||
Polygons& result_lines = patterns[pattern_idx];
|
||||
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, avoidOverlappingPerimeters, line_width, line_distance, infill_overlap, fill_angle);
|
||||
infill_comp.generate(result_polygons, result_lines, in_between);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
{
|
||||
if (!( storage.max_object_height_second_to_last_extruder >= 0
|
||||
// && storage.getSettingInMicrons("prime_tower_distance") > 0
|
||||
&& storage.getSettingInMicrons("prime_tower_size") > 0) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool prime_tower_added = false;
|
||||
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount() && !prime_tower_added; extruder++)
|
||||
{
|
||||
prime_tower_added = last_prime_tower_poly_printed[extruder] == int(layer_nr);
|
||||
}
|
||||
if (prime_tower_added)
|
||||
{ // don't print the prime tower if it has been printed already
|
||||
return;
|
||||
}
|
||||
|
||||
if (prev_extruder == gcodeLayer.getExtruder())
|
||||
{
|
||||
wipe = false;
|
||||
}
|
||||
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int new_extruder = gcodeLayer.getExtruder();
|
||||
|
||||
|
||||
Polygons& pattern = patterns_per_extruder[new_extruder][layer_nr % 2];
|
||||
|
||||
|
||||
GCodePathConfig& config = config_per_extruder[new_extruder];
|
||||
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
|
||||
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
|
||||
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
|
||||
|
||||
last_prime_tower_poly_printed[new_extruder] = layer_nr;
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
{
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
|
||||
}
|
||||
|
||||
if (wipe)
|
||||
{ //Make sure we wipe the old extruder on the prime tower.
|
||||
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
|
||||
}
|
||||
}
|
||||
|
||||
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
|
||||
{
|
||||
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int new_extruder = gcodeLayer.getExtruder();
|
||||
|
||||
int64_t offset = -config_per_extruder[new_extruder].getLineWidth();
|
||||
if (layer_nr > 0)
|
||||
offset *= 2;
|
||||
|
||||
//If we changed extruder, print the wipe/prime tower for this nozzle;
|
||||
std::vector<Polygons> insets;
|
||||
{ // generate polygons
|
||||
if ((layer_nr % 2) == 1)
|
||||
insets.push_back(storage.primeTower.ground_poly.offset(offset / 2));
|
||||
else
|
||||
insets.push_back(storage.primeTower.ground_poly);
|
||||
while(true)
|
||||
{
|
||||
Polygons new_inset = insets[insets.size() - 1].offset(offset);
|
||||
if (new_inset.size() < 1)
|
||||
break;
|
||||
insets.push_back(new_inset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(unsigned int n=0; n<insets.size(); n++)
|
||||
{
|
||||
GCodePathConfig& config = config_per_extruder[new_extruder];
|
||||
gcodeLayer.addPolygonsByOptimizer(insets[(prime_tower_dir_outward)? insets.size() - 1 - n : n], &config);
|
||||
}
|
||||
last_prime_tower_poly_printed[new_extruder] = layer_nr;
|
||||
|
||||
if (wipe)
|
||||
{ //Make sure we wipe the old extruder on the prime tower.
|
||||
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef PRIME_TOWER_H
|
||||
#define PRIME_TOWER_H
|
||||
|
||||
#include "gcodeExport.h" // GCodePathConfig
|
||||
#include "MeshGroup.h"
|
||||
#include "utils/polygon.h" // Polygons
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
class SliceDataStorage;
|
||||
class GCodePlanner;
|
||||
class GCodeExport;
|
||||
|
||||
typedef std::vector<IntPoint> PolyLine;
|
||||
|
||||
class PrimeTower
|
||||
{
|
||||
private:
|
||||
int extruder_count;
|
||||
std::vector<GCodePathConfig> config_per_extruder;
|
||||
|
||||
class WallInfill
|
||||
{
|
||||
|
||||
};
|
||||
public:
|
||||
void initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder);
|
||||
void setConfigs(MeshGroup* configs, int layer_thickness);
|
||||
|
||||
Polygons ground_poly;
|
||||
|
||||
std::vector<PolyLine> extruder_paths;
|
||||
|
||||
|
||||
void generateGroundpoly(SliceDataStorage& storage);
|
||||
|
||||
std::vector<std::vector<Polygons>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
|
||||
|
||||
void generatePaths3(SliceDataStorage& storage);
|
||||
|
||||
void generatePaths2(SliceDataStorage& storage);
|
||||
/*!
|
||||
* Generate the area where the prime tower should be.
|
||||
*
|
||||
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
|
||||
* \param total_layers The total number of layers
|
||||
*/
|
||||
void generatePaths(SliceDataStorage& storage, unsigned int total_layers);
|
||||
void generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers);
|
||||
|
||||
void computePrimeTowerMax(SliceDataStorage& storage);
|
||||
|
||||
PrimeTower();
|
||||
|
||||
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif // PRIME_TOWER_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef PRINT_FEATURE
|
||||
#define PRINT_FEATURE
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
enum class PrintFeatureType
|
||||
{
|
||||
NoneType, // unused, but libArcus depends on it
|
||||
OuterWall,
|
||||
InnerWall,
|
||||
Skin,
|
||||
Support,
|
||||
Skirt,
|
||||
Infill,
|
||||
SupportInfill,
|
||||
MoveCombing,
|
||||
MoveRetraction
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // PRINT_FEATURE
|
||||
@@ -0,0 +1,101 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#include "Progress.h"
|
||||
|
||||
#include "commandSocket.h"
|
||||
#include "utils/gettime.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
double Progress::times [] =
|
||||
{
|
||||
0.0,
|
||||
5.269,
|
||||
1.533,
|
||||
22.953,
|
||||
51.009,
|
||||
48.858,
|
||||
154.62,
|
||||
0.1
|
||||
};
|
||||
std::string Progress::names [] =
|
||||
{
|
||||
"start",
|
||||
"slice",
|
||||
"layerparts",
|
||||
"inset",
|
||||
"support",
|
||||
"skin",
|
||||
"export",
|
||||
"process"
|
||||
};
|
||||
|
||||
|
||||
double Progress::accumulated_times [N_PROGRESS_STAGES] = {-1};
|
||||
double Progress::total_timing = -1;
|
||||
|
||||
/*
|
||||
const Progress::Stage Progress::stages[] =
|
||||
{
|
||||
Progress::Stage::START,
|
||||
Progress::Stage::SLICING,
|
||||
Progress::Stage::PARTS,
|
||||
Progress::Stage::INSET,
|
||||
Progress::Stage::SUPPORT,
|
||||
Progress::Stage::SKIN,
|
||||
Progress::Stage::EXPORT,
|
||||
Progress::Stage::FINISH
|
||||
};
|
||||
*/
|
||||
|
||||
float Progress::calcOverallProgress(Stage stage, float stage_progress)
|
||||
{
|
||||
return ( accumulated_times[(int)stage] + stage_progress * times[(int)stage] ) / total_timing;
|
||||
}
|
||||
|
||||
|
||||
void Progress::init()
|
||||
{
|
||||
double accumulated_time = 0;
|
||||
for (int stage = 0; stage < N_PROGRESS_STAGES; stage++)
|
||||
{
|
||||
accumulated_times[(int)stage] = accumulated_time;
|
||||
accumulated_time += times[(int)stage];
|
||||
}
|
||||
total_timing = accumulated_time;
|
||||
}
|
||||
|
||||
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
|
||||
{
|
||||
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
|
||||
if (CommandSocket::getInstance())
|
||||
{
|
||||
CommandSocket::getInstance()->sendProgress(percentage);
|
||||
}
|
||||
|
||||
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
|
||||
}
|
||||
|
||||
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
|
||||
{
|
||||
if (CommandSocket::getInstance())
|
||||
{
|
||||
CommandSocket::getInstance()->sendProgressStage(stage);
|
||||
}
|
||||
|
||||
if (time_keeper)
|
||||
{
|
||||
if ((int)stage > 0)
|
||||
{
|
||||
log("Progress: %s accomplished in %5.3fs\n", names[(int)stage-1].c_str(), time_keeper->restart());
|
||||
}
|
||||
else
|
||||
{
|
||||
time_keeper->restart();
|
||||
}
|
||||
|
||||
if ((int)stage < (int)Stage::FINISH)
|
||||
log("Starting %s...\n", names[(int)stage].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace cura
|
||||
@@ -0,0 +1,73 @@
|
||||
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
|
||||
#ifndef PROGRESS_H
|
||||
#define PROGRESS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "utils/logoutput.h"
|
||||
#include "utils/gettime.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
class CommandSocket;
|
||||
|
||||
#define N_PROGRESS_STAGES 8
|
||||
|
||||
/*!
|
||||
* Class for handling the progress bar and the progress logging.
|
||||
*
|
||||
* The progress bar is based on a single slicing of a rather large model which needs some complex support;
|
||||
* the relative timing of each stage is currently based on that of the slicing of dragon_65_tilted_large.stl
|
||||
*/
|
||||
class Progress
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* The stage in the whole slicing process
|
||||
*/
|
||||
enum class Stage : unsigned int
|
||||
{
|
||||
START = 0,
|
||||
SLICING = 1,
|
||||
PARTS = 2,
|
||||
INSET = 3,
|
||||
SUPPORT = 4,
|
||||
SKIN = 5,
|
||||
EXPORT = 6,
|
||||
FINISH = 7
|
||||
};
|
||||
private:
|
||||
static double times [N_PROGRESS_STAGES]; //!< Time estimates per stage
|
||||
static std::string names[N_PROGRESS_STAGES]; //!< name of each stage
|
||||
static double accumulated_times [N_PROGRESS_STAGES]; //!< Time past before each stage
|
||||
static double total_timing; //!< An estimate of the total time
|
||||
/*!
|
||||
* Give an estimate between 0 and 1 of how far the process is.
|
||||
*
|
||||
* \param stage The current stage of processing
|
||||
* \param stage_process How far we currently are in the \p stage
|
||||
* \return An estimate of the overall progress.
|
||||
*/
|
||||
static float calcOverallProgress(Stage stage, float stage_progress);
|
||||
public:
|
||||
static void init(); //!< Initialize some values needed in a fast computation of the progress
|
||||
/*!
|
||||
* Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided).
|
||||
*
|
||||
* \param stage The current stage of processing
|
||||
* \param progress_in_stage Any number giving the progress within the stage
|
||||
* \param progress_in_stage_max The maximal value of \p progress_in_stage
|
||||
*/
|
||||
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
|
||||
/*!
|
||||
* Message the progress stage over the command socket.
|
||||
*
|
||||
* \param stage The current stage
|
||||
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
|
||||
*/
|
||||
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
|
||||
};
|
||||
|
||||
|
||||
} // name space cura
|
||||
#endif//PROGRESS_H
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef SPACE_FILL_TYPE
|
||||
#define SPACE_FILL_TYPE
|
||||
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Enum class enumerating the strategies with which an area can be occupied with filament
|
||||
*
|
||||
* The walls/perimeters are Polygons
|
||||
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
|
||||
* Grid, Triangles and lines infill is Lines
|
||||
*/
|
||||
enum class SpaceFillType
|
||||
{
|
||||
None,
|
||||
Polygons,
|
||||
PolyLines,
|
||||
Lines
|
||||
};
|
||||
|
||||
} // namespace cura
|
||||
|
||||
#endif // SPACE_FILL_TYPE
|
||||
@@ -0,0 +1,490 @@
|
||||
#include "Weaver.h"
|
||||
|
||||
#include <cmath> // sqrt
|
||||
#include <fstream> // debug IO
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Progress.h"
|
||||
#include "weaveDataStorage.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
void Weaver::weave(MeshGroup* meshgroup)
|
||||
{
|
||||
wireFrame.meshgroup = meshgroup;
|
||||
|
||||
int maxz = meshgroup->max().z;
|
||||
|
||||
int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;
|
||||
|
||||
DEBUG_SHOW(layer_count);
|
||||
|
||||
std::vector<cura::Slicer*> slicerList;
|
||||
|
||||
for(Mesh& mesh : meshgroup->meshes)
|
||||
{
|
||||
cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
|
||||
slicerList.push_back(slicer);
|
||||
}
|
||||
|
||||
int starting_layer_idx;
|
||||
{ // find first non-empty layer
|
||||
for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
|
||||
{
|
||||
Polygons parts;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
parts.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (parts.size() > 0)
|
||||
break;
|
||||
}
|
||||
if (starting_layer_idx > 0)
|
||||
{
|
||||
logError("First %i layers are empty!\n", starting_layer_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::cerr<< "chainifying layers..." << std::endl;
|
||||
{
|
||||
int starting_z = -1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
|
||||
|
||||
if (slicerList.empty()) //Wait, there is nothing to slice.
|
||||
{
|
||||
wireFrame.z_bottom = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
|
||||
}
|
||||
|
||||
Point starting_point_in_layer;
|
||||
if (wireFrame.bottom_outline.size() > 0)
|
||||
starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
|
||||
else
|
||||
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
|
||||
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
Polygons parts1;
|
||||
for (cura::Slicer* slicer : slicerList)
|
||||
parts1.add(slicer->layers[layer_idx].polygonList);
|
||||
|
||||
|
||||
Polygons chainified;
|
||||
|
||||
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
|
||||
|
||||
if (CommandSocket::isInstantiated())
|
||||
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
|
||||
|
||||
if (chainified.size() > 0)
|
||||
{
|
||||
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
|
||||
wireFrame.layers.emplace_back();
|
||||
WeaveLayer& layer = wireFrame.layers.back();
|
||||
|
||||
layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z;
|
||||
layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z;
|
||||
|
||||
layer.supported = chainified;
|
||||
|
||||
starting_point_in_layer = layer.supported.back().back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr<< "finding horizontal parts..." << std::endl;
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
|
||||
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
Polygons empty;
|
||||
Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty;
|
||||
|
||||
createHorizontalFill(*lower_top_parts, layer, layer_above, layer.z1);
|
||||
lower_top_parts = &layer.supported;
|
||||
}
|
||||
}
|
||||
// at this point layer.supported still only contains the polygons to be connected
|
||||
// when connecting layers, we further add the supporting polygons created by the roofs
|
||||
|
||||
std::cerr<< "connecting layers..." << std::endl;
|
||||
{
|
||||
Polygons* lower_top_parts = &wireFrame.bottom_outline;
|
||||
int last_z = wireFrame.z_bottom;
|
||||
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last
|
||||
{
|
||||
WeaveLayer& layer = wireFrame.layers[layer_idx];
|
||||
|
||||
connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer);
|
||||
layer.supported.add(layer.roofs.roof_outlines);
|
||||
lower_top_parts = &layer.supported;
|
||||
|
||||
last_z = layer.z1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // roofs:
|
||||
if (!wireFrame.layers.empty()) //If there are no layers, create no roof.
|
||||
{
|
||||
WeaveLayer& top_layer = wireFrame.layers.back();
|
||||
Polygons to_be_supported; // empty for the top layer
|
||||
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // bottom:
|
||||
if (!wireFrame.layers.empty()) //If there are no layers, create no bottom.
|
||||
{
|
||||
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
|
||||
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Weaver::createHorizontalFill(Polygons& lower_top_parts, WeaveLayer& layer, Polygons& layer_above, int z1)
|
||||
{
|
||||
int64_t bridgable_dist = connectionHeight;
|
||||
|
||||
// Polygons& polys_below = lower_top_parts;
|
||||
Polygons& polys_here = layer.supported;
|
||||
Polygons& polys_above = layer_above;
|
||||
|
||||
|
||||
{ // roofs
|
||||
Polygons to_be_supported = polys_above.offset(bridgable_dist);
|
||||
fillRoofs(polys_here, to_be_supported, -1, layer.z1, layer.roofs);
|
||||
|
||||
}
|
||||
|
||||
{ // floors
|
||||
Polygons to_be_supported = polys_above.offset(-bridgable_dist);
|
||||
fillFloors(polys_here, to_be_supported, 1, layer.z1, layer.roofs);
|
||||
}
|
||||
|
||||
{// optimize away doubly printed regions (boundaries of holes in layer etc.)
|
||||
for (WeaveRoofPart& inset : layer.roofs.roof_insets)
|
||||
connections2moves(inset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& horizontals)
|
||||
{
|
||||
std::vector<WeaveRoofPart>& insets = horizontals.roof_insets;
|
||||
|
||||
if (supporting.size() == 0) return; // no parts to start the roof from!
|
||||
|
||||
Polygons roofs = supporting.difference(to_be_supported);
|
||||
|
||||
roofs = roofs.offset(-roof_inset).offset(roof_inset);
|
||||
|
||||
if (roofs.size() == 0) return;
|
||||
|
||||
|
||||
Polygons roof_outlines;
|
||||
Polygons roof_holes;
|
||||
{ // split roofs into outlines and holes
|
||||
std::vector<PolygonsPart> roof_parts = roofs.splitIntoParts();
|
||||
for (PolygonsPart& roof_part : roof_parts)
|
||||
{
|
||||
roof_outlines.add(roof_part[0]);
|
||||
for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++)
|
||||
{
|
||||
roof_holes.add(roof_part[hole_idx]);
|
||||
roof_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Polygons supporting_outlines;
|
||||
|
||||
std::vector<PolygonsPart> supporting_parts = supporting.splitIntoParts();
|
||||
for (PolygonsPart& supporting_part : supporting_parts)
|
||||
supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes
|
||||
|
||||
|
||||
|
||||
Polygons inset1;
|
||||
Polygons last_inset;
|
||||
Polygons last_supported = supporting;
|
||||
for (Polygons inset0 = supporting_outlines; inset0.size() > 0; inset0 = last_inset)
|
||||
{
|
||||
last_inset = inset0.offset(direction * roof_inset, ClipperLib::jtRound);
|
||||
inset1 = last_inset.intersection(roof_outlines); // stay within roof area
|
||||
inset1 = inset1.unionPolygons(roof_holes);// make insets go around holes
|
||||
|
||||
if (inset1.size() == 0) break;
|
||||
|
||||
insets.emplace_back();
|
||||
|
||||
connect(last_supported, z, inset1, z, insets.back(), true);
|
||||
|
||||
inset1 = inset1.remove(roof_holes); // throw away holes which appear in every intersection
|
||||
inset1 = inset1.remove(roof_outlines);// throw away fully filled regions
|
||||
|
||||
last_supported = insets.back().supported; // chainified
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
horizontals.roof_outlines.add(roofs); // TODO just add the new lines, not the lines of the roofs which are already supported ==> make outlines into a connection from which we only print the top, not the connection
|
||||
}
|
||||
|
||||
void Weaver::fillFloors(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& horizontals)
|
||||
{
|
||||
std::vector<WeaveRoofPart>& outsets = horizontals.roof_insets;
|
||||
|
||||
if (to_be_supported.size() == 0) return; // no parts to start the floor from!
|
||||
if (supporting.size() == 0) return; // no parts to start the floor from!
|
||||
|
||||
Polygons floors = to_be_supported.difference(supporting);
|
||||
|
||||
floors = floors.offset(-roof_inset).offset(roof_inset);
|
||||
|
||||
if (floors.size() == 0) return;
|
||||
|
||||
|
||||
std::vector<PolygonsPart> floor_parts = floors.splitIntoParts();
|
||||
|
||||
Polygons floor_outlines;
|
||||
Polygons floor_holes;
|
||||
for (PolygonsPart& floor_part : floor_parts)
|
||||
{
|
||||
floor_outlines.add(floor_part[0]);
|
||||
for (unsigned int hole_idx = 1; hole_idx < floor_part.size(); hole_idx++)
|
||||
{
|
||||
floor_holes.add(floor_part[hole_idx]);
|
||||
//floor_holes.back().reverse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Polygons outset1;
|
||||
|
||||
Polygons last_supported = supporting;
|
||||
|
||||
for (Polygons outset0 = supporting; outset0.size() > 0; outset0 = outset1)
|
||||
{
|
||||
outset1 = outset0.offset(roof_inset * direction, ClipperLib::jtRound).intersection(floors);
|
||||
outset1 = outset1.remove(floor_holes); // throw away holes which appear in every intersection
|
||||
outset1 = outset1.remove(floor_outlines); // throw away holes which appear in every intersection
|
||||
|
||||
|
||||
outsets.emplace_back();
|
||||
|
||||
connect(last_supported, z, outset1, z, outsets.back(), true);
|
||||
|
||||
outset1 = outset1.remove(floor_outlines);// throw away fully filled regions
|
||||
|
||||
last_supported = outsets.back().supported; // chainified
|
||||
|
||||
}
|
||||
|
||||
|
||||
horizontals.roof_outlines.add(floors);
|
||||
}
|
||||
|
||||
|
||||
void Weaver::connections2moves(WeaveRoofPart& inset)
|
||||
{
|
||||
|
||||
|
||||
bool include_half_of_last_down = true;
|
||||
|
||||
|
||||
for (WeaveConnectionPart& part : inset.connections)
|
||||
{
|
||||
std::vector<WeaveConnectionSegment>& segments = part.connection.segments;
|
||||
for (unsigned int idx = 0; idx < part.connection.segments.size(); idx += 2)
|
||||
{
|
||||
WeaveConnectionSegment& segment = segments[idx];
|
||||
assert(segment.segmentType == WeaveSegmentType::UP);
|
||||
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
|
||||
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
|
||||
if (skipped)
|
||||
{
|
||||
unsigned int begin = idx;
|
||||
for (; idx < segments.size(); idx += 2)
|
||||
{
|
||||
WeaveConnectionSegment& segment = segments[idx];
|
||||
assert(segments[idx].segmentType == WeaveSegmentType::UP);
|
||||
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
|
||||
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
|
||||
if (!skipped)
|
||||
break;
|
||||
}
|
||||
int end = idx - ((include_half_of_last_down)? 2 : 1);
|
||||
if (idx >= segments.size())
|
||||
segments.erase(segments.begin() + begin, segments.end());
|
||||
else
|
||||
{
|
||||
segments.erase(segments.begin() + begin, segments.begin() + end);
|
||||
if (begin < segments.size())
|
||||
{
|
||||
segments[begin].segmentType = WeaveSegmentType::MOVE;
|
||||
if (include_half_of_last_down)
|
||||
segments[begin+1].segmentType = WeaveSegmentType::DOWN_AND_FLAT;
|
||||
}
|
||||
idx = begin + ((include_half_of_last_down)? 2 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Weaver::connect(Polygons& parts0, int z0, Polygons& parts1, int z1, WeaveConnection& result, bool include_last)
|
||||
{
|
||||
// TODO: convert polygons (with outset + difference) such that after printing the first polygon, we can't be in the way of the printed stuff
|
||||
// something like:
|
||||
// for (m > n)
|
||||
// parts[m] = parts[m].difference(parts[n].offset(nozzle_top_diameter))
|
||||
// according to the printing order!
|
||||
//
|
||||
// OR! :
|
||||
//
|
||||
// unify different parts if gap is too small
|
||||
|
||||
Polygons& supported = result.supported;
|
||||
|
||||
if (parts1.size() == 0) return;
|
||||
|
||||
Point& start_close_to = (parts0.size() > 0)? parts0.back().back() : parts1.back().back();
|
||||
|
||||
chainify_polygons(parts1, start_close_to, supported, include_last);
|
||||
|
||||
if (parts0.size() == 0) return;
|
||||
|
||||
connect_polygons(parts0, z0, supported, z1, result);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons& result, bool include_last)
|
||||
{
|
||||
|
||||
|
||||
for (unsigned int prt = 0 ; prt < parts1.size(); prt++)
|
||||
{
|
||||
const PolygonRef upperPart = parts1[prt];
|
||||
|
||||
ClosestPolygonPoint closestInPoly = PolygonUtils::findClosest(start_close_to, upperPart);
|
||||
|
||||
|
||||
PolygonRef part_top = result.newPoly();
|
||||
|
||||
GivenDistPoint next_upper;
|
||||
bool found = true;
|
||||
int idx = 0;
|
||||
|
||||
for (Point upper_point = upperPart[closestInPoly.pos]; found; upper_point = next_upper.location)
|
||||
{
|
||||
found = PolygonUtils::getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.pos, next_upper);
|
||||
|
||||
|
||||
if (!found)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
part_top.add(upper_point);
|
||||
|
||||
idx = next_upper.pos;
|
||||
}
|
||||
if (part_top.size() > 0)
|
||||
start_close_to = part_top.back();
|
||||
else
|
||||
result.remove(result.size()-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported, int z1, WeaveConnection& result)
|
||||
{
|
||||
|
||||
if (supporting.size() < 1)
|
||||
{
|
||||
DEBUG_PRINTLN("lower layer has zero parts!");
|
||||
return;
|
||||
}
|
||||
|
||||
result.z0 = z0;
|
||||
result.z1 = z1;
|
||||
|
||||
std::vector<WeaveConnectionPart>& parts = result.connections;
|
||||
|
||||
for (unsigned int prt = 0 ; prt < supported.size(); prt++)
|
||||
{
|
||||
|
||||
const PolygonRef upperPart = supported[prt];
|
||||
|
||||
|
||||
parts.emplace_back(prt);
|
||||
WeaveConnectionPart& part = parts.back();
|
||||
PolyLine3& connection = part.connection;
|
||||
|
||||
Point3 last_upper;
|
||||
bool firstIter = true;
|
||||
|
||||
for (const Point& upper_point : upperPart)
|
||||
{
|
||||
|
||||
ClosestPolygonPoint lowerPolyPoint = PolygonUtils::findClosest(upper_point, supporting);
|
||||
Point& lower = lowerPolyPoint.location;
|
||||
|
||||
Point3 lower3 = Point3(lower.X, lower.Y, z0);
|
||||
Point3 upper3 = Point3(upper_point.X, upper_point.Y, z1);
|
||||
|
||||
|
||||
if (firstIter)
|
||||
connection.from = lower3;
|
||||
else
|
||||
connection.segments.emplace_back<>(lower3, WeaveSegmentType::DOWN);
|
||||
|
||||
connection.segments.emplace_back<>(upper3, WeaveSegmentType::UP);
|
||||
last_upper = upper3;
|
||||
|
||||
firstIter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//namespace cura
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
#ifndef WEAVER_H
|
||||
#define WEAVER_H
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "utils/polygonUtils.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* The main weaver / WirePrint / wireframe printing class, which computes the basic paths to be followed.
|
||||
*/
|
||||
class Weaver : public SettingsMessenger, NoCopy
|
||||
{
|
||||
friend class Wireframe2gcode;
|
||||
private:
|
||||
static const int HIGHER_BEND_NO_STRAIGHTEN = 0;
|
||||
static const int MOVE_TO_STRAIGHTEN = 1;
|
||||
static const int RETRACT_TO_STRAIGHTEN = 2;
|
||||
|
||||
int initial_layer_thickness;
|
||||
int connectionHeight;
|
||||
int extrusionWidth;
|
||||
|
||||
int roof_inset;
|
||||
|
||||
int nozzle_outer_diameter;
|
||||
double nozzle_expansion_angle;
|
||||
int nozzle_clearance;
|
||||
int nozzle_top_diameter;
|
||||
|
||||
|
||||
public:
|
||||
Weaver(SettingsBase* settings_base) : SettingsMessenger(settings_base)
|
||||
{
|
||||
|
||||
initial_layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
connectionHeight = getSettingInMicrons("wireframe_height");
|
||||
|
||||
extrusionWidth = getSettingInMicrons("wall_line_width_x");
|
||||
|
||||
roof_inset = getSettingInMicrons("wireframe_roof_inset");
|
||||
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
|
||||
nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ .
|
||||
nozzle_clearance = getSettingInMicrons("wireframe_nozzle_clearance"); // at least line width
|
||||
nozzle_top_diameter = tan(nozzle_expansion_angle) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This is the main function for Neith / Weaving / WirePrinting / Webbed printing.
|
||||
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
|
||||
*
|
||||
* \param objects The objects for which to create a wireframe print
|
||||
*/
|
||||
void weave(MeshGroup* objects);
|
||||
|
||||
|
||||
private:
|
||||
WireFrame wireFrame;
|
||||
|
||||
|
||||
/*!
|
||||
* Connect two polygons, chainify the second and generate connections from it, supporting on the first polygon.
|
||||
*
|
||||
* \param supporting The polygons from which to start the connection
|
||||
* \param z0 The height of the \p supporting
|
||||
* \param supported The polygons to be supported by the connection from \p supporting to \p supported
|
||||
* \param z1 the height of \p supported
|
||||
* \param include_last Whether the last full link should be included in the chainified \p parts1 if the last link would be shorter than the normal link size.
|
||||
*/
|
||||
void connect(Polygons& parts0, int z0, Polygons& parts1, int z1, WeaveConnection& result, bool include_last);
|
||||
|
||||
/*!
|
||||
* Convert polygons, such that they consist of segments/links of uniform size, namely \p nozzle_top_diameter.
|
||||
*
|
||||
* \param parts1 The polygons to be chainified
|
||||
* \param start_close_to The point from which to start the first link
|
||||
* \param include_last governs whether the last segment is smaller or grater than the \p nozzle_top_diameter.
|
||||
* If true, the last segment may be smaller.
|
||||
*/
|
||||
void chainify_polygons(Polygons& parts1, Point start_close_to, Polygons& result, bool include_last);
|
||||
|
||||
/*!
|
||||
* The main weaving function.
|
||||
* Generate connections between two polygons.
|
||||
* The connections consist of zig zags of which the zig is a line from a point in \p supported to the closest point in \p supporting
|
||||
* and the zag is a diagonal line from the same point in \p supported to a point in \p supporting
|
||||
* with a distance equal to Weaver::nozzle_top_diameter from the other point in \p supporting of the zig.
|
||||
*
|
||||
* \param supporting The polygons from which to start the connection
|
||||
* \param z0 The height of the \p supporting
|
||||
* \param supported The polygons to be supported by the connection from \p supporting to \p supported
|
||||
* \param z1 the height of \p supported
|
||||
* \param result The resulting connection
|
||||
*/
|
||||
void connect_polygons(Polygons& supporting, int z0, Polygons& supported, int z1, WeaveConnection& result);
|
||||
|
||||
/*!
|
||||
* Creates the roofs and floors which are laid down horizontally.
|
||||
*/
|
||||
void createHorizontalFill(Polygons& lower_top_parts, WeaveLayer& layer, Polygons& layer_above, int z1);
|
||||
|
||||
/*!
|
||||
* Fill roofs starting from the outlines of \p supporting.
|
||||
* The area to be filled in is difference( \p to_be_supported , \p supporting ).
|
||||
*
|
||||
* The basic algorithm performs insets on \p supported until the whole area of \p to_be_supported is filled.
|
||||
* In order to not fill holes in the roof, the hole-areas are unioned with the insets, which results in connections where the UP move has close to zero length;
|
||||
* pieces of the area between two consecutive insets have close to zero distance at these points.
|
||||
* These parts of the horizontal infills are converted into moves by the function \p connections2moves.
|
||||
*
|
||||
* Note that the new inset is computed from the last inset, while the connections are between the last chainified inset and the new chainified inset.
|
||||
*
|
||||
*/
|
||||
void fillRoofs(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& roofs);
|
||||
|
||||
/*!
|
||||
* Fill floors starting from the outlines of \p supporting.
|
||||
* The area to be filled in is \p floors = difference( \p to_be_supported , \p supporting ).
|
||||
*
|
||||
* The basic algorithm performs outsets until the whole area of [to_be_supported] is filled.
|
||||
* In order to not fill too much, the outsets are intersected with the [floors] area, which results in connections where the UP move has close to zero length.
|
||||
* These parts of the horizontal infills are converted into moves by the function [connections2moves].
|
||||
*
|
||||
* The first supporting polygons are \p supporting while the supporting polygons in consecutive iterations are sub-areas of \p floors.
|
||||
*
|
||||
* Note that the new outset is computed from the last outset, while the connections are between the last chainified outset and the new (chainified) outset.
|
||||
*
|
||||
*/
|
||||
void fillFloors(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& roofs);
|
||||
|
||||
/*!
|
||||
* Filter out parts of connections with small distances; replace by moves.
|
||||
*
|
||||
*/
|
||||
void connections2moves(WeaveRoofPart& inset);
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//WEAVER_H
|
||||
@@ -0,0 +1,623 @@
|
||||
#include "Wireframe2gcode.h"
|
||||
|
||||
#include <cmath> // sqrt
|
||||
#include <fstream> // debug IO
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
#include "Progress.h"
|
||||
|
||||
#include "pathOrderOptimizer.h" // for skirt
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
|
||||
void Wireframe2gcode::writeGCode()
|
||||
{
|
||||
|
||||
gcode.preSetup(wireFrame.meshgroup);
|
||||
|
||||
if (CommandSocket::getInstance())
|
||||
CommandSocket::getInstance()->beginGCode();
|
||||
|
||||
processStartingCode();
|
||||
|
||||
int maxObjectHeight;
|
||||
if (wireFrame.layers.empty())
|
||||
{
|
||||
maxObjectHeight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxObjectHeight = wireFrame.layers.back().z1;
|
||||
}
|
||||
|
||||
processSkirt();
|
||||
|
||||
|
||||
unsigned int total_layers = wireFrame.layers.size();
|
||||
gcode.writeLayerComment(0);
|
||||
gcode.writeTypeComment("SKIRT");
|
||||
|
||||
gcode.setZ(initial_layer_thickness);
|
||||
|
||||
for (PolygonRef bottom_part : wireFrame.bottom_infill.roof_outlines)
|
||||
{
|
||||
if (bottom_part.size() == 0) continue;
|
||||
writeMoveWithRetract(bottom_part[bottom_part.size()-1]);
|
||||
for (Point& segment_to : bottom_part)
|
||||
{
|
||||
gcode.writeMove(segment_to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// bottom:
|
||||
Polygons empty_outlines;
|
||||
writeFill(wireFrame.bottom_infill.roof_insets, empty_outlines,
|
||||
[this](Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx) {
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE || segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT) // this is the case when an inset overlaps with a hole
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_connection);
|
||||
}
|
||||
}
|
||||
,
|
||||
[this](Wireframe2gcode& thiss, WeaveConnectionSegment& segment) {
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE)
|
||||
writeMoveWithRetract(segment.to);
|
||||
else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT)
|
||||
return; // do nothing
|
||||
else
|
||||
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
|
||||
}
|
||||
);
|
||||
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
|
||||
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
|
||||
{
|
||||
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
|
||||
|
||||
WeaveLayer& layer = wireFrame.layers[layer_nr];
|
||||
|
||||
gcode.writeLayerComment(layer_nr+1);
|
||||
|
||||
double fanSpeed = getSettingInPercentage("cool_fan_speed_max");
|
||||
if (layer_nr == 0)
|
||||
fanSpeed = getSettingInPercentage("cool_fan_speed_min");
|
||||
gcode.writeFanCommand(fanSpeed);
|
||||
|
||||
for (unsigned int part_nr = 0; part_nr < layer.connections.size(); part_nr++)
|
||||
{
|
||||
WeaveConnectionPart& part = layer.connections[part_nr];
|
||||
|
||||
if (part.connection.segments.size() == 0) continue;
|
||||
|
||||
gcode.writeTypeComment("SUPPORT"); // connection
|
||||
{
|
||||
if (vSize2(gcode.getPositionXY() - part.connection.from) > connectionHeight)
|
||||
{
|
||||
Point3 point_same_height(part.connection.from.x, part.connection.from.y, layer.z1+100);
|
||||
writeMoveWithRetract(point_same_height);
|
||||
}
|
||||
writeMoveWithRetract(part.connection.from);
|
||||
for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++)
|
||||
{
|
||||
handle_segment(layer, part, segment_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
gcode.writeTypeComment("WALL-OUTER"); // top
|
||||
{
|
||||
for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
if (segment.segmentType == WeaveSegmentType::DOWN) continue;
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE)
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
|
||||
gcode.writeDelay(flat_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// roofs:
|
||||
gcode.setZ(layer.z1);
|
||||
std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)>
|
||||
handle_roof = &Wireframe2gcode::handle_roof_segment;
|
||||
writeFill(layer.roofs.roof_insets, layer.roofs.roof_outlines,
|
||||
handle_roof,
|
||||
[this](Wireframe2gcode& thiss, WeaveConnectionSegment& segment) { // handle flat segments
|
||||
if (segment.segmentType == WeaveSegmentType::MOVE)
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
} else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT)
|
||||
{
|
||||
// do nothing
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
|
||||
gcode.writeDelay(flat_delay);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
gcode.setZ(maxObjectHeight);
|
||||
|
||||
gcode.writeRetraction(&standard_retraction_config);
|
||||
|
||||
|
||||
gcode.updateTotalPrintTime();
|
||||
|
||||
gcode.writeDelay(0.3);
|
||||
|
||||
gcode.writeFanCommand(0);
|
||||
|
||||
finalize();
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
if (go_back_to_last_top)
|
||||
gcode.writeMove(from, speedDown, 0);
|
||||
if (straight_first_when_going_down <= 0)
|
||||
{
|
||||
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
|
||||
} else
|
||||
{
|
||||
Point3& to = segment.to;
|
||||
Point3 from = gcode.getPosition();// segment.from;
|
||||
Point3 vec = to - from;
|
||||
Point3 in_between = from + vec * straight_first_when_going_down / 100;
|
||||
|
||||
Point3 up(in_between.x, in_between.y, from.z);
|
||||
int64_t new_length = (up - from).vSize() + (to - up).vSize() + 5;
|
||||
int64_t orr_length = vec.vSize();
|
||||
double enlargement = new_length / orr_length;
|
||||
gcode.writeMove(up, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
|
||||
gcode.writeMove(to, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
|
||||
}
|
||||
gcode.writeDelay(bottom_delay);
|
||||
if (up_dist_half_speed > 0)
|
||||
{
|
||||
|
||||
gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_per_mm_connection * 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
gcode.writeMove(segment.to, speedUp, extrusion_per_mm_connection);
|
||||
Point3 next_vector;
|
||||
if (segment_idx + 1 < part.connection.segments.size())
|
||||
{
|
||||
WeaveConnectionSegment& next_segment = part.connection.segments[segment_idx+1];
|
||||
next_vector = next_segment.to - segment.to;
|
||||
} else
|
||||
{
|
||||
next_vector = part.connection.segments[0].to - segment.to;
|
||||
}
|
||||
Point next_dir_2D(next_vector.x, next_vector.y);
|
||||
next_dir_2D = next_dir_2D * top_jump_dist / vSize(next_dir_2D);
|
||||
Point3 next_dir (next_dir_2D.X / 2, next_dir_2D.Y / 2, -top_jump_dist);
|
||||
|
||||
Point3 current_pos = gcode.getPosition();
|
||||
|
||||
gcode.writeMove(current_pos - next_dir, speedUp, 0);
|
||||
gcode.writeDelay(top_delay);
|
||||
gcode.writeMove(current_pos + next_dir_2D, speedUp, 0);
|
||||
}
|
||||
|
||||
void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
|
||||
RetractionConfig retraction_config;
|
||||
// TODO: get these from the settings!
|
||||
retraction_config.distance = 500; //INT2MM(getSettingInt("retraction_amount"))
|
||||
retraction_config.prime_volume = 0;//INT2MM(getSettingInt("retractionPrime
|
||||
retraction_config.speed = 20; // 40;
|
||||
retraction_config.primeSpeed = 15; // 30;
|
||||
retraction_config.zHop = 0; //getSettingInt("retraction_hop");
|
||||
retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
|
||||
retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
|
||||
retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
|
||||
double top_retract_pause = 2.0;
|
||||
int retract_hop_dist = 1000;
|
||||
bool after_retract_hop = false;
|
||||
//bool go_horizontal_first = true;
|
||||
bool lower_retract_start = true;
|
||||
|
||||
|
||||
Point3& to = segment.to;
|
||||
if (lower_retract_start)
|
||||
{
|
||||
Point3 vec = to - from;
|
||||
Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize();
|
||||
Point3 lower = to - lowering;
|
||||
gcode.writeMove(lower, speedUp, extrusion_per_mm_connection);
|
||||
gcode.writeRetraction(&retraction_config);
|
||||
gcode.writeMove(to + lowering, speedUp, 0);
|
||||
gcode.writeDelay(top_retract_pause);
|
||||
if (after_retract_hop)
|
||||
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
|
||||
|
||||
} else
|
||||
{
|
||||
gcode.writeMove(to, speedUp, extrusion_per_mm_connection);
|
||||
gcode.writeRetraction(&retraction_config);
|
||||
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
|
||||
gcode.writeDelay(top_retract_pause);
|
||||
if (after_retract_hop)
|
||||
gcode.writeMove(to + Point3(0, 0, retract_hop_dist*3), speedFlat, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Wireframe2gcode::strategy_compensate(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
Point3 to = segment.to + Point3(0, 0, fall_down*(segment.to - from).vSize() / connectionHeight);
|
||||
Point3 vector = segment.to - from;
|
||||
Point3 dir = vector * drag_along / vector.vSize();
|
||||
|
||||
Point3 next_point;
|
||||
if (segment_idx + 1 < part.connection.segments.size())
|
||||
{
|
||||
WeaveConnectionSegment& next_segment = part.connection.segments[segment_idx+1];
|
||||
next_point = next_segment.to;
|
||||
} else
|
||||
{
|
||||
next_point = part.connection.segments[0].to;
|
||||
}
|
||||
Point3 next_vector = next_point - segment.to;
|
||||
Point next_dir_2D(next_vector.x, next_vector.y);
|
||||
int64_t next_dir_2D_size = vSize(next_dir_2D);
|
||||
if (next_dir_2D_size > 0)
|
||||
next_dir_2D = next_dir_2D * drag_along / next_dir_2D_size;
|
||||
Point3 next_dir (next_dir_2D.X, next_dir_2D.Y, 0);
|
||||
|
||||
Point3 newTop = to - next_dir + dir;
|
||||
|
||||
int64_t orrLength = (segment.to - from).vSize() + next_vector.vSize() + 1; // + 1 in order to avoid division by zero
|
||||
int64_t newLength = (newTop - from).vSize() + (next_point - newTop).vSize() + 1; // + 1 in order to avoid division by zero
|
||||
|
||||
gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_per_mm_connection * orrLength / newLength);
|
||||
}
|
||||
void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
|
||||
switch(segment.segmentType)
|
||||
{
|
||||
case WeaveSegmentType::MOVE:
|
||||
writeMoveWithRetract(segment.to);
|
||||
break;
|
||||
case WeaveSegmentType::DOWN:
|
||||
go_down(layer, part, segment_idx);
|
||||
break;
|
||||
case WeaveSegmentType::FLAT:
|
||||
DEBUG_SHOW("flat piece in connection?!!?!");
|
||||
break;
|
||||
case WeaveSegmentType::UP:
|
||||
if (strategy == STRATEGY_KNOT)
|
||||
{
|
||||
strategy_knot(layer, part, segment_idx);
|
||||
} else if (strategy == STRATEGY_RETRACT)
|
||||
{
|
||||
strategy_retract(layer, part, segment_idx);
|
||||
} else if (strategy == STRATEGY_COMPENSATE)
|
||||
{
|
||||
strategy_compensate(layer, part, segment_idx);
|
||||
}
|
||||
break;
|
||||
case WeaveSegmentType::DOWN_AND_FLAT:
|
||||
logError("Down and flat move in non-horizontal connection!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)
|
||||
{
|
||||
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
|
||||
Point3 from = (segment_idx == 0)? part.connection.from : part.connection.segments[segment_idx - 1].to;
|
||||
WeaveConnectionSegment* next_segment = nullptr;
|
||||
if (segment_idx + 1 < part.connection.segments.size())
|
||||
next_segment = &part.connection.segments[segment_idx+1];
|
||||
switch(segment.segmentType)
|
||||
{
|
||||
case WeaveSegmentType::MOVE:
|
||||
case WeaveSegmentType::DOWN_AND_FLAT:
|
||||
if (next_segment && next_segment->segmentType != WeaveSegmentType::DOWN_AND_FLAT)
|
||||
{
|
||||
writeMoveWithRetract(segment.to);
|
||||
}
|
||||
break;
|
||||
case WeaveSegmentType::UP:
|
||||
{
|
||||
Point3 to = segment.to + Point3(0, 0, roof_fall_down);
|
||||
|
||||
Point3 vector = segment.to - from;
|
||||
if (vector.vSize2() == 0) return;
|
||||
Point3 dir = vector * roof_drag_along / vector.vSize();
|
||||
|
||||
Point3 next_vector;
|
||||
if (next_segment)
|
||||
{
|
||||
next_vector = next_segment->to - segment.to;
|
||||
} else
|
||||
{
|
||||
next_vector = part.connection.segments[0].to - segment.to;
|
||||
}
|
||||
Point next_dir_2D(next_vector.x, next_vector.y);
|
||||
Point3 detoured = to + dir;
|
||||
if (vSize2(next_dir_2D) > 0)
|
||||
{
|
||||
next_dir_2D = next_dir_2D * roof_drag_along / vSize(next_dir_2D);
|
||||
Point3 next_dir (next_dir_2D.X, next_dir_2D.Y, 0);
|
||||
detoured -= next_dir;
|
||||
}
|
||||
|
||||
gcode.writeMove(detoured, speedUp, extrusion_per_mm_connection);
|
||||
|
||||
}
|
||||
break;
|
||||
case WeaveSegmentType::DOWN:
|
||||
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
|
||||
gcode.writeDelay(roof_outer_delay);
|
||||
break;
|
||||
case WeaveSegmentType::FLAT:
|
||||
logError("Flat move in connection!");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& roof_outlines
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler)
|
||||
{
|
||||
|
||||
// bottom:
|
||||
gcode.writeTypeComment("FILL");
|
||||
for (unsigned int inset_idx = 0; inset_idx < infill_insets.size(); inset_idx++)
|
||||
{
|
||||
WeaveRoofPart& inset = infill_insets[inset_idx];
|
||||
|
||||
|
||||
for (unsigned int inset_part_nr = 0; inset_part_nr < inset.connections.size(); inset_part_nr++)
|
||||
{
|
||||
WeaveConnectionPart& inset_part = inset.connections[inset_part_nr];
|
||||
std::vector<WeaveConnectionSegment>& segments = inset_part.connection.segments;
|
||||
|
||||
gcode.writeTypeComment("SUPPORT"); // connection
|
||||
if (segments.size() == 0) continue;
|
||||
Point3 first_extrusion_from = inset_part.connection.from;
|
||||
unsigned int first_segment_idx;
|
||||
for (first_segment_idx = 0; first_segment_idx < segments.size() && segments[first_segment_idx].segmentType == WeaveSegmentType::MOVE; first_segment_idx++)
|
||||
{ // finds the first segment which is not a move
|
||||
first_extrusion_from = segments[first_segment_idx].to;
|
||||
}
|
||||
if (first_segment_idx == segments.size())
|
||||
continue;
|
||||
writeMoveWithRetract(first_extrusion_from);
|
||||
for (unsigned int segment_idx = first_segment_idx; segment_idx < segments.size(); segment_idx++)
|
||||
{
|
||||
connectionHandler(*this, inset, inset_part, segment_idx);
|
||||
}
|
||||
|
||||
gcode.writeTypeComment("WALL-INNER"); // top
|
||||
for (unsigned int segment_idx = 0; segment_idx < segments.size(); segment_idx++)
|
||||
{
|
||||
WeaveConnectionSegment& segment = segments[segment_idx];
|
||||
|
||||
if (segment.segmentType == WeaveSegmentType::DOWN) continue;
|
||||
|
||||
flatHandler(*this, segment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
gcode.writeTypeComment("WALL-OUTER"); // outer perimeter of the flat parts
|
||||
for (PolygonRef poly : roof_outlines)
|
||||
{
|
||||
writeMoveWithRetract(poly[poly.size() - 1]);
|
||||
for (Point& p : poly)
|
||||
{
|
||||
Point3 to(p.X, p.Y, gcode.getPositionZ());
|
||||
WeaveConnectionSegment segment(to, WeaveSegmentType::FLAT);
|
||||
flatHandler(*this, segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Wireframe2gcode::writeMoveWithRetract(Point3 to)
|
||||
{
|
||||
if ((gcode.getPosition() - to).vSize2() >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
|
||||
gcode.writeRetraction(&standard_retraction_config);
|
||||
gcode.writeMove(to, moveSpeed, 0);
|
||||
}
|
||||
|
||||
void Wireframe2gcode::writeMoveWithRetract(Point to)
|
||||
{
|
||||
if (vSize2(gcode.getPositionXY() - to) >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
|
||||
gcode.writeRetraction(&standard_retraction_config);
|
||||
gcode.writeMove(to, moveSpeed, 0);
|
||||
}
|
||||
|
||||
Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base)
|
||||
: SettingsMessenger(settings_base)
|
||||
, gcode(gcode)
|
||||
, wireFrame(weaver.wireFrame)
|
||||
{
|
||||
initial_layer_thickness = getSettingInMicrons("layer_height_0");
|
||||
connectionHeight = getSettingInMicrons("wireframe_height");
|
||||
roof_inset = getSettingInMicrons("wireframe_roof_inset");
|
||||
|
||||
filament_diameter = getSettingInMicrons("material_diameter");
|
||||
extrusionWidth = getSettingInMicrons("wall_line_width_x");
|
||||
|
||||
flowConnection = getSettingInPercentage("wireframe_flow_connection");
|
||||
flowFlat = getSettingInPercentage("wireframe_flow_flat");
|
||||
|
||||
double filament_area = /* M_PI * */ (INT2MM(filament_diameter) / 2.0) * (INT2MM(filament_diameter) / 2.0);
|
||||
double lineArea = /* M_PI * */ (INT2MM(extrusionWidth) / 2.0) * (INT2MM(extrusionWidth) / 2.0);
|
||||
extrusion_per_mm_connection = lineArea / filament_area * flowConnection / 100.0;
|
||||
extrusion_per_mm_flat = lineArea / filament_area * flowFlat / 100.0;
|
||||
|
||||
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
|
||||
nozzle_head_distance = getSettingInMicrons("machine_nozzle_head_distance"); // | | .
|
||||
nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ .
|
||||
nozzle_clearance = getSettingInMicrons("wireframe_nozzle_clearance"); // at least line width
|
||||
nozzle_top_diameter = tan(nozzle_expansion_angle) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
|
||||
|
||||
moveSpeed = 40;
|
||||
speedBottom = getSettingInMillimetersPerSecond("wireframe_printspeed_bottom");
|
||||
speedUp = getSettingInMillimetersPerSecond("wireframe_printspeed_up");
|
||||
speedDown = getSettingInMillimetersPerSecond("wireframe_printspeed_down");
|
||||
speedFlat = getSettingInMillimetersPerSecond("wireframe_printspeed_flat");
|
||||
|
||||
flat_delay = getSettingInSeconds("wireframe_flat_delay");
|
||||
bottom_delay = getSettingInSeconds("wireframe_bottom_delay");
|
||||
top_delay = getSettingInSeconds("wireframe_top_delay");
|
||||
|
||||
up_dist_half_speed = getSettingInMicrons("wireframe_up_half_speed");
|
||||
|
||||
top_jump_dist = getSettingInMicrons("wireframe_top_jump");
|
||||
|
||||
fall_down = getSettingInMicrons("wireframe_fall_down");
|
||||
drag_along = getSettingInMicrons("wireframe_drag_along");
|
||||
|
||||
strategy = STRATEGY_COMPENSATE;
|
||||
if (getSettingString("wireframe_strategy") == "Compensate")
|
||||
strategy = STRATEGY_COMPENSATE;
|
||||
if (getSettingString("wireframe_strategy") == "Knot")
|
||||
strategy = STRATEGY_KNOT;
|
||||
if (getSettingString("wireframe_strategy") == "Retract")
|
||||
strategy = STRATEGY_RETRACT;
|
||||
|
||||
go_back_to_last_top = false;
|
||||
straight_first_when_going_down = getSettingInPercentage("wireframe_straight_before_down");
|
||||
|
||||
roof_fall_down = getSettingInMicrons("wireframe_roof_fall_down");
|
||||
roof_drag_along = getSettingInMicrons("wireframe_roof_drag_along");
|
||||
roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay");
|
||||
|
||||
|
||||
standard_retraction_config.distance = INT2MM(getSettingInMicrons("retraction_amount"));
|
||||
standard_retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
|
||||
standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
|
||||
standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
|
||||
standard_retraction_config.zHop = getSettingInMicrons("retraction_hop");
|
||||
standard_retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
|
||||
standard_retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
|
||||
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
|
||||
}
|
||||
|
||||
void Wireframe2gcode::processStartingCode()
|
||||
{
|
||||
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
|
||||
{
|
||||
if (!CommandSocket::isInstantiated())
|
||||
{
|
||||
gcode.writeCode(gcode.getFileHeader().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getSettingBoolean("material_bed_temp_prepend"))
|
||||
{
|
||||
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
|
||||
{
|
||||
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
|
||||
}
|
||||
}
|
||||
|
||||
if (getSettingBoolean("material_print_temp_prepend"))
|
||||
{
|
||||
if (getSettingInDegreeCelsius("material_print_temperature") > 0)
|
||||
{
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
|
||||
if (getSettingBoolean("machine_print_temp_wait"))
|
||||
{
|
||||
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
|
||||
|
||||
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
|
||||
if (gcode.getFlavor() == EGCodeFlavor::BFB)
|
||||
{
|
||||
gcode.writeComment("enable auto-retraction");
|
||||
std::ostringstream tmp;
|
||||
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000);
|
||||
gcode.writeLine(tmp.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::processSkirt()
|
||||
{
|
||||
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
|
||||
{
|
||||
return;
|
||||
}
|
||||
Polygons skirt = wireFrame.bottom_outline.offset(100000+5000).offset(-100000);
|
||||
PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN));
|
||||
order.addPolygons(skirt);
|
||||
order.optimize();
|
||||
|
||||
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
|
||||
{
|
||||
unsigned int poly_idx = order.polyOrder[poly_order_idx];
|
||||
PolygonRef poly = skirt[poly_idx];
|
||||
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
|
||||
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
|
||||
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Wireframe2gcode::finalize()
|
||||
{
|
||||
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
|
||||
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
|
||||
gcode.writeTemperatureCommand(e, 0, false);
|
||||
}
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef WIREFRAME2GCODE_H
|
||||
#define WIREFRAME2GCODE_H
|
||||
|
||||
|
||||
#include <functional> // passing function pointer or lambda as argument to a function
|
||||
|
||||
#include "utils/NoCopy.h"
|
||||
|
||||
#include "weaveDataStorage.h"
|
||||
#include "commandSocket.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "MeshGroup.h"
|
||||
#include "slicer.h"
|
||||
|
||||
#include "utils/polygon.h"
|
||||
#include "Weaver.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
/*!
|
||||
* Export class for exporting wireframe print gcode / weaver gcode / wireprint gcode.
|
||||
*/
|
||||
class Wireframe2gcode : public SettingsMessenger, NoCopy
|
||||
{
|
||||
private:
|
||||
static const int STRATEGY_COMPENSATE = 0;
|
||||
static const int STRATEGY_KNOT = 1;
|
||||
static const int STRATEGY_RETRACT = 2;
|
||||
|
||||
int initial_layer_thickness;
|
||||
int filament_diameter;
|
||||
int extrusionWidth;
|
||||
double flowConnection;
|
||||
double flowFlat;
|
||||
double extrusion_per_mm_connection;
|
||||
double extrusion_per_mm_flat;
|
||||
int nozzle_outer_diameter;
|
||||
int nozzle_head_distance;
|
||||
double nozzle_expansion_angle;
|
||||
int nozzle_clearance;
|
||||
int nozzle_top_diameter;
|
||||
double moveSpeed;
|
||||
double speedBottom;
|
||||
double speedUp;
|
||||
double speedDown;
|
||||
double speedFlat;
|
||||
int connectionHeight;
|
||||
int roof_inset;
|
||||
double flat_delay;
|
||||
double bottom_delay;
|
||||
double top_delay;
|
||||
int up_dist_half_speed;
|
||||
int top_jump_dist;
|
||||
int fall_down;
|
||||
int drag_along;
|
||||
int strategy;
|
||||
double go_back_to_last_top;
|
||||
int straight_first_when_going_down;
|
||||
int roof_fall_down;
|
||||
int roof_drag_along;
|
||||
double roof_outer_delay;
|
||||
|
||||
RetractionConfig standard_retraction_config; //!< The standard retraction settings used for moves between parts etc.
|
||||
|
||||
public:
|
||||
GCodeExport& gcode; //!< Where the result is 'stored'
|
||||
|
||||
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
|
||||
|
||||
void writeGCode();
|
||||
|
||||
|
||||
private:
|
||||
WireFrame& wireFrame;
|
||||
|
||||
/*!
|
||||
* Startup gcode: nozzle temp up, retraction settings, bed temp
|
||||
*/
|
||||
void processStartingCode();
|
||||
|
||||
/*!
|
||||
* Lay down a skirt
|
||||
*/
|
||||
void processSkirt();
|
||||
|
||||
/*!
|
||||
* End gcode: nozzle temp down
|
||||
*/
|
||||
void finalize();
|
||||
|
||||
void writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& outlines
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
|
||||
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode for a diagonally down movement of a connection.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of an upward move of a connection, which does a couple of small moves at the top.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of an upward move of a connection, which does a retract at the top.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void strategy_retract(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of an upward move of a connection, which goes Wireframe2gcode::fall_down further up
|
||||
* and Wireframe2gcode::drag_along back from the direction it will go to next.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void strategy_compensate(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function writing the gcode of a segment in the connection between two layers.
|
||||
*
|
||||
* \param layer The layer in which the segment is
|
||||
* \param part The part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Function for writing the gcode of a segment in the connection between two roof insets / floor outsets.
|
||||
*
|
||||
* \param inset The inset in which the segment is
|
||||
* \param part the part in which the segment is
|
||||
* \param segment_idx The index of the segment in the \p part
|
||||
*/
|
||||
void handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx);
|
||||
|
||||
/*!
|
||||
* Write a move action to gcode, inserting a retraction if neccesary.
|
||||
*
|
||||
* \param to The 3D destination of the move
|
||||
*/
|
||||
void writeMoveWithRetract(Point3 to);
|
||||
|
||||
/*!
|
||||
* Write a move action to gcode, inserting a retraction if neccesary.
|
||||
*
|
||||
* \param to The 2D destination of the move
|
||||
*/
|
||||
void writeMoveWithRetract(Point to);
|
||||
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//WIREFRAME2GCODE_H
|
||||
+61
-59
@@ -1,59 +1,61 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef BRIDGE_H
|
||||
#define BRIDGE_H
|
||||
|
||||
int bridgeAngle(SliceLayerPart* part, SliceLayer* prevLayer)
|
||||
{
|
||||
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
|
||||
// This gives us the islands that the layer rests on.
|
||||
ClipperLib::Clipper bridgeClip;
|
||||
bridgeClip.AddPolygon(part->outline[0], ClipperLib::ptSubject);
|
||||
|
||||
for(unsigned int n=0; n<prevLayer->parts.size(); n++)
|
||||
{
|
||||
if (!part->boundaryBox.hit(prevLayer->parts[n].boundaryBox)) continue;
|
||||
|
||||
bridgeClip.AddPolygon(prevLayer->parts[n].outline[0], ClipperLib::ptClip);
|
||||
}
|
||||
|
||||
Polygons islands;
|
||||
bridgeClip.Execute(ClipperLib::ctIntersection, islands);
|
||||
if (islands.size() > 5)
|
||||
return -1;
|
||||
|
||||
//Next find the 2 largest islands that we rest on.
|
||||
double area1 = 0;
|
||||
double area2 = 0;
|
||||
int idx1 = -1;
|
||||
int idx2 = -1;
|
||||
for(unsigned int n=0; n<islands.size(); n++)
|
||||
{
|
||||
double area = fabs(Area(islands[n]));
|
||||
if (area > area1)
|
||||
{
|
||||
if (area1 > area2)
|
||||
{
|
||||
area2 = area1;
|
||||
idx2 = idx1;
|
||||
}
|
||||
area1 = area;
|
||||
idx1 = n;
|
||||
}else if (area > area2)
|
||||
{
|
||||
area2 = area;
|
||||
idx2 = n;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx1 < 0 || idx2 < 0)
|
||||
return -1;
|
||||
|
||||
Point center1 = centerOfMass(islands[idx1]);
|
||||
Point center2 = centerOfMass(islands[idx2]);
|
||||
|
||||
double angle = atan2(center2.X - center1.X, center2.Y - center1.Y) / M_PI * 180;
|
||||
if (angle < 0) angle += 360;
|
||||
return angle;
|
||||
}
|
||||
|
||||
#endif//BRIDGE_H
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include "bridge.h"
|
||||
|
||||
#include "sliceDataStorage.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
int bridgeAngle(Polygons outline, SliceLayer* prevLayer)
|
||||
{
|
||||
AABB boundaryBox(outline);
|
||||
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
|
||||
// This gives us the islands that the layer rests on.
|
||||
Polygons islands;
|
||||
for(auto prevLayerPart : prevLayer->parts)
|
||||
{
|
||||
if (!boundaryBox.hit(prevLayerPart.boundaryBox))
|
||||
continue;
|
||||
|
||||
islands.add(outline.intersection(prevLayerPart.outline));
|
||||
}
|
||||
if (islands.size() > 5 || islands.size() < 1)
|
||||
return -1;
|
||||
|
||||
//Next find the 2 largest islands that we rest on.
|
||||
double area1 = 0;
|
||||
double area2 = 0;
|
||||
int idx1 = -1;
|
||||
int idx2 = -1;
|
||||
for(unsigned int n=0; n<islands.size(); n++)
|
||||
{
|
||||
//Skip internal holes
|
||||
if (!islands[n].orientation())
|
||||
continue;
|
||||
double area = fabs(islands[n].area());
|
||||
if (area > area1)
|
||||
{
|
||||
if (area1 > area2)
|
||||
{
|
||||
area2 = area1;
|
||||
idx2 = idx1;
|
||||
}
|
||||
area1 = area;
|
||||
idx1 = n;
|
||||
}else if (area > area2)
|
||||
{
|
||||
area2 = area;
|
||||
idx2 = n;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx1 < 0 || idx2 < 0)
|
||||
return -1;
|
||||
|
||||
Point center1 = islands[idx1].centerOfMass();
|
||||
Point center2 = islands[idx2].centerOfMass();
|
||||
|
||||
return angle(center2 - center1);
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef BRIDGE_H
|
||||
#define BRIDGE_H
|
||||
|
||||
namespace cura {
|
||||
class Polygons;
|
||||
class SliceLayer;
|
||||
|
||||
int bridgeAngle(Polygons outline, SliceLayer* prevLayer);
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//BRIDGE_H
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#include "comb.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "utils/polygonUtils.h"
|
||||
#include "sliceDataStorage.h"
|
||||
#include "utils/SVG.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
// boundary_outside is only computed when it's needed!
|
||||
Polygons* Comb::getBoundaryOutside()
|
||||
{
|
||||
if (!boundary_outside)
|
||||
{
|
||||
boundary_outside = new Polygons();
|
||||
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
|
||||
}
|
||||
return boundary_outside;
|
||||
}
|
||||
|
||||
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
|
||||
: storage(storage)
|
||||
, layer_nr(layer_nr)
|
||||
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
|
||||
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
|
||||
, offset_from_outlines_outside(travel_avoid_distance)
|
||||
, avoid_other_parts(travel_avoid_other_parts)
|
||||
// , boundary_inside( boundary.offset(-offset_from_outlines) ) // TODO: make inside boundary configurable?
|
||||
, boundary_inside( comb_boundary_inside )
|
||||
, boundary_outside(nullptr)
|
||||
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
|
||||
{
|
||||
}
|
||||
|
||||
Comb::~Comb()
|
||||
{
|
||||
if (boundary_outside)
|
||||
delete boundary_outside;
|
||||
}
|
||||
|
||||
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Move start and end point inside the comb boundary
|
||||
unsigned int start_inside_poly = NO_INDEX;
|
||||
if (startInside)
|
||||
{
|
||||
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
|
||||
if (!boundary_inside.inside(start_inside_poly) || start_inside_poly == NO_INDEX)
|
||||
{
|
||||
if (start_inside_poly != NO_INDEX)
|
||||
{ // if not yet inside because of overshoot, try again
|
||||
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
|
||||
}
|
||||
if (start_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract.
|
||||
{
|
||||
startInside = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int end_inside_poly = NO_INDEX;
|
||||
if (endInside)
|
||||
{
|
||||
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
|
||||
if (!boundary_inside.inside(endPoint) || end_inside_poly == NO_INDEX)
|
||||
{
|
||||
if (end_inside_poly != NO_INDEX)
|
||||
{ // if not yet inside because of overshoot, try again
|
||||
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
|
||||
}
|
||||
if (end_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract.
|
||||
{
|
||||
endInside = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int start_part_boundary_poly_idx;
|
||||
unsigned int end_part_boundary_poly_idx;
|
||||
unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx);
|
||||
unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx);
|
||||
|
||||
if (startInside && endInside && start_part_idx == end_part_idx)
|
||||
{ // normal combing within part
|
||||
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
|
||||
Point middle_from;
|
||||
Point middle_to;
|
||||
Point inside_middle_from;
|
||||
Point inside_middle_to;
|
||||
|
||||
if (startInside && endInside)
|
||||
{
|
||||
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]);
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
|
||||
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
|
||||
middle_from = middle_from_cp.location;
|
||||
inside_middle_from = middle_from_cp.location;
|
||||
middle_to = middle_to_cp.location;
|
||||
inside_middle_to = middle_to_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored); //Also move the intermediary waypoint inside if it isn't yet.
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
else if(!startInside && !endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
inside_middle_from = startPoint;
|
||||
middle_to = endPoint;
|
||||
inside_middle_to = endPoint;
|
||||
}
|
||||
else if(!startInside && endInside)
|
||||
{
|
||||
middle_from = startPoint;
|
||||
inside_middle_from = startPoint;
|
||||
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from,boundary_inside[end_part_boundary_poly_idx]);
|
||||
middle_to = middle_to_cp.location;
|
||||
inside_middle_to = middle_to_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
else if(startInside && !endInside)
|
||||
{
|
||||
middle_to = endPoint;
|
||||
inside_middle_to = endPoint;
|
||||
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to,boundary_inside[start_part_boundary_poly_idx]);
|
||||
middle_from = middle_from_cp.location;
|
||||
inside_middle_from = middle_from_cp.location;
|
||||
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
if (startInside)
|
||||
{
|
||||
// start to boundary
|
||||
PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part_begin, startPoint, inside_middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
// throught air from boundary to boundary
|
||||
if (avoid_other_parts)
|
||||
{
|
||||
Polygons& middle = *getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
|
||||
Point from_outside = middle_from;
|
||||
if (startInside || middle.inside(from_outside, true))
|
||||
{ // move outside
|
||||
PolygonUtils::moveInside(middle, from_outside, -offset_extra_start_end, max_moveInside_distance2);
|
||||
}
|
||||
Point to_outside = middle_to;
|
||||
if (endInside || middle.inside(to_outside, true))
|
||||
{ // move outside
|
||||
PolygonUtils::moveInside(middle, to_outside, -offset_extra_start_end, max_moveInside_distance2);
|
||||
}
|
||||
combPaths.emplace_back();
|
||||
combPaths.back().throughAir = true;
|
||||
if ( vSize(inside_middle_from - inside_middle_to) < vSize(inside_middle_from - from_outside) + vSize(inside_middle_to - to_outside) )
|
||||
{ // via outside is a detour
|
||||
combPaths.back().push_back(inside_middle_from);
|
||||
combPaths.back().push_back(inside_middle_to);
|
||||
}
|
||||
else
|
||||
{
|
||||
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // directly through air (not avoiding other parts)
|
||||
combPaths.emplace_back();
|
||||
combPaths.back().throughAir = true;
|
||||
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
|
||||
combPaths.back().push_back(inside_middle_from);
|
||||
combPaths.back().push_back(inside_middle_to);
|
||||
}
|
||||
|
||||
if (endInside)
|
||||
{
|
||||
// boundary to end
|
||||
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
|
||||
combPaths.emplace_back();
|
||||
LinePolygonsCrossings::comb(part_end, inside_middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void LinePolygonsCrossings::calcScanlineCrossings()
|
||||
{
|
||||
|
||||
min_crossing_idx = NO_INDEX;
|
||||
max_crossing_idx = NO_INDEX;
|
||||
|
||||
for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++)
|
||||
{
|
||||
PolyCrossings minMax(poly_idx);
|
||||
PolygonRef poly = boundary[poly_idx];
|
||||
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
|
||||
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
|
||||
{
|
||||
Point p1 = transformation_matrix.apply(poly[point_idx]);
|
||||
if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
|
||||
{
|
||||
if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
|
||||
{
|
||||
p0 = p1;
|
||||
continue;
|
||||
}
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
|
||||
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
|
||||
{
|
||||
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
|
||||
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
|
||||
{
|
||||
minMax.min.x = x;
|
||||
minMax.min.point_idx = point_idx;
|
||||
}
|
||||
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
|
||||
{
|
||||
minMax.max.x = x;
|
||||
minMax.max.point_idx = point_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
if (minMax.min.point_idx != NO_INDEX)
|
||||
{ // then also max.point_idx != -1
|
||||
if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); }
|
||||
if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); }
|
||||
crossings.push_back(minMax);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
|
||||
{
|
||||
Point diff = endPoint - startPoint;
|
||||
|
||||
transformation_matrix = PointMatrix(diff);
|
||||
transformed_startPoint = transformation_matrix.apply(startPoint);
|
||||
transformed_endPoint = transformation_matrix.apply(endPoint);
|
||||
|
||||
for(PolygonRef poly : boundary)
|
||||
{
|
||||
Point p0 = transformation_matrix.apply(poly.back());
|
||||
for(Point p1_ : poly)
|
||||
{
|
||||
Point p1 = transformation_matrix.apply(p1_);
|
||||
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
|
||||
{
|
||||
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
|
||||
|
||||
if (x > transformed_startPoint.X && x < transformed_endPoint.X)
|
||||
return true;
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
|
||||
{
|
||||
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
|
||||
{
|
||||
//We're not crossing any boundaries. So skip the comb generation.
|
||||
combPath.push_back(startPoint);
|
||||
combPath.push_back(endPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
calcScanlineCrossings();
|
||||
|
||||
CombPath basicPath;
|
||||
getBasicCombingPath(basicPath);
|
||||
optimizePath(basicPath, combPath);
|
||||
}
|
||||
|
||||
|
||||
void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
|
||||
{
|
||||
for (PolyCrossings* crossing = getNextPolygonAlongScanline(transformed_startPoint.X)
|
||||
; crossing != nullptr
|
||||
; crossing = getNextPolygonAlongScanline(crossing->max.x))
|
||||
{
|
||||
getBasicCombingPath(*crossing, combPath);
|
||||
}
|
||||
combPath.push_back(endPoint);
|
||||
}
|
||||
|
||||
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
|
||||
{
|
||||
PolygonRef poly = boundary[polyCrossings.poly_idx];
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
|
||||
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
|
||||
< poly.size() / 2 )
|
||||
{ // follow the path in the same direction as the winding order of the boundary polygon
|
||||
for(unsigned int point_idx = polyCrossings.min.point_idx
|
||||
; point_idx != polyCrossings.max.point_idx
|
||||
; point_idx = (point_idx < poly.size() - 1) ? (point_idx + 1) : (0))
|
||||
{
|
||||
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // follow the path in the opposite direction of the winding order of the boundary polygon
|
||||
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
|
||||
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
|
||||
|
||||
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
|
||||
{
|
||||
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
|
||||
}
|
||||
}
|
||||
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlongScanline(int64_t x)
|
||||
{
|
||||
PolyCrossings* ret = nullptr;
|
||||
for(PolyCrossings& crossing : crossings)
|
||||
{
|
||||
if (crossing.min.x > x && (ret == nullptr || crossing.min.x < ret->min.x) )
|
||||
{
|
||||
ret = &crossing;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
|
||||
{
|
||||
optimized_comb_path.push_back(startPoint);
|
||||
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
|
||||
{
|
||||
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Point& current_point = optimized_comb_path.back();
|
||||
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
|
||||
{
|
||||
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx - 1]))
|
||||
{
|
||||
comb_path.cross_boundary = true;
|
||||
}
|
||||
optimized_comb_path.push_back(comb_path[point_idx - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// : dont add the newest point
|
||||
|
||||
// TODO: add the below extra optimization? (+/- 7% extra computation time, +/- 2% faster print for Dual_extrusion_support_generation.stl)
|
||||
while (optimized_comb_path.size() > 1)
|
||||
{
|
||||
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
optimized_comb_path.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
optimized_comb_path.push_back(comb_path.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
|
||||
#ifndef COMB_H
|
||||
#define COMB_H
|
||||
|
||||
#include "utils/polygon.h"
|
||||
|
||||
namespace cura
|
||||
{
|
||||
|
||||
struct CombPath : public std::vector<Point> //!< A single path either inside or outise the parts
|
||||
{
|
||||
bool throughAir = false; //!< Whether the path is one which moves through air.
|
||||
bool cross_boundary = false; //!< Whether the path crosses a boundary.
|
||||
};
|
||||
struct CombPaths : public std::vector<CombPath> //!< A list of paths alternating between inside a part and outside a part
|
||||
{
|
||||
};
|
||||
|
||||
/*!
|
||||
* Class for generating a combing move action from point a to point b and avoiding collision with other parts when moving through air.
|
||||
* See LinePolygonsCrossings::comb.
|
||||
*
|
||||
* The general implementation is by rotating everything such that the the line segment from a to b is aligned with the x-axis.
|
||||
* We call the line on which a and b lie the 'scanline'.
|
||||
*
|
||||
* The basic path is generated by following the scanline until it hits a polygon, then follow the polygon until the last point where it hits the scanline,
|
||||
* follow the scanline again, etc.
|
||||
* The path is offsetted from the polygons, so that it doesn't intersect with them.
|
||||
*
|
||||
* Next the basic path is optimized by taking shortcuts where possible. Only shortcuts which skip a single point are considered, in order to reduce computational complexity.
|
||||
*/
|
||||
class LinePolygonsCrossings
|
||||
{
|
||||
private:
|
||||
|
||||
/*!
|
||||
* A Crossing holds data on a single point where a polygon crosses the scanline.
|
||||
*/
|
||||
struct Crossing
|
||||
{
|
||||
int64_t x; //!< x coordinate of crossings between the polygon and the scanline.
|
||||
unsigned int point_idx; //!< The index of the first point of the line segment which crosses the scanline
|
||||
|
||||
/*!
|
||||
* Creates a Crossing with minimal initialization
|
||||
* \param x The x-coordinate in transformed space
|
||||
* \param point_idx The index of the first point of the line segment which crosses the scanline
|
||||
*/
|
||||
Crossing(int64_t x, unsigned int point_idx)
|
||||
: x(x), point_idx(point_idx)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* A PolyCrossings holds data on where a polygon crosses the scanline. Only the Crossing with lowest Crossing::x and highest are recorded.
|
||||
*/
|
||||
struct PolyCrossings
|
||||
{
|
||||
unsigned int poly_idx; //!< The index of the polygon which crosses the scanline
|
||||
Crossing min; //!< The point where the polygon first crosses the scanline.
|
||||
Crossing max; //!< The point where the polygon last crosses the scanline.
|
||||
/*!
|
||||
* Create a PolyCrossings with minimal initialization. PolyCrossings::min and PolyCrossings::max are not yet computed.
|
||||
* \param poly_idx The index of the polygon in LinePolygonsCrossings::boundary
|
||||
*/
|
||||
PolyCrossings(unsigned int poly_idx)
|
||||
: poly_idx(poly_idx)
|
||||
, min(INT64_MAX, NO_INDEX), max(INT64_MIN, NO_INDEX)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* A PolyCrossings list: for every polygon a PolyCrossings.
|
||||
*/
|
||||
struct PartCrossings : public std::vector<PolyCrossings>
|
||||
{
|
||||
//unsigned int part_idx;
|
||||
};
|
||||
|
||||
|
||||
PartCrossings crossings; //!< All crossings of polygons in the LinePolygonsCrossings::boundary with the scanline.
|
||||
unsigned int min_crossing_idx; //!< The index into LinePolygonsCrossings::crossings to the crossing with the minimal PolyCrossings::min crossing of all PolyCrossings's.
|
||||
unsigned int max_crossing_idx; //!< The index into LinePolygonsCrossings::crossings to the crossing with the maximal PolyCrossings::max crossing of all PolyCrossings's.
|
||||
|
||||
Polygons& boundary; //!< The boundary not to cross during combing.
|
||||
Point startPoint; //!< The start point of the scanline.
|
||||
Point endPoint; //!< The end point of the scanline.
|
||||
|
||||
int64_t dist_to_move_boundary_point_outside; //!< The distance used to move outside or inside so that a boundary point doesn't intersect with the boundary anymore. Neccesary due to computational rounding problems. Use negative value for insicde combing.
|
||||
|
||||
PointMatrix transformation_matrix; //!< The transformation which rotates everything such that the scanline is aligned with the x-axis.
|
||||
Point transformed_startPoint; //!< The LinePolygonsCrossings::startPoint as transformed by Comb::transformation_matrix
|
||||
Point transformed_endPoint; //!< The LinePolygonsCrossings::endPoint as transformed by Comb::transformation_matrix
|
||||
|
||||
|
||||
/*!
|
||||
* Check if we are crossing the boundaries, and pre-calculate some values.
|
||||
*
|
||||
* Sets Comb::transformation_matrix, Comb::transformed_startPoint and Comb::transformed_endPoint
|
||||
* \return Whether the line segment from LinePolygonsCrossings::startPoint to LinePolygonsCrossings::endPoint collides with the boundary
|
||||
*/
|
||||
bool lineSegmentCollidesWithBoundary();
|
||||
|
||||
/*!
|
||||
* Calculate Comb::crossings, Comb::min_crossing_idx and Comb::max_crossing_idx.
|
||||
*/
|
||||
void calcScanlineCrossings();
|
||||
|
||||
/*!
|
||||
* Get the basic combing path and optimize it.
|
||||
*
|
||||
* \param combPath Output parameter: the points along the combing path.
|
||||
*/
|
||||
void getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
|
||||
/*!
|
||||
* Get the basic combing path, without shortcuts. The path goes straight toward the endPoint and follows the boundary when it hits it, until it passes the scanline again.
|
||||
*
|
||||
* Walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
|
||||
* to the \p combPath and continue with the next boundary we will cross, until there are no more boundaries to cross.
|
||||
* This gives a path from the start to finish curved around the holes that it encounters.
|
||||
*
|
||||
* \param combPath Output parameter: the points along the combing path.
|
||||
*/
|
||||
void getBasicCombingPath(CombPath& combPath);
|
||||
|
||||
/*!
|
||||
* Get the basic combing path, following a single boundary polygon when it hits it, until it passes the scanline again.
|
||||
*
|
||||
* Find the initial cross point and the exit point. Then add all the points in between
|
||||
* to the \p combPath and continue with the next boundary we will cross, until there are no more boundaries to cross.
|
||||
* This gives a path from the start to finish curved around the polygon that it encounters.
|
||||
*
|
||||
* \param combPath Output parameter: where to add the points along the combing path.
|
||||
*/
|
||||
void getBasicCombingPath(PolyCrossings& crossings, CombPath& combPath);
|
||||
|
||||
/*!
|
||||
* Find the first polygon cutting the scanline after \p x.
|
||||
*
|
||||
* Note that this function only looks at the first segment cutting the scanline (see Comb::minX)!
|
||||
* It doesn't return the next polygon which crosses the scanline, but the first polygon crossing the scanline for the first time.
|
||||
*
|
||||
* \param x The point on the scanline from where to look.
|
||||
* \return The next PolyCrossings fully beyond \p x or one with PolyCrossings::poly_idx set to NO_INDEX if there's none left.
|
||||
*/
|
||||
PolyCrossings* getNextPolygonAlongScanline(int64_t x);
|
||||
|
||||
/*!
|
||||
* Optimize the \p comb_path: skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip some unneeded corners.
|
||||
*
|
||||
* \param comb_path The unoptimized combing path.
|
||||
* \param optimized_comb_path Output parameter: The points of optimized combing path
|
||||
* \return Whether it turns out that the basic comb path already crossed a boundary
|
||||
*/
|
||||
bool optimizePath(CombPath& comb_path, CombPath& optimized_comb_path);
|
||||
|
||||
/*!
|
||||
* Create a LinePolygonsCrossings with minimal initialization.
|
||||
* \param boundary The boundary which not to cross during combing
|
||||
* \param start the starting point
|
||||
* \param end the end point
|
||||
* \param dist_to_move_boundary_point_outside Distance used to move a point from a boundary so that it doesn't intersect with it anymore. (Precision issue)
|
||||
*/
|
||||
LinePolygonsCrossings(Polygons& boundary, Point& start, Point& end, int64_t dist_to_move_boundary_point_outside)
|
||||
: boundary(boundary), startPoint(start), endPoint(end), dist_to_move_boundary_point_outside(dist_to_move_boundary_point_outside)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
* The main function of this class: calculate one combing path within the boundary.
|
||||
* \param boundary The polygons to follow when calculating the basic combing path
|
||||
* \param startPoint From where to start the combing move.
|
||||
* \param endPoint Where to end the combing move.
|
||||
* \param combPath Output parameter: the combing path generated.
|
||||
*/
|
||||
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored = MM2INT(1.5))
|
||||
{
|
||||
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
|
||||
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
|
||||
};
|
||||
};
|
||||
|
||||
class SliceDataStorage;
|
||||
|
||||
/*!
|
||||
* Class for generating a full combing actions from a travel move from a start point to an end point.
|
||||
* A single Comb object is used for each layer.
|
||||
*
|
||||
* Comb::calc is the main function of this class.
|
||||
*
|
||||
* Typical output: A combing path to the boundary of the polygon + a move through air avoiding other parts in the layer + a combing path from the boundary of the ending polygon to the end point.
|
||||
* Each of these three is a CombPath; the first and last are within Comb::boundary_inside while the middle is outside of Comb::boundary_outside.
|
||||
* Between these there is a little gap where the nozzle crosses the boundary of an object approximately perpendicular to its boundary.
|
||||
*
|
||||
* As an optimization, the combing paths inside are calculated on specifically those PolygonsParts within which to comb, while the coundary_outside isn't split into outside parts,
|
||||
* because generally there is only one outside part; encapsulated holes occur less often.
|
||||
*/
|
||||
class Comb
|
||||
{
|
||||
friend class LinePolygonsCrossings;
|
||||
private:
|
||||
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
|
||||
int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
|
||||
|
||||
int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
|
||||
int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
|
||||
int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
|
||||
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
|
||||
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
|
||||
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
|
||||
|
||||
bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
|
||||
|
||||
Polygons& boundary_inside; //!< The boundary within which to comb.
|
||||
Polygons* boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
|
||||
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
|
||||
|
||||
/*!
|
||||
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
|
||||
*/
|
||||
Polygons* getBoundaryOutside();
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Initializes the combing areas for every mesh in the layer (not support)
|
||||
* \param storage Where the layer polygon data is stored
|
||||
* \param layer_nr The number of the layer for which to generate the combing areas.
|
||||
* \param comb_boundary_inside The comb boundary within which to comb within layer parts.
|
||||
* \param offset_from_outlines The offset from the outline polygon, to create the combing boundary in case there is no second wall.
|
||||
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
|
||||
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
|
||||
*/
|
||||
Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
|
||||
|
||||
~Comb();
|
||||
|
||||
/*!
|
||||
* Calculate the comb paths (if any) - one for each polygon combed alternated with travel paths
|
||||
*
|
||||
* \param startPoint Where to start moving from
|
||||
* \param endPoint Where to move to
|
||||
* \param combPoints Output parameter: The points along the combing path, excluding the \p startPoint (?) and \p endPoint
|
||||
* \param startInside Whether we want to start inside the comb boundary
|
||||
* \param endInside Whether we want to end up inside the comb boundary
|
||||
* \return Whether combing has succeeded; otherwise a retraction is needed.
|
||||
*/
|
||||
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//COMB_H
|
||||
@@ -0,0 +1,413 @@
|
||||
#include "utils/logoutput.h"
|
||||
#include "commandSocket.h"
|
||||
#include "FffProcessor.h"
|
||||
#include "Progress.h"
|
||||
|
||||
#include <thread>
|
||||
#include <cinttypes>
|
||||
|
||||
#include <Arcus/Socket.h>
|
||||
#include <Arcus/SocketListener.h>
|
||||
#include <Arcus/Error.h>
|
||||
|
||||
#include <string> // stoi
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(x)
|
||||
// std::cerr << x;
|
||||
|
||||
namespace cura {
|
||||
|
||||
#define BYTES_PER_FLOAT 4
|
||||
#define FLOATS_PER_VECTOR 3
|
||||
#define VECTORS_PER_FACE 3
|
||||
|
||||
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
|
||||
|
||||
class Listener : public Arcus::SocketListener
|
||||
{
|
||||
public:
|
||||
void stateChanged(Arcus::SocketState::SocketState newState) override
|
||||
{
|
||||
}
|
||||
|
||||
void messageReceived() override
|
||||
{
|
||||
}
|
||||
|
||||
void error(const Arcus::Error & error) override
|
||||
{
|
||||
if(error.getErrorCode() == Arcus::ErrorCode::Debug)
|
||||
{
|
||||
log("%s\n", error.toString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("%s\n", error.toString().c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CommandSocket::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: socket(nullptr)
|
||||
, object_count(0)
|
||||
, sliced_objects(0)
|
||||
, current_layer_count(0)
|
||||
, current_layer_offset(0)
|
||||
{ }
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
|
||||
|
||||
Arcus::Socket* socket;
|
||||
|
||||
// Number of objects that need to be sliced
|
||||
int object_count;
|
||||
|
||||
// Number of sliced objects for this sliced object list
|
||||
int sliced_objects;
|
||||
|
||||
// Number of layers sent to the front end so far
|
||||
// Used for incrementing the current layer in one at a time mode
|
||||
int current_layer_count;
|
||||
int current_layer_offset;
|
||||
|
||||
std::string temp_gcode_file;
|
||||
std::ostringstream gcode_output_stream;
|
||||
|
||||
// Print object that olds one or more meshes that need to be sliced.
|
||||
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
|
||||
|
||||
std::unordered_map<int, std::shared_ptr<cura::proto::Layer>> sliced_layers;
|
||||
};
|
||||
|
||||
CommandSocket::CommandSocket()
|
||||
: private_data(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
CommandSocket* CommandSocket::getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void CommandSocket::instantiate()
|
||||
{
|
||||
instance = new CommandSocket();
|
||||
}
|
||||
|
||||
bool CommandSocket::isInstantiated()
|
||||
{
|
||||
return instance != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void CommandSocket::connect(const std::string& ip, int port)
|
||||
{
|
||||
private_data->socket = new Arcus::Socket();
|
||||
private_data->socket->addListener(new Listener());
|
||||
|
||||
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
|
||||
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
|
||||
|
||||
private_data->socket->connect(ip, port);
|
||||
|
||||
log("Connecting to %s:%i\n", ip.c_str(), port);
|
||||
|
||||
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
log("Connected to %s:%i\n", ip.c_str(), port);
|
||||
|
||||
bool slice_another_time = true;
|
||||
|
||||
// Start & continue listening as long as socket is not closed and there is no error.
|
||||
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != Arcus::SocketState::Error && slice_another_time)
|
||||
{
|
||||
// Actually start handling messages.
|
||||
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
|
||||
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
|
||||
if(setting_list)
|
||||
{
|
||||
handleSettingList(setting_list);
|
||||
}
|
||||
|
||||
/*cura::proto::ObjectList* object_list = dynamic_cast<cura::proto::ObjectList*>(message.get());
|
||||
if(object_list)
|
||||
{
|
||||
handleObjectList(object_list);
|
||||
}*/
|
||||
|
||||
cura::proto::Slice* slice = dynamic_cast<cura::proto::Slice*>(message.get());
|
||||
if(slice)
|
||||
{
|
||||
// Reset object counts
|
||||
private_data->object_count = 0;
|
||||
for(auto object : slice->object_lists())
|
||||
{
|
||||
handleObjectList(&object);
|
||||
}
|
||||
}
|
||||
|
||||
//If there is an object to slice, do so.
|
||||
if(private_data->objects_to_slice.size())
|
||||
{
|
||||
FffProcessor::getInstance()->resetFileNumber();
|
||||
for(auto object : private_data->objects_to_slice)
|
||||
{
|
||||
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
|
||||
{
|
||||
logError("Slicing mesh group failed!");
|
||||
}
|
||||
}
|
||||
private_data->objects_to_slice.clear();
|
||||
FffProcessor::getInstance()->finalize();
|
||||
flushGcode();
|
||||
sendPrintTime();
|
||||
sendFinishedSlicing();
|
||||
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
|
||||
//TODO: Support all-at-once/one-at-a-time printing
|
||||
//private_data->processor->processModel(private_data->object_to_slice.get());
|
||||
//private_data->object_to_slice.reset();
|
||||
//private_data->processor->resetFileNumber();
|
||||
|
||||
//sendPrintTime();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
log("Closing connection\n");
|
||||
private_data->socket->close();
|
||||
}
|
||||
|
||||
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
|
||||
{
|
||||
if(list->objects_size() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FMatrix3x3 matrix;
|
||||
//private_data->object_count = 0;
|
||||
//private_data->object_ids.clear();
|
||||
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
|
||||
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
|
||||
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
meshgroup->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
|
||||
{ // initialize remaining extruder trains and load the defaults
|
||||
meshgroup->createExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
|
||||
}
|
||||
|
||||
for(auto object : list->objects())
|
||||
{
|
||||
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
|
||||
int face_count = object.vertices().size() / bytes_per_face;
|
||||
|
||||
if(face_count <= 0)
|
||||
{
|
||||
logWarning("Got an empty mesh, ignoring it!");
|
||||
continue;
|
||||
}
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
|
||||
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
|
||||
for(auto setting : object.settings())
|
||||
{
|
||||
if (setting.name() == "extruder_nr")
|
||||
{
|
||||
extruder_train_nr = std::stoi(setting.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
|
||||
|
||||
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
|
||||
Mesh& mesh = meshgroup->meshes.back();
|
||||
|
||||
for(int i = 0; i < face_count; ++i)
|
||||
{
|
||||
//TODO: Apply matrix
|
||||
std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face);
|
||||
const FPoint3* float_vertices = reinterpret_cast<const FPoint3*>(data.data());
|
||||
|
||||
Point3 verts[3];
|
||||
verts[0] = matrix.apply(float_vertices[0]);
|
||||
verts[1] = matrix.apply(float_vertices[1]);
|
||||
verts[2] = matrix.apply(float_vertices[2]);
|
||||
mesh.addFace(verts[0], verts[1], verts[2]);
|
||||
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" facet normal -1 0 0\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" outer loop\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[0].x) <<" " << INT2MM(verts[0].y) <<" " << INT2MM(verts[0].z) << "\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[1].x) <<" " << INT2MM(verts[1].y) <<" " << INT2MM(verts[1].z) << "\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[2].x) <<" " << INT2MM(verts[2].y) <<" " << INT2MM(verts[2].z) << "\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endloop\n");
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n");
|
||||
}
|
||||
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n");
|
||||
for(auto setting : object.settings())
|
||||
{
|
||||
mesh.setSetting(setting.name(), setting.value());
|
||||
}
|
||||
|
||||
mesh.finish();
|
||||
}
|
||||
|
||||
private_data->object_count++;
|
||||
meshgroup->finalize();
|
||||
}
|
||||
|
||||
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
|
||||
{
|
||||
for(auto setting : list->settings())
|
||||
{
|
||||
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
|
||||
{
|
||||
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
|
||||
layer->set_height(z);
|
||||
layer->set_thickness(height);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
|
||||
{
|
||||
if (polygons.size() == 0)
|
||||
return;
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
|
||||
|
||||
for(unsigned int i = 0; i < polygons.size(); ++i)
|
||||
{
|
||||
cura::proto::Polygon* p = proto_layer->add_polygons();
|
||||
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
|
||||
std::string polydata;
|
||||
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
|
||||
p->set_points(polydata);
|
||||
p->set_line_width(line_width);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgress(float amount)
|
||||
{
|
||||
auto message = std::make_shared<cura::proto::Progress>();
|
||||
amount /= private_data->object_count;
|
||||
amount += private_data->sliced_objects * (1. / private_data->object_count);
|
||||
message->set_amount(amount);
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendProgressStage(Progress::Stage stage)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintTime()
|
||||
{
|
||||
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
|
||||
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
|
||||
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
|
||||
{
|
||||
// socket.sendInt32(CMD_OBJECT_PRINT_MATERIAL);
|
||||
// socket.sendInt32(12);
|
||||
// socket.sendInt32(index);
|
||||
// socket.sendInt32(extruder_nr);
|
||||
// socket.sendFloat32(print_time);
|
||||
}
|
||||
|
||||
void CommandSocket::sendLayerData()
|
||||
{
|
||||
private_data->sliced_objects++;
|
||||
private_data->current_layer_offset = private_data->current_layer_count;
|
||||
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
|
||||
|
||||
if(private_data->sliced_objects >= private_data->object_count)
|
||||
{
|
||||
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
|
||||
{
|
||||
private_data->socket->sendMessage(entry.second); //Send the actual layers.
|
||||
}
|
||||
private_data->sliced_objects = 0;
|
||||
private_data->current_layer_count = 0;
|
||||
private_data->current_layer_offset = 0;
|
||||
private_data->sliced_layers.clear();
|
||||
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandSocket::sendFinishedSlicing()
|
||||
{
|
||||
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
|
||||
private_data->socket->sendMessage(done_message);
|
||||
}
|
||||
|
||||
void CommandSocket::beginGCode()
|
||||
{
|
||||
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
|
||||
}
|
||||
|
||||
void CommandSocket::flushGcode()
|
||||
{
|
||||
auto message = std::make_shared<cura::proto::GCodeLayer>();
|
||||
message->set_data(private_data->gcode_output_stream.str());
|
||||
private_data->socket->sendMessage(message);
|
||||
|
||||
private_data->gcode_output_stream.str("");
|
||||
}
|
||||
|
||||
void CommandSocket::sendGCodePrefix(std::string prefix)
|
||||
{
|
||||
auto message = std::make_shared<cura::proto::GCodePrefix>();
|
||||
message->set_data(prefix);
|
||||
private_data->socket->sendMessage(message);
|
||||
}
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
|
||||
{
|
||||
id += current_layer_offset;
|
||||
|
||||
auto itr = sliced_layers.find(id);
|
||||
|
||||
std::shared_ptr<cura::proto::Layer> layer;
|
||||
if(itr != sliced_layers.end())
|
||||
{
|
||||
layer = itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
layer = std::make_shared<cura::proto::Layer>();
|
||||
layer->set_id(id);
|
||||
current_layer_count++;
|
||||
sliced_layers[id] = layer;
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
}//namespace cura
|
||||
@@ -0,0 +1,117 @@
|
||||
#ifndef COMMAND_SOCKET_H
|
||||
#define COMMAND_SOCKET_H
|
||||
|
||||
#include "utils/socket.h"
|
||||
#include "utils/polygon.h"
|
||||
#include "settings.h"
|
||||
#include "Progress.h"
|
||||
#include "PrintFeature.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Cura.pb.h"
|
||||
|
||||
namespace cura {
|
||||
|
||||
|
||||
class CommandSocket
|
||||
{
|
||||
private:
|
||||
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
|
||||
|
||||
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
|
||||
|
||||
public:
|
||||
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
|
||||
|
||||
static void instantiate(); //!< Instantiate the CommandSocket.
|
||||
|
||||
static bool isInstantiated(); //!< Check whether the singleton is instantiated
|
||||
|
||||
/*!
|
||||
* Connect with the GUI
|
||||
* This creates and initialises the arcus socket and then continues listening for messages.
|
||||
* \param ip string containing the ip to connect with
|
||||
* \param port int of the port to connect with.
|
||||
*/
|
||||
void connect(const std::string& ip, int port);
|
||||
|
||||
/*!
|
||||
* Handler for ObjectList message.
|
||||
* Loads all objects from the message and starts the slicing process
|
||||
*/
|
||||
void handleObjectList(cura::proto::ObjectList* list);
|
||||
|
||||
/*!
|
||||
* Handler for SettingList message.
|
||||
* This simply sets all the settings by using key value pair
|
||||
*/
|
||||
void handleSettingList(cura::proto::SettingList* list);
|
||||
|
||||
/*!
|
||||
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
|
||||
*/
|
||||
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine. This is used for the layerview in the GUI
|
||||
*/
|
||||
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
|
||||
*/
|
||||
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
|
||||
|
||||
/*!
|
||||
* Send progress to GUI
|
||||
*/
|
||||
void sendProgress(float amount);
|
||||
|
||||
/*!
|
||||
* Send the current stage of the process to the GUI (starting, slicing infill, etc)
|
||||
*/
|
||||
void sendProgressStage(Progress::Stage stage);
|
||||
|
||||
/*!
|
||||
* Send time estimate of how long print would take.
|
||||
*/
|
||||
void sendPrintTime();
|
||||
|
||||
/*!
|
||||
* Does nothing at the moment
|
||||
*/
|
||||
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
|
||||
|
||||
/*!
|
||||
* Send the sliced layer data to the GUI.
|
||||
*
|
||||
* The GUI may use this to visualise the g-code, so that the user can
|
||||
* inspect the result of slicing.
|
||||
*/
|
||||
void sendLayerData();
|
||||
|
||||
/*!
|
||||
* \brief Sends a message to indicate that all the slicing is done.
|
||||
*
|
||||
* This should indicate that no more data (g-code, prefix/postfix, metadata
|
||||
* or otherwise) should be sent any more regarding the latest slice job.
|
||||
*/
|
||||
void sendFinishedSlicing();
|
||||
|
||||
void beginGCode();
|
||||
|
||||
/*!
|
||||
* Flush the gcode in gcode_output_stream into a message queued in the socket.
|
||||
*/
|
||||
void flushGcode();
|
||||
void sendGCodePrefix(std::string prefix);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> private_data;
|
||||
};
|
||||
|
||||
}//namespace cura
|
||||
|
||||
#endif//COMMAND_SOCKET_H
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário