Compare commits
1430 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 824d502c37 | |||
| 36ff017c4f | |||
| 3ef9fe142c | |||
| 4bf0c496d8 | |||
| 9bf4bda852 | |||
| fa7f10a6e9 | |||
| 483e45db8a | |||
| e6097f4534 | |||
| 749f56084a | |||
| 2fa4a63f02 | |||
| 9b1249abce | |||
| bbfc0bc77a | |||
| 5a416e51d2 | |||
| 67ba06cd77 | |||
| aa61d681d9 | |||
| c8e6b967e5 | |||
| 819e91aa37 | |||
| 7f835cf7cb | |||
| 43571e6d40 | |||
| dd3666b6d3 | |||
| f98b191616 | |||
| 9fdcf0db74 | |||
| 7abab41f2b | |||
| 45569c0d4d | |||
| ca653c5bec | |||
| 6c0539bac6 | |||
| 65c9a46c8d | |||
| 7bd97558dc | |||
| 6807f53a7e | |||
| ae396ef576 | |||
| 3dd1c500cf | |||
| 59e4129194 | |||
| 54d441b62a | |||
| f8d36363b4 | |||
| c32c7fda58 | |||
| 87b220e747 | |||
| 57a6f4303b | |||
| 407456eac5 | |||
| f118d0cd35 | |||
| 1b0fb95583 | |||
| 0daed24020 | |||
| 92eaa95127 | |||
| a9d22cfb60 | |||
| b433b76a84 | |||
| 5ff0a419dd | |||
| 6b7be5ae12 | |||
| 8d1f44a0c5 | |||
| 0c101e7d88 | |||
| 6e3c218d89 | |||
| fe436cf54f | |||
| efdfec13ed | |||
| 52522fde48 | |||
| 2a4882d92e | |||
| bf98db67a3 | |||
| 9f375aef09 | |||
| 1faa858690 | |||
| 5e7d2fb8ba | |||
| 974c6e5950 | |||
| e53f27c224 | |||
| d98262bc08 | |||
| 945874ca7d | |||
| 4c839837d3 | |||
| f230dee72c | |||
| 11a859e83e | |||
| 9029e37671 | |||
| 2a4b74d0ff | |||
| 0af25a2684 | |||
| f62f4806c3 | |||
| 22496535d1 | |||
| cb8df169c7 | |||
| ac25972873 | |||
| f1f9d807e6 | |||
| 7d34b9bb46 | |||
| ae009cd53f | |||
| dd36d17fa6 | |||
| 9843921503 | |||
| f6feb4b9eb | |||
| 246632abca | |||
| 5da33d7de8 | |||
| 9243de6ce9 | |||
| a5b6c27bec | |||
| 076de033fd | |||
| a1116f8e3b | |||
| d8c4c53567 | |||
| df9217e3b9 | |||
| c5aebf1309 | |||
| dc5ae4ef96 | |||
| d4ecf468b1 | |||
| aa2cee405a | |||
| 5fc02f7f58 | |||
| 3d5ae2fc3b | |||
| eecfdab590 | |||
| e9ef5c852b | |||
| 8594926b67 | |||
| 74a37b7817 | |||
| 13b47a4ee6 | |||
| 0c2d80841c | |||
| d1465ff401 | |||
| ee0a428dbf | |||
| f808a9fae5 | |||
| 932d0da73d | |||
| d9fcfbe80e | |||
| ca3baf3881 | |||
| c37829eafb | |||
| d9a48ac7de | |||
| c684213106 | |||
| 5318352929 | |||
| dd3216489b | |||
| 17db6c01b4 | |||
| 8efd0f00c4 | |||
| b258c9d8c8 | |||
| b0548ffc14 | |||
| 5194e49b4c | |||
| 2083af9257 | |||
| 6cd8b8ff8d | |||
| ca9e47bc4d | |||
| ed8e72c1bc | |||
| 4e5e9986a1 | |||
| 55bb6fda1b | |||
| c50a5bf123 | |||
| 2702dc6654 | |||
| 3fe013cd24 | |||
| fba6802066 | |||
| fa8c1e3967 | |||
| 23b191231c | |||
| aa5d514d57 | |||
| 3fb45764b2 | |||
| fcff639d0c | |||
| 96181b8c48 | |||
| 62290a2a24 | |||
| bfafe91842 | |||
| cbaa8fca56 | |||
| f606f083a4 | |||
| b60007cde9 | |||
| 89e271bd91 | |||
| 3ed50bbf0f | |||
| 0d210843e7 | |||
| 08bed07632 | |||
| c47ab69383 | |||
| eb942352ce | |||
| 00379097cf | |||
| 16b568840d | |||
| c1dae9e9d9 | |||
| 8d8921fd08 | |||
| d04d651e55 | |||
| c771739b1b | |||
| a800f8d4d9 | |||
| 4b5ec3ad07 | |||
| 35c68c8890 | |||
| 5005120f5e | |||
| 7b68e7985f | |||
| 871c7d86c7 | |||
| 79614ffcec | |||
| 6821148e8e | |||
| 3255c5a122 | |||
| edcaab3c56 | |||
| bb9d36eb7a | |||
| 503d741ea6 | |||
| 50984b3bd2 | |||
| 7520c1dced | |||
| bfd468fc3a | |||
| 3ab0078818 | |||
| 1fc587559c | |||
| 4821e04145 | |||
| bb8e62769f | |||
| 15dc61feb5 | |||
| d0d72242cb | |||
| bece5a2620 | |||
| d972702d3e | |||
| d08b33e696 | |||
| da60b47af5 | |||
| 0ac63857c4 | |||
| d08422811e | |||
| d5788fc1e7 | |||
| 6da5bb8822 | |||
| a0a3b42872 | |||
| caf0aa098a | |||
| 55ea8b4a70 | |||
| 25c53f0e52 | |||
| 7c3938f793 | |||
| 20a10fcf7d | |||
| 6ee49f8430 | |||
| 0768c8a658 | |||
| 89b2be4ecb | |||
| 369d510b3e | |||
| d1dbb1bf2a | |||
| ccf726ddcb | |||
| 5b2cd1b8ee | |||
| 16b8d01b88 | |||
| 8d7b07eab3 | |||
| 599d6a2ec8 | |||
| 345f5654a3 | |||
| 77e04c45db | |||
| ad4d7214e4 | |||
| 4051d016b9 | |||
| 8610a82a88 | |||
| 4832d870dd | |||
| ace915fbfb | |||
| c6593423f5 | |||
| f69daa8cfe | |||
| 71246620aa | |||
| 6e3181375c | |||
| e7b1dc0cac | |||
| d78bb56316 | |||
| 648c5ac587 | |||
| df7d28dc99 | |||
| 4d28a49c70 | |||
| ef3e7af329 | |||
| 3221b80aeb | |||
| 13fc936e1f | |||
| 4e5b1aa6e5 | |||
| cb577f0ef3 | |||
| b23dd39e06 | |||
| 7498b3c769 | |||
| 0c0a2c215c | |||
| b682bc8e96 | |||
| 205345a8d3 | |||
| faff9cebe4 | |||
| bcd1bc1009 | |||
| f2ca493a14 | |||
| 54301fa97a | |||
| 20a290792f | |||
| 31f5490517 | |||
| 8554e632bd | |||
| 70d0d786e2 | |||
| 5af5ec7313 | |||
| 7fab2f1c6b | |||
| 461ef20225 | |||
| 4357fde5ea | |||
| f93977b1a4 | |||
| 2e77d0df98 | |||
| fc40b0e0ac | |||
| 85aca2aa67 | |||
| 961d6be9f9 | |||
| 33553eb055 | |||
| fbe8dcaf78 | |||
| b5f54afed2 | |||
| c99fcc3dcd | |||
| 4a7ea82862 | |||
| 3bad6e9b4c | |||
| 96ab1537d3 | |||
| c62714d188 | |||
| 2b6a90766a | |||
| 366d598ff0 | |||
| 84a8039022 | |||
| 8011c9f675 | |||
| d709b53ec7 | |||
| 39c97ecdc5 | |||
| 45c434b5ed | |||
| 312f98e380 | |||
| d093d1ad7e | |||
| 22e8718e85 | |||
| e489787535 | |||
| 73ff286162 | |||
| c75f6f5a9d | |||
| 33d04c3e6b | |||
| 2ede388c15 | |||
| 745189981b | |||
| dd70192de4 | |||
| 5fc2f6cf3b | |||
| 2c6f54579d | |||
| 9bcab45cb8 | |||
| 3b7464e739 | |||
| 11c86b2d30 | |||
| 7a521ef21f | |||
| 7fb1a6a517 | |||
| f20582e528 | |||
| 92e609876d | |||
| 5a251e5702 | |||
| 33cf0e66ec | |||
| 9d75916244 | |||
| 6ed18617d5 | |||
| 2022aebb07 | |||
| b73c38fe69 | |||
| 11322d0d8b | |||
| 82479d48b9 | |||
| a07fd5d3db | |||
| 10ab3ae974 | |||
| b908202c47 | |||
| 61d4c84ef4 | |||
| 68a0baf299 | |||
| aef75cb073 | |||
| c5fe90d135 | |||
| 85e5f2e971 | |||
| 58bf14fc85 | |||
| 0d09975fba | |||
| 06afcedf56 | |||
| c7cc76b2b6 | |||
| 84b108f6d0 | |||
| cb6f0772b7 | |||
| 4263ced69b | |||
| f5c6b06eb6 | |||
| 05ef841d63 | |||
| 140afeb53d | |||
| 639a09bccb | |||
| f105d41e54 | |||
| 140a429e09 | |||
| 28d5e0bb7d | |||
| 3dec7fe394 | |||
| e54eb53665 | |||
| 9b3cf3a818 | |||
| 3d306578f1 | |||
| e615718f1b | |||
| fbbbd40353 | |||
| 0e694a9767 | |||
| 203e351cef | |||
| 74ec895aca | |||
| 3459bcd817 | |||
| d2c0840c43 | |||
| 9534bae4ef | |||
| 2a720a7491 | |||
| 7b31e5f9b8 | |||
| d0be1e7d1d | |||
| 8434741599 | |||
| cfe1ede25a | |||
| ecc891279e | |||
| 750f96cca7 | |||
| 5051e06be0 | |||
| cde041e977 | |||
| 0fdf46fd2e | |||
| c902c3ff35 | |||
| f0b006d3c9 | |||
| 044daf08d4 | |||
| 0b5b8b2cc2 | |||
| fae5c45cc4 | |||
| 3bc8ffda58 | |||
| 589713cb75 | |||
| ca9ce5427d | |||
| 374809d3cf | |||
| e1d45dec5d | |||
| bf60d6482f | |||
| e04858c975 | |||
| e0148ba1a8 | |||
| ee521107d3 | |||
| acbce64be9 | |||
| bb11426274 | |||
| 2249aaafdf | |||
| 172193e706 | |||
| 93d7e53f8b | |||
| 7e05bb0aca | |||
| a1130efa2f | |||
| e903514f70 | |||
| 87f2b0154e | |||
| 0ab42eb008 | |||
| 39e93f40c2 | |||
| 0458c18c13 | |||
| a3fc47a7de | |||
| 715181585a | |||
| ecadf4c8f6 | |||
| 899c8b3634 | |||
| 68b344e7c3 | |||
| 58cf8e471e | |||
| 68b29f63a6 | |||
| 985d43bf4c | |||
| e3239bf312 | |||
| 39cb3733db | |||
| f9ea9cd999 | |||
| 47900b1a80 | |||
| 9488176a2d | |||
| 091ca327ba | |||
| f2af121661 | |||
| fe07fe0a7b | |||
| c4354c86c0 | |||
| 7ea1eebc9e | |||
| 3d73591178 | |||
| 5335159203 | |||
| 7b2be59b7b | |||
| 3eafc67654 | |||
| acf4cb4e5b | |||
| f53b070d79 | |||
| aaaedcab1a | |||
| 02b54e72a6 | |||
| c034d7bf1b | |||
| 8636718c88 | |||
| f11d5b1e63 | |||
| 903fb84af0 | |||
| 92814e71c7 | |||
| 35e24fef2a | |||
| 95a29f9cbe | |||
| 6ba1607bb1 | |||
| 2bbaf9d504 | |||
| 607640ce73 | |||
| 195c6d601c | |||
| c39a6d8ec3 | |||
| 42e88225c5 | |||
| b9b6915855 | |||
| b2852802db | |||
| ced201700e | |||
| 6d737dea7b | |||
| 311ace523d | |||
| d6bd10a937 | |||
| a03ae0be10 | |||
| faa401f0e5 | |||
| 6378f88ba9 | |||
| f5cbb1e341 | |||
| c9c4de37e6 | |||
| 567d3688e8 | |||
| e3a166f04c | |||
| 74001a555b | |||
| 4c6b96e38a | |||
| 1f14a4d4c6 | |||
| 242d8b290d | |||
| 2cfd1838ca | |||
| 642bb9aead | |||
| fc0a12ac36 | |||
| a2bdc4c3ef | |||
| faf905a932 | |||
| 1ba5ad0e90 | |||
| 59ad0d830b | |||
| 1fcd06f903 | |||
| 66e8f6615e | |||
| 10e7cb8eba | |||
| 3c9a7193fd | |||
| 424b98e1a1 | |||
| 57db8095c4 | |||
| c0e68af63f | |||
| f2f647c642 | |||
| a117bbc04b | |||
| abc212b039 | |||
| cb1d32422a | |||
| 0dd970fc6b | |||
| ce1276bcbd | |||
| c2c1c9d738 | |||
| 6d3e8fcb81 | |||
| 7954adedf3 | |||
| fc6dfb8dcb | |||
| 7f1863f3b9 | |||
| 9fb6b8059f | |||
| e34570e307 | |||
| 32d712ce29 | |||
| bc42001b75 | |||
| 914d3cac44 | |||
| 10868ef565 | |||
| 046643f743 | |||
| 1ef4cfb86a | |||
| 9324123b4d | |||
| eddf523d08 | |||
| 37ff1a73ed | |||
| 91d4e9dbe2 | |||
| 0821ad3c03 | |||
| bf2e5d3fdc | |||
| 7e54cd4306 | |||
| 6ecf783fd5 | |||
| b45c336063 | |||
| 4d9dc31c5e | |||
| ec4420cb38 | |||
| 243ff2d59e | |||
| cc2e8ed513 | |||
| ca0c78fe5c | |||
| e5b678f4a8 | |||
| fe7989798c | |||
| 12efedf3e3 | |||
| 25fec470a7 | |||
| 6d148b05a3 | |||
| 4c3002e72d | |||
| cc4750688e | |||
| be5cd7de2e | |||
| 0d2ccfddfb | |||
| fa6f0a6b00 | |||
| 0b71f55b29 | |||
| 9cdc72662f | |||
| a37d9768a0 | |||
| 7de8d302fa | |||
| e29edc9e97 | |||
| 1e216291ab | |||
| ce5c36ded5 | |||
| 568c4a0228 | |||
| 0c5e561eac | |||
| d796812161 | |||
| 26905b18ac | |||
| 7fc7471f81 | |||
| 3272f72df0 | |||
| ab8b57ed49 | |||
| eb7ac6ea3d | |||
| c056f8525d | |||
| 024a11f35b | |||
| 33390a2c2d | |||
| d409ad22a0 | |||
| c600cb0294 | |||
| 077f1ddb84 | |||
| a9cbe354c1 | |||
| 44341d1ff5 | |||
| e2a4b0eb30 | |||
| d2ca5e3bf3 | |||
| 8cb32b1f14 | |||
| 8553bef132 | |||
| 20f6d559c3 | |||
| d50c28ebe7 | |||
| 1c854519c8 | |||
| 229f74e370 | |||
| fa324561da | |||
| e176c6c315 | |||
| 3b56977c49 | |||
| 5482cfac3f | |||
| 04e5ff7eba | |||
| e230686851 | |||
| 5694ce53f6 | |||
| 4dd4d5eb5f | |||
| 0e94e57263 | |||
| 7bf89d8614 | |||
| d47e8c0f44 | |||
| 48c04a5a44 | |||
| 31bc74e2b1 | |||
| 1e23b8d475 | |||
| 48208998e2 | |||
| 92aafb1349 | |||
| e1e8a480f9 | |||
| b6e641c035 | |||
| fe77659058 | |||
| b645ca984f | |||
| 46a3f2e495 | |||
| e4e9cbf6cb | |||
| d43e53f896 | |||
| 2d947f4a97 | |||
| 71720de7b2 | |||
| 4964eb31fa | |||
| 0b8f7d67dc | |||
| faf6ea72e0 | |||
| b66415a338 | |||
| e14733d460 | |||
| b6f7c8bc4b | |||
| 91aef71891 | |||
| 408db8dc4f | |||
| 4902716a44 | |||
| e084c1410c | |||
| ea790cc7c5 | |||
| c0fc671a83 | |||
| fb418c87ac | |||
| b6da96fb78 | |||
| 1a751c7bae | |||
| 2d100d8261 | |||
| 3f70fdb3aa | |||
| 1819e3dd99 | |||
| 64791017f8 | |||
| 5e0d42898c | |||
| 4486620918 | |||
| b6bd69055b | |||
| 1e2ea1614c | |||
| ea74fcd4d6 | |||
| c39613ddd8 | |||
| 4a4f9dff6a | |||
| cd55218a2c | |||
| 881d415f21 | |||
| db02ffa436 | |||
| 2c78f1ff7b | |||
| 2bf3ddf45b | |||
| d8c48f1a85 | |||
| 1f566294ac | |||
| 86643058b6 | |||
| 6b6d589aa7 | |||
| cbc0419c6e | |||
| 5920f3b34d | |||
| c472ecf3b0 | |||
| 70c507b8f8 | |||
| 2245e572aa | |||
| c984722807 | |||
| 97c0161b9d | |||
| 05eed0fdda | |||
| 9e74976d6d | |||
| c8b75dfae1 | |||
| ca600e9d76 | |||
| 94812cde7a | |||
| 3db930cb93 | |||
| 37300a9345 | |||
| 54fc1ff69c | |||
| 6d1c723e4b | |||
| eab5091147 | |||
| b8f21ad878 | |||
| cb4702f7b8 | |||
| 25313f6b43 | |||
| a0e0c2c950 | |||
| 8b43f70ed4 | |||
| 867a2b8748 | |||
| 63cd86589c | |||
| 31c4414c32 | |||
| 36558389b6 | |||
| 686ab1b51f | |||
| a728ed9a60 | |||
| 00fd916595 | |||
| 7c5b7b09c2 | |||
| c3c63971ab | |||
| 5c1c80fd9a | |||
| c9a9541d0e | |||
| 224858dad5 | |||
| 8a9e963d92 | |||
| 464c0271f0 | |||
| 798d78813a | |||
| 6b0e45c450 | |||
| cf8bd786d0 | |||
| 9b4a51c5be | |||
| b58c52f12c | |||
| 17eef46da2 | |||
| cc14ab20bb | |||
| 978f00bbb9 | |||
| d0a73eb862 | |||
| 0ca5a30b8a | |||
| 76c7d9dc89 | |||
| 653e0b80b2 | |||
| 8b0fc1629a | |||
| a76e756f4f | |||
| f632cd48a4 | |||
| e12b0a585c | |||
| 151ce619dc | |||
| 28903d5db2 | |||
| d179b5d6bf | |||
| 12df8fa500 | |||
| 99293ba5e6 | |||
| 2dc3096ee9 | |||
| 926a023b1d | |||
| 4f705a6e58 | |||
| 74beb86530 | |||
| a55f8d0099 | |||
| caca1b2e3c | |||
| 62fe5d003e | |||
| 216dae97fc | |||
| 07ffa8c117 | |||
| 62dc012c66 | |||
| 831987c76f | |||
| 32b85d48c5 | |||
| f6d37afba0 | |||
| 3b962ffb08 | |||
| 2c3bffaf76 | |||
| ba8110dc20 | |||
| 55e17a4a65 | |||
| 767b8b245a | |||
| 2ea37776ed | |||
| 03d2f704c5 | |||
| f7e19bcf63 | |||
| 1e0fcbc65e | |||
| 5d5c4d76b9 | |||
| 871561afd7 | |||
| 1da4b2eda9 | |||
| 9775abecc2 | |||
| 1650049b88 | |||
| d7cc15d391 | |||
| 57286ea40d | |||
| 5ad4c517e1 | |||
| eba6e29092 | |||
| 7dc6ea355c | |||
| 3e31c4363f | |||
| 61e3abaf80 | |||
| bd8e9dc947 | |||
| 236aaed2a3 | |||
| 37044e5fc8 | |||
| b05d6e2ed3 | |||
| d28164c897 | |||
| 6c68b6f6fe | |||
| d0c6f0d6ee | |||
| 150b5a1f06 | |||
| 8ea6d70433 | |||
| f8ee4da180 | |||
| 4a57f462bc | |||
| 11ae5ea014 | |||
| 4cb3e6a23c | |||
| dd645bcd9f | |||
| bccf4ef563 | |||
| 85e2534e93 | |||
| 2c57e0c958 | |||
| cb161a25da | |||
| a4380586e9 | |||
| f2a583a599 | |||
| 3ea4c8d9ca | |||
| 30f321d88b | |||
| 1db3b69489 | |||
| 3a775315b9 | |||
| 1ed9bcae5c | |||
| 03d57e690c | |||
| 4247584df2 | |||
| 844c3f7002 | |||
| ff774dc409 | |||
| 9fd7a84b48 | |||
| e555f679c1 | |||
| 89613bfee5 | |||
| 3fdf87a955 | |||
| 0f07975857 | |||
| 48a245559c | |||
| 648f26f075 | |||
| 808e20588e | |||
| 82dd5bf3eb | |||
| 0950c738e3 | |||
| f12f6db310 | |||
| ae4031b993 | |||
| 2495d2a1e9 | |||
| 0a97b08c72 | |||
| e0a89c55ac | |||
| 75ac34b719 | |||
| 2072c54c53 | |||
| 6e1ed63005 | |||
| 17e59daeb4 | |||
| 69e83ef89f | |||
| b2707f5c8a | |||
| 0fd24e5210 | |||
| 3dcc7e5556 | |||
| 57ebeae1ad | |||
| 9e35cc3fe1 | |||
| 3e1258aa0c | |||
| 96e1b1d105 | |||
| fe80e486aa | |||
| ad051f21c1 | |||
| ac24a4ae19 | |||
| 7ae43ee9f6 | |||
| 0e79070ad8 | |||
| f11b976574 | |||
| 59da5d5deb | |||
| 0deb7038e6 | |||
| 2ef692c690 | |||
| 9228c0b9ec | |||
| 89d02b1d67 | |||
| a63a9248d6 | |||
| 79777adc07 | |||
| 5619feb6c0 | |||
| f15a721db6 | |||
| d6b3b68cd9 | |||
| 78785fa8fe | |||
| 74c69f575f | |||
| 12e34bb24f | |||
| 4ac5484b4f | |||
| 8306619faf | |||
| 5673477428 | |||
| 84c5b67574 | |||
| e71a15d46e | |||
| daf9556bed | |||
| b6a7c1036c | |||
| 8c2a66b00c | |||
| a1182e0ebe | |||
| 750492f9e3 | |||
| c6893f6cb5 | |||
| 64e035c53c | |||
| adbe515af7 | |||
| f0dac1630f | |||
| ddaa69bce2 | |||
| bb7c2fd660 | |||
| 8dfca61769 | |||
| bb3c54cb27 | |||
| 2816b1128f | |||
| cf0cf972e3 | |||
| bb0e783c02 | |||
| a7b7c5ba81 | |||
| 7214b8b1b1 | |||
| db1e25252a | |||
| 7a248331e7 | |||
| 161ffdee94 | |||
| b3e8fd9c66 | |||
| 538135bc5f | |||
| 0aac0398e8 | |||
| 552024ff31 | |||
| 2f14c37c59 | |||
| 7f8dc486bb | |||
| a89b42229a | |||
| e061d156bc | |||
| 8f4753a0bb | |||
| 068523fac1 | |||
| b3336229ab | |||
| 3614966241 | |||
| 80c1cb954d | |||
| 0f6336b3dd | |||
| cd1ee47ace | |||
| 46ed2f6263 | |||
| 63f6128c87 | |||
| 9f9ac4ec92 | |||
| 7cf223a5b8 | |||
| 9a6e0b3a54 | |||
| 589643d591 | |||
| f75ae01c16 | |||
| 3509648553 | |||
| 94841669bf | |||
| fadc0bf6c7 | |||
| fd2c01808a | |||
| 8342da1ea2 | |||
| e9abb6d021 | |||
| 505989eec7 | |||
| 08a30b255d | |||
| 1543c77a39 | |||
| 4c22a421a1 | |||
| 972c97d5ec | |||
| fd611b3647 | |||
| 71cbaa843c | |||
| bbf8b50ad6 | |||
| ddd083b8c1 | |||
| 03f6c76b53 | |||
| 6d6d4f14b5 | |||
| 0735a3475c | |||
| 9a445d529d | |||
| 1ea5a5ac0b | |||
| bc8370eadd | |||
| cdc5a2b361 | |||
| 4270ac80fb | |||
| 405df5da8b | |||
| f742ae4c88 | |||
| a99783dfa0 | |||
| 9af5edd04d | |||
| d0830b90be | |||
| 8cd48bc660 | |||
| 379a320d2d | |||
| d5f3a3ef69 | |||
| a3be970f29 | |||
| 63f8f52c9f | |||
| 169851564c | |||
| c32b9029d1 | |||
| 3fe60b9d18 | |||
| 34cff752f1 | |||
| 0dba83daf6 | |||
| c399092261 | |||
| dcabb2c579 | |||
| 3542785021 | |||
| 65173e6dd2 | |||
| bf0bb68e28 | |||
| 21774dc36b | |||
| 0e3a9f0b8f | |||
| 903cb404c3 | |||
| 03325533bf | |||
| 710123aab4 | |||
| 4742ec972b | |||
| e860ba220a | |||
| 39605bccc2 | |||
| 1a4f411b8f | |||
| a6588daa3e | |||
| b053366b06 | |||
| e838ac502d | |||
| 6131154dd0 | |||
| 0c67a61e7d | |||
| 72696a4326 | |||
| 3231f4d9b1 | |||
| 8a81b398cf | |||
| a37a1a0004 | |||
| b6b00c843a | |||
| 5506c0543c | |||
| 7a1b48311c | |||
| 0b3dafcd18 | |||
| 81f6b3c7bd | |||
| c2da4acb48 | |||
| 841b05631d | |||
| a6e60aea04 | |||
| 7f2a1db64a | |||
| 926ccdf618 | |||
| 9774eca8e2 | |||
| 2ad033f8e2 | |||
| 7c26ffee51 | |||
| aa37ba0463 | |||
| d61c86b54b | |||
| e72fa1dc74 | |||
| 16db531cce | |||
| 226023043b | |||
| 5d2538c4c2 | |||
| 0075103ddf | |||
| 8982a67902 | |||
| 65ab45ff1b | |||
| 577d434bc8 | |||
| b300e634f6 | |||
| 61d13b9b2d | |||
| 78059bf825 | |||
| dd02a69e8f | |||
| 5ec80ad1f1 | |||
| 4849772435 | |||
| 6546879d3a | |||
| cbc379daa9 | |||
| a7be08ef46 | |||
| 47e48e5d27 | |||
| 968b8109dd | |||
| 19fba27951 | |||
| b78da20d04 | |||
| 5e445b4a71 | |||
| 0e5df883d0 | |||
| b79ea119ea | |||
| b21d98affd | |||
| d83666bed3 | |||
| cab6ca3ef0 | |||
| 33a37f9494 | |||
| 79e978aac9 | |||
| 3af60a81f8 | |||
| 56525ced92 | |||
| 581f3797bc | |||
| 2c8d51988b | |||
| 19243b741a | |||
| afc1e08f21 | |||
| 80c01e74c6 | |||
| d728bfb90c | |||
| 203d73ee26 | |||
| 05fe220102 | |||
| c4f2de198e | |||
| 50f9846836 | |||
| a33b3cfbd8 | |||
| 248f7cce90 | |||
| 8f381e2611 | |||
| f4b14d0d8d | |||
| 93428815e4 | |||
| 6b70610770 | |||
| 84eead47e3 | |||
| 009d03f1f8 | |||
| 9482a247e7 | |||
| 022c79fa95 | |||
| a9ed4ac21d | |||
| 354e39e9bf | |||
| 757e08c6fe | |||
| 33e5775294 | |||
| 5b73a8ed1c | |||
| f864ebbf1a | |||
| 54b6051315 | |||
| ebea34920b | |||
| 44294306ce | |||
| c2af1a0666 | |||
| 8f30696a4d | |||
| b58082a37b | |||
| a594878860 | |||
| 3a2b6296ac | |||
| 87af609a6f | |||
| a0d5d0f3c8 | |||
| 53510f3268 | |||
| 9d5ce9753a | |||
| d7a2df41fa | |||
| f905cb9577 | |||
| b9b6439f47 | |||
| ffcdf986a3 | |||
| 43245b5eb5 | |||
| 420228c67b | |||
| f1719d6e91 | |||
| 9aa1fd5c00 | |||
| 141845aa40 | |||
| 7867df6079 | |||
| 182b937143 | |||
| 5a54fb7e9f | |||
| 399a9b16f6 | |||
| fef45dfc30 | |||
| 42aa7d8d55 | |||
| 8778119792 | |||
| 10e3f2c990 | |||
| 5e25989366 | |||
| f12d328cf7 | |||
| efc4917724 | |||
| c2bf145ff5 | |||
| c8c82811b3 | |||
| c21e133e69 | |||
| 789a7fbb88 | |||
| 888ed9f0ae | |||
| 57da253fa8 | |||
| 44a6707397 | |||
| f0a3ca4314 | |||
| 9bbd52dcc2 | |||
| f50862ac51 | |||
| 017bed1a07 | |||
| 3765d5f549 | |||
| dd28159564 | |||
| 63cdf9f8e7 | |||
| e30889aa12 | |||
| b7caec1bfe | |||
| 660786b99c | |||
| b060576209 | |||
| 7b3826c2d5 | |||
| 51d4416b00 | |||
| cfe6103f48 | |||
| bee8230079 | |||
| 3d77c59c92 | |||
| 667ed161c0 | |||
| 979f9495f1 | |||
| d29a44efa7 | |||
| 7750003033 | |||
| e673339dc4 | |||
| b20b47f8eb | |||
| 689e437bac | |||
| e6bb17dc86 | |||
| 80a97b0f26 | |||
| ffea050bc2 | |||
| 8951526d0d | |||
| 3e799fbe21 | |||
| 53e27dbc38 | |||
| 9985d303ce | |||
| ba46299088 | |||
| 9679eae662 | |||
| 7acb5a4bdb | |||
| d1428b8221 | |||
| c59a361ab3 | |||
| 8935728daa | |||
| 155ac91385 | |||
| 4403ed3860 | |||
| 712b567752 | |||
| 75be945359 | |||
| fe50a457cd | |||
| 6f821e5344 | |||
| 14cbfb5706 | |||
| a882685169 | |||
| b9b7edc72d | |||
| d0b939da4a | |||
| 2643d493a3 | |||
| c26689fcb1 | |||
| a2365f2c14 | |||
| 435ff795ca | |||
| af34fdc45a | |||
| 3bfedd844e | |||
| f726151e6e | |||
| c6c4becf7d | |||
| e2deb71988 | |||
| a717bf4f34 | |||
| 2fef7a5b84 | |||
| 12309c281b | |||
| a2a4e5b350 | |||
| 6741287ef1 | |||
| 6940f5a982 | |||
| b0bd5c13a0 | |||
| af119a0248 | |||
| 759cd7b3f2 | |||
| 5e06e0d2d2 | |||
| 85b83e1745 | |||
| 441954be35 | |||
| 11c1a23dfe | |||
| 1386435b17 | |||
| f33d135182 | |||
| 5396477082 | |||
| 5ea77cc4b6 | |||
| 45b5b33929 | |||
| 9213b09c57 | |||
| 113e616d25 | |||
| 8861c553da | |||
| 6be7995500 | |||
| 89d08606ad | |||
| e709560319 | |||
| 9eda7548d0 | |||
| 6e0522308a | |||
| 7062e8864b | |||
| 957cea2638 | |||
| 532710e6bf | |||
| 24e46e4c01 | |||
| cb3cdbf6e7 | |||
| dcceef2e52 | |||
| cbc965dc53 | |||
| f5b793f80c | |||
| d3b51703f2 | |||
| 0ecee70af2 | |||
| 061704ac6e | |||
| 3e1ce8c2fa | |||
| 1cc84672e7 | |||
| 04f502df8a | |||
| 22c3afd8a0 | |||
| d9be97fc45 | |||
| b04e6ff699 | |||
| 9c1a5548e1 | |||
| b73d7f03a7 | |||
| 106ed6b953 | |||
| cccbaa0bdc | |||
| 874dacd2c8 | |||
| a72a23b548 | |||
| 31004e8c12 | |||
| 87c471c0f5 | |||
| d3e1a423fd | |||
| 16cc743d26 | |||
| 9ccb699fca | |||
| 7e0960e7a0 | |||
| ee4fc2803b | |||
| 268c2861c9 | |||
| 15739bc0ee | |||
| e84de56f2e | |||
| 6b46e81fbd | |||
| f5de5f58ce | |||
| 3c2cfa333c | |||
| 63bc3a2b88 | |||
| 1e83bc05ee | |||
| acb9faa6e3 | |||
| 732103abf7 | |||
| 90223ff514 | |||
| ec8084823c | |||
| 05b55fa8dc | |||
| eaad735d22 | |||
| ecf483702f | |||
| 2e9e1fc424 | |||
| b5c73d1574 | |||
| beb73d891d | |||
| a4edd8e41f | |||
| cac3dae96c | |||
| 279ef2075f | |||
| 5d0740c0e5 | |||
| 9ee8055093 | |||
| 07ed7eb6dd | |||
| ed6878a48f | |||
| 09a17ba783 | |||
| 046b291c1f | |||
| 187a19bfd1 | |||
| 91816e6715 | |||
| 13339125eb | |||
| 3197db9fc1 | |||
| 6e4aac1123 | |||
| 94450a26e9 | |||
| f571596eda | |||
| 8b6ca96e0a | |||
| 198de2607a | |||
| 7cd1c9e8c8 | |||
| fc51c355a5 | |||
| 59e7213c96 | |||
| 192020a93a | |||
| 5f5019d310 | |||
| 9aa201dcb3 | |||
| f0f90d87af | |||
| 1bbe800dcd | |||
| a731ef6e2d | |||
| 25b09a7962 | |||
| 80151ac1a3 | |||
| bad5668ff7 | |||
| cad0f15aed | |||
| 9c42c30d8a | |||
| c471d1e4c3 | |||
| 7ae4fae68c | |||
| 2405d5151b | |||
| 4f667291bd | |||
| 49ad3a51aa | |||
| f2e98acc65 | |||
| b68adde751 | |||
| 7220eca7a8 | |||
| 10dd9b5120 | |||
| 7590c133fd | |||
| 2873685517 | |||
| 9887892a92 | |||
| 6fa15751ae | |||
| 24e3fcbb6c | |||
| 91f51778f1 | |||
| 5c2bf5d76c | |||
| c4233e2896 | |||
| e0b7bdc1a5 | |||
| 91ec39e141 | |||
| 602dbdf1d2 | |||
| 3eabaf074a | |||
| a81178121f | |||
| e1a83aab1d | |||
| 84601bacdb | |||
| e991e2bf37 | |||
| 9b6fc4bc6b | |||
| c9e832600c | |||
| 25190d945d | |||
| b2f2ffcfed | |||
| 00821c1ae3 | |||
| 937acbc218 | |||
| e0f9313b8b | |||
| 6d0d8ea90a | |||
| ebf32390ef | |||
| eefea15069 | |||
| 531dd43284 | |||
| e483ce85dc | |||
| 80da14c1fb | |||
| 374580a3d6 | |||
| 4a51e4e7fb | |||
| ae969dad38 | |||
| 7fcd008f2f | |||
| def5be630a | |||
| de3848ff53 | |||
| 8d4ab6bb94 | |||
| 28ac117d68 | |||
| 065ff48b15 | |||
| 69bdd6db83 | |||
| cd270dd407 | |||
| b4944a6f81 | |||
| 4049bf20c2 | |||
| 805ca0d378 | |||
| fcaae346a7 | |||
| 0684102511 | |||
| 6ca87f6d33 | |||
| 3c96831cdc | |||
| b551f6714b | |||
| c005cc211f | |||
| 92b0d5c0be | |||
| 45dc9e5c0d | |||
| 7c505949fc | |||
| 905accdb7d | |||
| 30f3e3cf56 | |||
| 71131fd00c | |||
| 60b8b0080c | |||
| b30ff9b41b | |||
| cf1a0e3c33 | |||
| e94fe13178 | |||
| b2c4561a7d | |||
| ef63c8d66e | |||
| 00be8a2237 | |||
| 6593e3cfe6 | |||
| ac73798b9d | |||
| 1697b99bbc | |||
| 1e9467a221 | |||
| b56238089c | |||
| 47d8aa871b | |||
| 87627b8320 | |||
| c419f87b64 | |||
| 9ae22a1ad7 | |||
| b4dfd35be6 | |||
| 916654b3ec | |||
| 2acd088226 | |||
| 2d217edc15 | |||
| b55d13bc21 | |||
| 1f9468e029 | |||
| 593804096d | |||
| befb14682d | |||
| 07a6700ab6 | |||
| 8131953e49 | |||
| 7e360aa915 | |||
| 0710fba638 | |||
| ad1bcbb0ac | |||
| afb7a17e5f | |||
| badf302756 | |||
| 99941dde8f | |||
| e5c55b6962 | |||
| 7a7f4cb306 | |||
| c0ff8f80b1 | |||
| d084e31065 | |||
| 12008ef1c6 | |||
| 23ec52c6b4 | |||
| 5485740291 | |||
| 6dc0823b73 | |||
| 0f9220c965 | |||
| 5c21261aea | |||
| 1fda0df03e | |||
| d4406bb806 | |||
| b1095e29bc | |||
| 3e56aa43e4 | |||
| c2dc030fae | |||
| 5565680b53 | |||
| 340cd0a1fe | |||
| 2844e706a9 | |||
| 55ee64b4e6 | |||
| 3849700a28 | |||
| 3dd0049973 | |||
| 8e6ccf1b20 | |||
| c544dbbbb7 | |||
| 5205ac38ad | |||
| 0143807515 | |||
| 5176ab1382 | |||
| b0d54f7c5d | |||
| 96f0335af4 | |||
| 572f8ebfde | |||
| 07a8d844d2 | |||
| c48abef5f8 | |||
| 9b3679792d | |||
| cb00983db3 | |||
| 83686866cc | |||
| 7a49249384 | |||
| 0548793bfd | |||
| cd770f70c0 | |||
| a3f1950a18 | |||
| 1874c28cad | |||
| 317f01a6b4 | |||
| 5e8eff074d | |||
| db028f2774 | |||
| a82a154be9 | |||
| af01927807 | |||
| f60aac93ab | |||
| 4860bf3f19 | |||
| adf2af82b4 | |||
| 15ddc5f06c | |||
| 8d16563014 | |||
| 3559b539e3 | |||
| da765a5eec | |||
| c2e8b5a1fa | |||
| 72c4962e97 | |||
| 789cf1d66b | |||
| 64ec922d7f | |||
| 85b3aefd67 | |||
| af0dabec99 | |||
| de85983247 | |||
| 7eaae3a6d5 | |||
| 403b57c076 | |||
| dd434b89d2 | |||
| 1a3d58e09b | |||
| 89ae239743 | |||
| c785d1509b | |||
| 9e412555bb | |||
| 0717805773 | |||
| d045c5a390 | |||
| 3cc0d0c735 | |||
| 9a3c727532 | |||
| a7f2484d45 | |||
| 96d97699a0 | |||
| e92e832258 | |||
| 3a769549d8 | |||
| 0eab0dc680 | |||
| 2b48f50de4 | |||
| fa60eacfc5 | |||
| d2a4136d4f | |||
| 7d88a746ea | |||
| 7f4537bdc6 | |||
| d68829820b | |||
| 9cf3036ab5 | |||
| 2467eb71c1 | |||
| 345bb5d1aa | |||
| 8d50828998 | |||
| 201de82baf | |||
| 99519ac3da | |||
| a575ab7c74 | |||
| c4f79814df | |||
| 6135f6271c | |||
| 8ef70f07f0 | |||
| 17b7e37885 | |||
| 35c17044e1 | |||
| 7085d98f41 | |||
| ee73e32b0e | |||
| 371430c172 | |||
| 495753b517 | |||
| 211be0a863 | |||
| 12c9a8e6c3 | |||
| fb5d9e0c01 | |||
| 61a2d962c0 | |||
| 440622830a | |||
| 2652990432 | |||
| 652e2f27b6 | |||
| 77a82eaa6a | |||
| 624a181b23 | |||
| 865ac52442 | |||
| 4f064d7c02 | |||
| facb67e94e | |||
| 124c838e06 | |||
| 026e894e1d | |||
| 2ed0f1661a | |||
| 00f285cba3 | |||
| ddf5bf472f | |||
| 534c7799e5 | |||
| b254aaa081 | |||
| 76f2860c74 | |||
| 5ebcb77d0d | |||
| 4847752ef4 | |||
| 84ec8d61ae | |||
| 0f13551b6d | |||
| 3e66c6cce4 | |||
| 09de3a5e7a | |||
| 00483d4c0d | |||
| 1e3d10ad31 | |||
| dc31cd7103 | |||
| cffb670f72 | |||
| 781fea7431 | |||
| 78b0b7e01d | |||
| e96367cd4b | |||
| 30af3c7f8a | |||
| 264405827e | |||
| 083721bbc1 | |||
| 91ed04ff83 | |||
| cbbd4fd029 | |||
| f6bc7721a8 | |||
| 32ec9e82bf | |||
| 69a7a8a04e | |||
| ccb5194bb5 | |||
| 2d280edf0c | |||
| afe4817150 | |||
| 6754b3746e | |||
| 287f83f4d0 | |||
| bf518a961e | |||
| 4a55467eef | |||
| 56cf5249f9 | |||
| 17e8bc6949 | |||
| 02ede8020f | |||
| 54158a0340 | |||
| 7ce96a4e3d | |||
| 2067b70445 | |||
| d02658e0ba | |||
| 27e68ed6a9 | |||
| b3e0ac017f | |||
| 8bc437a84e | |||
| 11e5a94fc4 | |||
| c11941cbc1 | |||
| f792aea98e | |||
| 12e5937a91 | |||
| 60b9cd8789 | |||
| 41e4c80494 | |||
| bd2756c0bf | |||
| 1f3c4948c6 | |||
| 062f9a195c | |||
| a3e8af40b2 | |||
| 49f4773e81 | |||
| ee78385b4c | |||
| e99ccd0051 | |||
| cbc022524b | |||
| 34d9d042aa | |||
| 22f69a99e2 | |||
| 4ee5539e3d | |||
| 048636e894 | |||
| 5797ad2d5b | |||
| ba9a8d52cc | |||
| ee18a9dd6c | |||
| 517c2f456c | |||
| e8734daa4d | |||
| 1fd7c6e2f1 | |||
| bc638ea06e | |||
| 5b0ba79a35 | |||
| 173c7b2aec | |||
| fd37a9c660 | |||
| fc460554e7 | |||
| 73490903a3 | |||
| 964c0ac420 | |||
| f59ad1620a | |||
| 4797f18819 | |||
| b0d73a3314 | |||
| 562e60f964 | |||
| fde0fa673b | |||
| c93d5e7830 | |||
| e305b973b2 | |||
| 506d3611fd | |||
| 14b644777d | |||
| b9c23c75f4 | |||
| af60d03060 | |||
| adb66cae9d | |||
| aeaa0c5f8d | |||
| b009da4b47 | |||
| 8abd70bed8 | |||
| 91cd6c1954 | |||
| 7a702489d0 | |||
| d33331f650 | |||
| 8e3bd07c91 | |||
| ad55551819 | |||
| 1d38e50281 | |||
| cef0882e80 | |||
| 5a07c4917e | |||
| e498d091cd | |||
| 8ba30c5453 | |||
| 685e2eaff7 | |||
| 6ca8471c64 | |||
| e30f4652af | |||
| 9acb89ba8a | |||
| 9b9a775a41 | |||
| a6f11cdb31 | |||
| 777642ef55 | |||
| d1d7fd258f | |||
| 7e4635de26 | |||
| d7b2173066 | |||
| 55e9c8c9eb | |||
| c34e1d3c19 | |||
| f89b1ba303 | |||
| fc7b6dd0c6 | |||
| 1b5240a513 | |||
| b57abfd563 | |||
| 92f9ccce5c | |||
| 1a67ca897c | |||
| bd06f0c713 | |||
| 4e5c88eb74 | |||
| 6b6394ba97 | |||
| c273bf6a7b | |||
| bde415edd7 | |||
| 2004090ce5 | |||
| 18ea542cc4 | |||
| d1980ef978 | |||
| b3db49b4ae |
@@ -0,0 +1,24 @@
|
||||
name: CompatHelper
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '00 * * * *'
|
||||
|
||||
jobs:
|
||||
CompatHelper:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
julia-version: [1.2.0]
|
||||
julia-arch: [x86]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: julia-actions/setup-julia@latest
|
||||
with:
|
||||
version: ${{ matrix.julia-version }}
|
||||
- name: Pkg.add("CompatHelper")
|
||||
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
|
||||
- name: CompatHelper.main()
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: julia -e 'using CompatHelper; CompatHelper.main()'
|
||||
@@ -0,0 +1,11 @@
|
||||
name: TagBot
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 * * * *
|
||||
jobs:
|
||||
TagBot:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: JuliaRegistries/TagBot@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -5,3 +5,8 @@
|
||||
examples/.ipynb_checkpoints/*
|
||||
examples/meetup/.ipynb_checkpoints/*
|
||||
deps/plotly-latest.min.js
|
||||
deps/build.log
|
||||
deps/deps.jl
|
||||
Manifest.toml
|
||||
dev/
|
||||
test/tmpplotsave.hdf5
|
||||
|
||||
+25
-40
@@ -4,56 +4,41 @@ os:
|
||||
- linux
|
||||
# - osx
|
||||
julia:
|
||||
- 0.6
|
||||
- 1.0
|
||||
- 1
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
- julia: nightly
|
||||
|
||||
# # before install:
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install wkhtmltopdf; fi
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- at-spi2-core
|
||||
- libgtk-3-dev
|
||||
- xauth
|
||||
- xvfb
|
||||
|
||||
# ref: http://askubuntu.com/a/556672 for the wkhtmltopdf apt repository info
|
||||
env:
|
||||
- GKS_ENCODING="utf8"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.julia/artifacts
|
||||
|
||||
sudo: required
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./test/install_wkhtmltoimage.sh ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:pov/wkhtmltopdf ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y wkhtmltopdf ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wkhtmltopdf -V ; fi
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wkhtmltoimage -V ; fi
|
||||
# echo 'exec xvfb-run -a -s "-screen 0 640x480x16" wkhtmltopdf "$@"' | sudo tee /usr/local/bin/wkhtmltopdf.sh >/dev/null
|
||||
# sudo chmod a+x /usr/local/bin/wkhtmltopdf.sh
|
||||
|
||||
# # borrowed from Blink.jl's travis file
|
||||
# matrix:
|
||||
# include:
|
||||
# - os: linux
|
||||
# julia: 0.4
|
||||
# env: TESTCMD="xvfb-run julia"
|
||||
# - os: osx
|
||||
# julia: 0.4
|
||||
# env: TESTCMD="julia"
|
||||
|
||||
|
||||
|
||||
notifications:
|
||||
email: true
|
||||
# uncomment the following lines to override the default test script
|
||||
|
||||
after_success:
|
||||
- julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())'
|
||||
|
||||
script:
|
||||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")'
|
||||
- julia test/travis_commands.jl
|
||||
# - julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
|
||||
# - julia -e 'Pkg.clone("GR"); Pkg.build("GR")'
|
||||
# # - julia -e 'Pkg.clone("https://github.com/tbreloff/ImageMagick.jl.git"); Pkg.checkout("ImageMagick","tb_write"); Pkg.build("ImageMagick")'
|
||||
# - julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");'
|
||||
# # - julia -e 'Pkg.clone("https://github.com/JunoLab/Blink.jl.git"); Pkg.build("Blink"); import Blink; Blink.AtomShell.install()'
|
||||
# # - julia -e 'Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")'
|
||||
# - julia -e 'ENV["PYTHON"] = ""; Pkg.add("PyPlot"); Pkg.build("PyPlot")'
|
||||
#
|
||||
# # - $TESTCMD -e 'Pkg.test("Plots"; coverage=false)'
|
||||
# - julia -e 'Pkg.test("Plots"; coverage=false)'
|
||||
# # - julia -e 'cd(Pkg.dir("Plots")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())'
|
||||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
|
||||
- $TESTCMD -e 'using Pkg; Pkg.build(); Pkg.test(coverage=true)'
|
||||
|
||||
@@ -3,14 +3,229 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.15!
|
||||
- Minor version 0.11 is the last one to support Julia 0.5!!
|
||||
- Critical bugfixes only
|
||||
- `backports` branch is for Julia 0.5
|
||||
|
||||
---
|
||||
## (current master)
|
||||
|
||||
## 0.28.3
|
||||
- support generalized array interface
|
||||
- save to pdf, svg and eps in plotlyjs
|
||||
- fix for clims in line_z
|
||||
- optimize heatmap logic in gr
|
||||
|
||||
## 0.26.3
|
||||
- fix `vline` with dates
|
||||
- fix PyPlot logscale bug
|
||||
- avoid annotation clipping for PyPlot
|
||||
- allow plotting of Any vectors and 3D plotting again in convertToAnyVector
|
||||
- specify legend title font in GR and PyPlot
|
||||
- delete `pushtomaster.sh`
|
||||
- use `=== nothing`
|
||||
|
||||
## 0.26.2
|
||||
- improve empty animation build error
|
||||
- fix GR axis flip for heatmaps and images
|
||||
- fix ribbons specified as tuples
|
||||
- add Char recipe
|
||||
- fix Plotly plots with single-element series
|
||||
- rewrite PlotlyJS backend
|
||||
|
||||
## 0.26.1
|
||||
- handle `Char`s as input data
|
||||
- fix html saving for Plotly
|
||||
- expand ~ in paths on UNIX systems
|
||||
- convertToAnyVector clean-up
|
||||
- fix color_palette grouping issue
|
||||
|
||||
## 0.26.0
|
||||
- use FFMPEG.jl
|
||||
- add missing method for convertToAnyVector
|
||||
|
||||
## 0.25.3
|
||||
- add areaplot
|
||||
- allow missing in z_color arguments
|
||||
- more general tuple recipe
|
||||
- stephist logscale improvements
|
||||
|
||||
## 0.25.2
|
||||
- improvements to handle missings
|
||||
- pyplot: allow setting the color gradient for z values
|
||||
- document :colorbar_entry
|
||||
- limit number of automatic bins
|
||||
- fix ENV['PLOTS_DEFAULT_BACKEND']
|
||||
- don't let aspect_ratio impact subplot size
|
||||
- implement arrowstyle for GR
|
||||
- fix bug in plotly_convert_to_datetime
|
||||
- improve missing support
|
||||
- gr: polar heatmaps
|
||||
- make sure show returns nothing
|
||||
|
||||
## 0.25.1
|
||||
- fix gr_display
|
||||
|
||||
## 0.25.0
|
||||
- Replace StaticArrays with GeometryTypes
|
||||
- Contour fixes for GR
|
||||
|
||||
## 0.24.0
|
||||
- Update to the new PyCall and PyPlot API
|
||||
- fix drawing of ticks
|
||||
- fix y label position with GR
|
||||
|
||||
## 0.23.2
|
||||
- pyplot fixes
|
||||
- Add option :tex_output_standalone to set the 'include_preamble' argument in the PGFPlots backend.
|
||||
- fix ticks
|
||||
- support plotly json mime
|
||||
- fix image axis limits
|
||||
- default to radius 0 at center for polar plots
|
||||
|
||||
## 0.23.1
|
||||
- slightly faster load time
|
||||
- fixed errant MethodError
|
||||
- fix bar plots with unicodeplots
|
||||
- better colorbars for contour
|
||||
- add volume seriestype for GR
|
||||
- fix passing a tuple to custom ticks
|
||||
- add vline to pgfplots
|
||||
- add tex output for pyplot
|
||||
- better 3d axis labels for GR
|
||||
|
||||
## 0.23.0
|
||||
- compatible with StatPlots -> StatsPlots name shift
|
||||
- fix histograms for vectors with NaN and Inf
|
||||
- change gif behaviour (remove cache-busting)
|
||||
- improved docstrings for shorthands functions
|
||||
- fix font rotation for pyplot
|
||||
- fix greyscale images for pyplot
|
||||
- clamp greyscale images with values outside 0,1
|
||||
- support keyword argument for font options
|
||||
- allow vector of markers for pyplot scatter
|
||||
|
||||
## 0.22.5
|
||||
- improve behaviour of plotlyjs backend
|
||||
|
||||
## 0.22.4
|
||||
- Add support for discrete contourf plots with GR
|
||||
|
||||
## 0.22.3
|
||||
- Fix the `showtheme` function
|
||||
|
||||
## 0.22.2
|
||||
- Allow annotations to accept a Tuple instead of the result of a text call (making it possible to specify font characteristics in recipes). E.g. `annotations = (2, 4, ("test", :right, 8, :red))` is the same as `annotations = (2, 4, text("test", :right, 8, :red))`
|
||||
|
||||
## 0.22.1
|
||||
- push PlotsDisplay just after REPLDisplay
|
||||
|
||||
## 0.22.0
|
||||
- deprecate GLVisualize
|
||||
- allow 1-row and 1-column heatmaps
|
||||
- add portfoliodecomposition recipe from PlotRecipes
|
||||
- solve Shape bug
|
||||
- simplify PyPlot backend installation
|
||||
- fix wireframe bug in PyPlot
|
||||
- fix color bug in PyPlot
|
||||
- minor bug fixes in gr and pyplot
|
||||
|
||||
## 0.21.0
|
||||
- Compatibility with StaticArrays 0.9.0
|
||||
- Up GR min version to 0.35
|
||||
- fix :mirror
|
||||
|
||||
## 0.20.6
|
||||
- fixes for PlotDocs.jl
|
||||
- fix gr axis color argument
|
||||
- Shapes for inspectdr
|
||||
- don't load plotly js file by default
|
||||
|
||||
## 0.20.5
|
||||
- fix precompilation issue when depending on Plots
|
||||
|
||||
## 0.20.4
|
||||
- honour `html_output_format` in Juno
|
||||
|
||||
## 0.20.3
|
||||
- implement guide position in gr, pyplot and pgfplots
|
||||
- inspectdr fixes
|
||||
- default appveyor
|
||||
- rudimentary missings support
|
||||
- deprecation fixes for PGFPlots
|
||||
|
||||
## 0.20.0
|
||||
Many updates, min julia 1.0
|
||||
- change display type to use PlotsDisplay (fixes Juno integration)
|
||||
- change all internal uses of `d` to `plotattributes` (no user change)
|
||||
- change spy implementation to use `scatter` not `heatmap`
|
||||
- sort x axes when passing a vector of strings as x
|
||||
- improve performance of marker_z
|
||||
- update CI to 1.0
|
||||
- minor depwarn ifixes
|
||||
- only draw one colorbar with GR
|
||||
- add colorbar_title to GR and pgfplots
|
||||
- fix savefig with latexstrings for PyPlot
|
||||
- fix NamedTuple integration
|
||||
- don't export `P2` and `P3`
|
||||
- make it possible to use 2-argument function as argument to marker_z
|
||||
- make `plotattr` work again
|
||||
|
||||
## 0.19.3
|
||||
- fix some julia 0.7 deprecations
|
||||
- fix 32-bit OS functionality
|
||||
|
||||
## 0.19.2
|
||||
- several small fixes for 1.0 compatibility
|
||||
|
||||
## 0.19.1
|
||||
- don't broadcast plot_color
|
||||
|
||||
## 0.19.0
|
||||
- Refactor conditional loading to use Requires
|
||||
- Many fixes for 1.0 compatibility
|
||||
|
||||
## 0.18.0
|
||||
- update minor version to 0.7
|
||||
|
||||
## 0.17.4
|
||||
- fix thickness_scaling for pyplot
|
||||
|
||||
## 0.17.3
|
||||
- Log-scale heatmap edge computation
|
||||
- Fix size and dpi for GR and PyPlot
|
||||
- Fix fillrange with line segments on PyPlot and Plotly
|
||||
- fix flip for heatmap and image on GR
|
||||
- New attributes for PGFPlots
|
||||
- Widen axes for most series types and log scales
|
||||
- Plotly: fix log scale with no ticks
|
||||
- Fix axis flip on Plotly
|
||||
- Fix hover and zcolor interaction in Plotly
|
||||
- WebIO integration for PlotlyJS backend
|
||||
|
||||
## 0.17.2
|
||||
- fix single subplot in plotly
|
||||
- implement `(xyz)lims = :round`
|
||||
- PyPlot: fix bg_legend = invisible()
|
||||
- set fallback tick specification for axes with discrete values
|
||||
- restructure of show methods
|
||||
|
||||
## 0.17.1
|
||||
- Fix contour for PGFPlots
|
||||
- 32Bit fix: Int64 -> Int
|
||||
- Make series of shapes and segments toggle together in Plotly(JS)
|
||||
- Fix marker arguments
|
||||
- Fix processing order of series recipes
|
||||
- Fix Plotly(JS) ribbon
|
||||
- Contour plots with x,y in grid form on PyPlot
|
||||
|
||||
## 0.17.0
|
||||
- Add GR dependency to make it the default backend
|
||||
- Improve histogram2d bin estimation
|
||||
- Allow vector arguments for certain series attributes and support line_z and fill_z on GR, PyPlot, Plotly(JS) and PGFPlots
|
||||
- Automatic scientific notation for tick labels
|
||||
- Allow to set the theme in PLOTS_DEFAULTS
|
||||
- Implement plots_heatmap seriestype providing a Plots recipe for heatmaps
|
||||
|
||||
## 0.16.0
|
||||
- fix 3D plotting in PyPlot
|
||||
- Infinite objects
|
||||
|
||||
## 0.15.1
|
||||
|
||||
- fix scientific notation for labels in GR
|
||||
@@ -68,7 +283,7 @@
|
||||
- add `reset_defaults()` function to reset plot defaults
|
||||
- update syntax to 0.6
|
||||
- make `fill = true` fill to 0 rather than to 1
|
||||
- use new `@df` syntax in StatPlots examples
|
||||
- use new `@df` syntax in StatsPlots examples
|
||||
- allow changing the color of legend box
|
||||
- implement `title_location` for gr
|
||||
- add `hline` marker to pgfplots - fixes errorbars
|
||||
@@ -220,7 +435,7 @@
|
||||
- added dependency on PlotThemes
|
||||
- set_theme --> theme
|
||||
- remove Compat from REQUIRE
|
||||
- warning for DataFrames without StatPlots
|
||||
- warning for DataFrames without StatsPlots
|
||||
- closeall exported and implemented for gr/pyplot
|
||||
- fix DateTime recipe
|
||||
- reset theme with theme(:none)
|
||||
@@ -342,8 +557,8 @@
|
||||
#### 0.8.0
|
||||
|
||||
- added dependency on PlotUtils
|
||||
- BREAKING: removed DataFrames support (now in StatPlots.jl)
|
||||
- BREAKING: removed boxplot/violin/density recipes (now in StatPlots.jl)
|
||||
- BREAKING: removed DataFrames support (now in StatsPlots.jl)
|
||||
- BREAKING: removed boxplot/violin/density recipes (now in StatsPlots.jl)
|
||||
- GR:
|
||||
- inline iterm2 support
|
||||
- trisurface support
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
name = "Plots"
|
||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
author = ["Tom Breloff (@tbreloff)"]
|
||||
version = "1.0.6"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
FFMPEG = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
|
||||
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
|
||||
GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
|
||||
GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
|
||||
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
|
||||
NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
|
||||
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||
PlotThemes = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
|
||||
PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
|
||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
|
||||
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
|
||||
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
|
||||
Showoff = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
|
||||
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
||||
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
|
||||
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
|
||||
[compat]
|
||||
Contour = "0.5"
|
||||
FFMPEG = "0.2, 0.3"
|
||||
FixedPointNumbers = "0.6, 0.7, 0.8"
|
||||
GR = "0.46, 0.47, 0.48"
|
||||
GeometryTypes = "0.7, 0.8"
|
||||
JSON = "0.21"
|
||||
Measures = "0.3"
|
||||
NaNMath = "0.3"
|
||||
PGFPlotsX = "1.2.0"
|
||||
PlotThemes = "1"
|
||||
PlotUtils = "0.6.5"
|
||||
RecipesBase = "1"
|
||||
Reexport = "0.2"
|
||||
Requires = "0.5, 1.0"
|
||||
Showoff = "0.3.1"
|
||||
StatsBase = "0.32, 0.33"
|
||||
julia = "1"
|
||||
|
||||
[extras]
|
||||
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
|
||||
GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
|
||||
Gtk = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44"
|
||||
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
||||
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
|
||||
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
|
||||
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
|
||||
PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925"
|
||||
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
|
||||
VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"
|
||||
|
||||
[targets]
|
||||
test = ["FileIO", "GeometryTypes", "Gtk", "ImageMagick", "Images", "LibGit2", "OffsetArrays", "PGFPlotsX", "HDF5", "Random", "RDatasets", "StaticArrays", "StatsPlots", "Test", "UnicodePlots", "VisualRegressionTests"]
|
||||
@@ -1,16 +1,30 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://ci.appveyor.com/project/mkborregaard/plots-jl)
|
||||
[](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
<!-- [](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
|
||||
<!-- [](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
|
||||
[travis-img]: https://img.shields.io/travis/JuliaPlots/Plots.jl?logo=travis
|
||||
[travis-url]: https://travis-ci.org/JuliaPlots/Plots.jl
|
||||
|
||||
[appveyor-img]: https://ci.appveyor.com/api/projects/status/github/juliaplots/plots.jl?branch=master&svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/mkborregaard/plots-jl
|
||||
|
||||
[pkgeval-img]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/P/Plots.svg
|
||||
[pkgeval-url]: https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html
|
||||
|
||||
[gitter-img]: https://badges.gitter.im/tbreloff/Plots.jl.svg
|
||||
[gitter-url]: https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
[docs-img]: https://img.shields.io/badge/docs-stable-blue.svg
|
||||
[docs-url]: http://docs.juliaplots.org/latest/
|
||||
|
||||
[![][travis-img]][travis-url]
|
||||
[![][appveyor-img]][appveyor-url]
|
||||
[![][pkgeval-img]][pkgeval-url]
|
||||
[![][gitter-img]][gitter-url]
|
||||
[![][docs-img]][docs-url]
|
||||
[](https://codecov.io/gh/JuliaPlots/Plots.jl)
|
||||
|
||||
#### Created by Tom Breloff (@tbreloff)
|
||||
|
||||
#### Maintained by the [JuliaPlot members](https://github.com/orgs/JuliaPlots/people)
|
||||
#### Maintained by the [JuliaPlots members](https://github.com/orgs/JuliaPlots/people)
|
||||
|
||||
Plots is a plotting API and toolset. My goals with the package are:
|
||||
|
||||
@@ -21,5 +35,3 @@ Plots is a plotting API and toolset. My goals with the package are:
|
||||
- **Consistent**. Don't commit to one graphics package, use the same code everywhere.
|
||||
- **Lightweight**. Very few dependencies.
|
||||
- **Smart**. Attempts to figure out what you **want** it to do... not just what you **tell** it.
|
||||
|
||||
View the [full documentation](http://docs.juliaplots.org/latest).
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
julia 0.6
|
||||
|
||||
RecipesBase 0.2.3
|
||||
PlotUtils 0.4.1
|
||||
PlotThemes 0.1.3
|
||||
Reexport
|
||||
StaticArrays 0.5
|
||||
FixedPointNumbers 0.3
|
||||
Measures
|
||||
Showoff
|
||||
StatsBase 0.14.0
|
||||
JSON
|
||||
NaNMath
|
||||
Requires
|
||||
Contour
|
||||
+29
-22
@@ -1,15 +1,26 @@
|
||||
environment:
|
||||
matrix:
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
|
||||
- julia_version: 1.0
|
||||
- julia_version: 1
|
||||
- julia_version: nightly
|
||||
|
||||
platform:
|
||||
- x86 # 32-bit
|
||||
- x64 # 64-bit
|
||||
|
||||
# # Uncomment the following lines to allow failures on nightly julia
|
||||
# # (tests will run but not make your overall status red)
|
||||
matrix:
|
||||
allow_failures:
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" #check and address
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
|
||||
- julia_version: nightly
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release-.*/
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.julia\artifacts'
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
@@ -18,22 +29,18 @@ notifications:
|
||||
on_build_status_changed: false
|
||||
|
||||
install:
|
||||
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12"
|
||||
# If there's a newer build queued for the same PR, cancel this one
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
|
||||
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
|
||||
throw "There are newer queued builds for this pull request, failing early." }
|
||||
# Download most recent Julia Windows binary
|
||||
- ps: (new-object net.webclient).DownloadFile($env:JULIA_URL, "C:\projects\julia-binary.exe")
|
||||
# Run installer silently, output to C:\projects\julia
|
||||
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
|
||||
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
|
||||
|
||||
build_script:
|
||||
# Need to convert from shallow to complete for Pkg.clone to work
|
||||
- IF EXIST .git\shallow (git fetch --unshallow)
|
||||
- C:\projects\julia\bin\julia -e "versioninfo(); Pkg.clone(pwd(), \"Plots\"); Pkg.build(\"Plots\")"
|
||||
- echo "%JL_TEST_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
|
||||
|
||||
test_script:
|
||||
# - C:\projects\julia\bin\julia -e "Pkg.test(\"Plots\")"
|
||||
- C:\projects\julia\bin\julia -e "include(Pkg.dir(\"Plots\", \"test\", \"travis_commands.jl\"))"
|
||||
- echo "%JL_TEST_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"
|
||||
|
||||
# # Uncomment to support code coverage upload. Should only be enabled for packages
|
||||
# # which would have coverage gaps without running on Windows
|
||||
# on_success:
|
||||
# - echo "%JL_CODECOV_SCRIPT%"
|
||||
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
ignore:
|
||||
- "src/backends/inspectdr.jl"
|
||||
- "src/backends/orca.jl"
|
||||
- "src/backends/pgfplots.jl"
|
||||
- "src/backends/plotly.jl"
|
||||
- "src/backends/plotlyjs.jl"
|
||||
- "src/backends/pyplot.jl"
|
||||
- "src/backends/web.jl"
|
||||
- "src/fileio.jl"
|
||||
- "src/ijulia.jl"
|
||||
Vendored
+15
-5
@@ -1,8 +1,18 @@
|
||||
|
||||
#TODO: download https://cdn.plot.ly/plotly-latest.min.js to deps/ if it doesn't exist
|
||||
|
||||
local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
|
||||
if !isfile(local_fn)
|
||||
info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
|
||||
download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
|
||||
file_path = ""
|
||||
if get(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL", "false") == "true"
|
||||
global file_path
|
||||
local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
|
||||
if !isfile(local_fn)
|
||||
@info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
|
||||
download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
|
||||
isfile(local_fn) && (file_path = local_fn)
|
||||
else
|
||||
file_path = local_fn
|
||||
end
|
||||
end
|
||||
|
||||
open("deps.jl", "w") do io
|
||||
println(io, "const plotly_local_file_path = $(repr(file_path))")
|
||||
end
|
||||
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
# To figure out what should be precompiled, run this script, then move
|
||||
# precompile_Plots.jl in precompiles_path (see below) to src/precompile.jl
|
||||
|
||||
# This script works by using SnoopCompile to log compilations that take place
|
||||
# while running the examples on the GR backend. So SnoopCompile must be
|
||||
# installed, and StatsPlots, RDatasets, and FileIO are also required for
|
||||
# certain examples.
|
||||
|
||||
# If precompilation fails with an UndefVarError for a module, probably what is
|
||||
# happening is that the module appears in the precompile statements, but is
|
||||
# only visible to one of Plots' dependencies, and not Plots itself. Adding the
|
||||
# module to the blacklist below will remove these precompile statements.
|
||||
|
||||
# Anonymous functions may appear in precompile statements as functions with
|
||||
# hashes in their name. Those of the form "#something##kw" have to do with
|
||||
# compiling functions with keyword arguments, and are named reproducibly, so
|
||||
# can be kept. Others generally will not work. Currently, SnoopCompile includes
|
||||
# some anonymous functions that not reproducible, but SnoopCompile PR #30
|
||||
# (which looks about to be merged) will ensure that anonymous functions are
|
||||
# actually defined before attempting to precompile them. Alternatively, we can
|
||||
# keep only the keyword argument related anonymous functions by changing the
|
||||
# regex that SnoopCompile uses to detect anonymous functions to
|
||||
# r"#{1,2}[^\"#]+#{1,2}\d+" (see anonrex in SnoopCompile.jl). To exclude all
|
||||
# precompile statements involving anonymous functions, "#" can also be added to
|
||||
# the blacklist below.
|
||||
|
||||
using SnoopCompile
|
||||
|
||||
project_flag = string("--project=", joinpath(homedir(), ".julia", "dev", "Plots"))
|
||||
log_path = joinpath(tempdir(), "compiles.log")
|
||||
precompiles_path = joinpath(tempdir(), "precompile")
|
||||
|
||||
# run examples with GR backend, logging what needs to be compiled
|
||||
SnoopCompile.@snoopc project_flag log_path begin
|
||||
using Plots
|
||||
Plots.test_examples(:gr)
|
||||
Plots.test_examples(:plotly, skip = Plots._backend_skips[:plotly])
|
||||
end
|
||||
|
||||
# precompile calls containing the following strings are dropped
|
||||
blacklist = [
|
||||
# functions defined in examples
|
||||
"PlotExampleModule",
|
||||
# the following are not visible to Plots, only its dependencies
|
||||
"CategoricalArrays",
|
||||
"FixedPointNumbers",
|
||||
"SparseArrays",
|
||||
r"#{1,2}[^\"#]+#{1,2}\d+",
|
||||
]
|
||||
|
||||
data = SnoopCompile.read(log_path)
|
||||
pc = SnoopCompile.parcel(reverse!(data[2]), blacklist=blacklist)
|
||||
SnoopCompile.write(precompiles_path, pc)
|
||||
@@ -1,4 +0,0 @@
|
||||
git checkout master
|
||||
git merge --ff-only dev
|
||||
git push origin master
|
||||
git checkout dev
|
||||
+54
-129
@@ -1,14 +1,21 @@
|
||||
__precompile__(true)
|
||||
|
||||
module Plots
|
||||
|
||||
if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@optlevel"))
|
||||
@eval Base.Experimental.@optlevel 1
|
||||
end
|
||||
|
||||
const _current_plots_version = VersionNumber(split(first(filter(line -> occursin("version", line), readlines(normpath(@__DIR__, "..", "Project.toml")))), "\"")[2])
|
||||
|
||||
using Reexport
|
||||
|
||||
import StaticArrays
|
||||
using StaticArrays.FixedSizeArrays
|
||||
import GeometryTypes
|
||||
using Dates, Printf, Statistics, Base64, LinearAlgebra, Random
|
||||
import SparseArrays: findnz
|
||||
|
||||
using FFMPEG
|
||||
|
||||
@reexport using RecipesBase
|
||||
import RecipesBase: plot, plot!, animate
|
||||
import RecipesBase: plot, plot!, animate, is_explicit
|
||||
using Base.Meta
|
||||
@reexport using PlotUtils
|
||||
@reexport using PlotThemes
|
||||
@@ -18,6 +25,17 @@ import JSON
|
||||
|
||||
using Requires
|
||||
|
||||
if isfile(joinpath(@__DIR__, "..", "deps", "deps.jl"))
|
||||
include(joinpath(@__DIR__, "..", "deps", "deps.jl"))
|
||||
else
|
||||
# This is a bit dirty, but I don't really see why anyone should be forced
|
||||
# to build Plots, while it will just include exactly the below line
|
||||
# as long as `ENV["PLOTS_HOST_DEPENDENCY_LOCAL"] = "true"` is not set.
|
||||
# If the above env is set + `plotly_local_file_path == ""``,
|
||||
# it will warn in the __init__ function to run build
|
||||
const plotly_local_file_path = ""
|
||||
end
|
||||
|
||||
export
|
||||
grid,
|
||||
bbox,
|
||||
@@ -72,7 +90,6 @@ export
|
||||
backends,
|
||||
backend_name,
|
||||
backend_object,
|
||||
add_backend,
|
||||
aliases,
|
||||
|
||||
Shape,
|
||||
@@ -104,8 +121,6 @@ export
|
||||
rotate,
|
||||
rotate!,
|
||||
center,
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve,
|
||||
|
||||
plotattr
|
||||
@@ -118,7 +133,7 @@ ignorenan_minimum(x) = Base.minimum(x)
|
||||
ignorenan_maximum(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.maximum(x)
|
||||
ignorenan_maximum(x) = Base.maximum(x)
|
||||
ignorenan_mean(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.mean(x)
|
||||
ignorenan_mean(x) = Base.mean(x)
|
||||
ignorenan_mean(x) = Statistics.mean(x)
|
||||
ignorenan_extrema(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.extrema(x)
|
||||
ignorenan_extrema(x) = Base.extrema(x)
|
||||
|
||||
@@ -170,122 +185,40 @@ include("arg_desc.jl")
|
||||
include("plotattr.jl")
|
||||
include("backends.jl")
|
||||
include("output.jl")
|
||||
include("ijulia.jl")
|
||||
include("fileio.jl")
|
||||
include("init.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
include("backends/plotly.jl")
|
||||
include("backends/gr.jl")
|
||||
include("backends/web.jl")
|
||||
|
||||
@shorthands scatter
|
||||
@shorthands bar
|
||||
@shorthands barh
|
||||
@shorthands histogram
|
||||
@shorthands barhist
|
||||
@shorthands stephist
|
||||
@shorthands scatterhist
|
||||
@shorthands histogram2d
|
||||
@shorthands density
|
||||
@shorthands heatmap
|
||||
@shorthands hexbin
|
||||
@shorthands sticks
|
||||
@shorthands hline
|
||||
@shorthands vline
|
||||
@shorthands hspan
|
||||
@shorthands vspan
|
||||
@shorthands ohlc
|
||||
@shorthands contour
|
||||
@shorthands contourf
|
||||
@shorthands contour3d
|
||||
@shorthands surface
|
||||
@shorthands wireframe
|
||||
@shorthands path3d
|
||||
@shorthands scatter3d
|
||||
@shorthands boxplot
|
||||
@shorthands violin
|
||||
@shorthands quiver
|
||||
@shorthands curves
|
||||
|
||||
"Plot a pie diagram"
|
||||
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
|
||||
"Plot with seriestype :path3d"
|
||||
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
|
||||
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
|
||||
|
||||
"Add title to an existing plot"
|
||||
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
||||
|
||||
"Add xlabel to an existing plot"
|
||||
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
|
||||
|
||||
"Add ylabel to an existing plot"
|
||||
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
|
||||
|
||||
"Set xlims for an existing plot"
|
||||
xlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; xlims = lims, kw...)
|
||||
|
||||
"Set ylims for an existing plot"
|
||||
ylims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; ylims = lims, kw...)
|
||||
|
||||
"Set zlims for an existing plot"
|
||||
zlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; zlims = lims, kw...)
|
||||
|
||||
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
|
||||
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
|
||||
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
|
||||
|
||||
|
||||
"Set xticks for an existing plot"
|
||||
xticks!(v::AVec{T}; kw...) where {T<:Real} = plot!(; xticks = v, kw...)
|
||||
|
||||
"Set yticks for an existing plot"
|
||||
yticks!(v::AVec{T}; kw...) where {T<:Real} = plot!(; yticks = v, kw...)
|
||||
|
||||
xticks!(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
|
||||
yticks!(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
|
||||
|
||||
"Add annotations to an existing plot"
|
||||
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
|
||||
annotate!(anns::AVec{T}; kw...) where {T<:Tuple} = plot!(; annotation = anns, kw...)
|
||||
|
||||
"Flip the current plots' x axis"
|
||||
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
|
||||
|
||||
"Flip the current plots' y axis"
|
||||
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
|
||||
|
||||
"Specify x axis attributes for an existing plot"
|
||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||
|
||||
"Specify x axis attributes for an existing plot"
|
||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
|
||||
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
|
||||
include("shorthands.jl")
|
||||
|
||||
let PlotOrSubplot = Union{Plot, Subplot}
|
||||
title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||
ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||
xlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; xlims = lims, kw...)
|
||||
ylims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; ylims = lims, kw...)
|
||||
zlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; zlims = lims, kw...)
|
||||
xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||
ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||
zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||
xticks!(plt::PlotOrSubplot, ticks::AVec{T}; kw...) where {T<:Real} = plot!(plt; xticks = ticks, kw...)
|
||||
yticks!(plt::PlotOrSubplot, ticks::AVec{T}; kw...) where {T<:Real} = plot!(plt; yticks = ticks, kw...)
|
||||
xticks!(plt::PlotOrSubplot,
|
||||
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
global xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||
global ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||
global xlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; xlims = lims, kw...)
|
||||
global ylims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; ylims = lims, kw...)
|
||||
global zlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; zlims = lims, kw...)
|
||||
global xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||
global ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||
global zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||
global xticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) where {T<:Real} = plot!(plt; xticks = ticks, kw...)
|
||||
global yticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) where {T<:Real} = plot!(plt; yticks = ticks, kw...)
|
||||
global xticks!(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
yticks!(plt::PlotOrSubplot,
|
||||
global yticks!(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
|
||||
ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
|
||||
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
annotate!(plt::PlotOrSubplot, anns::AVec{T}; kw...) where {T<:Tuple} = plot!(plt; annotation = anns, kw...)
|
||||
xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||
yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||
xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||
yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
global xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
|
||||
global ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
|
||||
global annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
global annotate!(plt::PlotOrSubplot, anns::AVec{T}; kw...) where {T<:Tuple} = plot!(plt; annotation = anns, kw...)
|
||||
global xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||
global yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||
global xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||
global yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
end
|
||||
|
||||
|
||||
@@ -293,15 +226,7 @@ end
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
# for compatibility with Requires.jl:
|
||||
@init begin
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
for (k,v) in Main.PLOTS_DEFAULTS
|
||||
default(k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
include("precompile.jl")
|
||||
_precompile_()
|
||||
|
||||
end # module
|
||||
|
||||
+47
-21
@@ -60,49 +60,75 @@ end
|
||||
|
||||
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
|
||||
|
||||
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim.dir, fn, false; kw...)
|
||||
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn, false; kw...)
|
||||
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim, fn; kw...)
|
||||
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim, fn, false; kw...)
|
||||
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim, fn, false; kw...)
|
||||
|
||||
ffmpeg_framerate(fps) = "$fps"
|
||||
ffmpeg_framerate(fps::Rational) = "$(fps.num)/$(fps.den)"
|
||||
|
||||
function buildanimation(animdir::AbstractString, fn::AbstractString,
|
||||
function buildanimation(anim::Animation, fn::AbstractString,
|
||||
is_animated_gif::Bool=true;
|
||||
fps::Integer = 20, loop::Integer = 0,
|
||||
fps::Real = 20, loop::Integer = 0,
|
||||
variable_palette::Bool=false,
|
||||
verbose=false,
|
||||
show_msg::Bool=true)
|
||||
fn = abspath(fn)
|
||||
if length(anim.frames) == 0
|
||||
throw(ArgumentError("Cannot build empty animations"))
|
||||
end
|
||||
|
||||
fn = abspath(expanduser(fn))
|
||||
animdir = anim.dir
|
||||
framerate = ffmpeg_framerate(fps)
|
||||
verbose_level = (verbose isa Int ? verbose :
|
||||
verbose ? 32 # "info"
|
||||
: 16) # "error"
|
||||
|
||||
if is_animated_gif
|
||||
if variable_palette
|
||||
# generate a colorpalette for each frame for highest quality, but larger filesize
|
||||
palette="palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1"
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
|
||||
ffmpeg_exe(`-v $verbose_level -framerate $framerate -loop $loop -i $(animdir)/%06d.png -lavfi "$palette" -y $fn`)
|
||||
else
|
||||
# generate a colorpalette first so ffmpeg does not have to guess it
|
||||
run(`ffmpeg -v 0 -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
|
||||
ffmpeg_exe(`-v $verbose_level -i $(animdir)/%06d.png -vf "palettegen=stats_mode=diff" -y "$(animdir)/palette.bmp"`)
|
||||
# then apply the palette to get better results
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
|
||||
ffmpeg_exe(`-v $verbose_level -framerate $framerate -loop $loop -i $(animdir)/%06d.png -i "$(animdir)/palette.bmp" -lavfi "paletteuse=dither=sierra2_4a" -y $fn`)
|
||||
end
|
||||
else
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
||||
ffmpeg_exe(`-v $verbose_level -framerate $framerate -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
||||
end
|
||||
|
||||
show_msg && info("Saved animation to ", fn)
|
||||
show_msg && @info("Saved animation to ", fn)
|
||||
AnimatedGif(fn)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
|
||||
# write out html to view the gif
|
||||
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||
ext = file_extension(agif.filename)
|
||||
write(io, if ext == "gif"
|
||||
"<img src=\"$(relpath(agif.filename))?$(rand())>\" />"
|
||||
if ext == "gif"
|
||||
html = "<img src=\"data:image/gif;base64," * base64encode(read(agif.filename)) * "\" />"
|
||||
elseif ext in ("mov", "mp4")
|
||||
"<video controls><source src=\"$(relpath(agif.filename))?$(rand())>\" type=\"video/$ext\"></video>"
|
||||
mimetype = ext == "mov" ? "video/quicktime" : "video/mp4"
|
||||
html = "<video controls><source src=\"data:$mimetype;base64," *
|
||||
base64encode(read(agif.filename)) *
|
||||
"\" type = \"$mimetype\"></video>"
|
||||
else
|
||||
error("Cannot show animation with extension $ext: $agif")
|
||||
end)
|
||||
end
|
||||
|
||||
write(io, html)
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
# Only gifs can be shown via image/gif
|
||||
Base.showable(::MIME"image/gif", agif::AnimatedGif) = file_extension(agif.filename) == "gif"
|
||||
|
||||
function Base.show(io::IO, ::MIME"image/gif", agif::AnimatedGif)
|
||||
open(fio-> write(io, fio), agif.filename)
|
||||
end
|
||||
|
||||
|
||||
@@ -141,17 +167,17 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
error("Unsupported animate filter: $args")
|
||||
end
|
||||
|
||||
push!(block.args, :(if $filterexpr; frame($animsym); end))
|
||||
push!(block.args, :($countersym += 1))
|
||||
push!(block.args, :(if $filterexpr; Plots.frame($animsym); end))
|
||||
push!(block.args, :(global $countersym += 1))
|
||||
|
||||
# add a final call to `gif(anim)`?
|
||||
retval = callgif ? :(gif($animsym)) : animsym
|
||||
retval = callgif ? :(Plots.gif($animsym)) : animsym
|
||||
|
||||
# full expression:
|
||||
esc(quote
|
||||
$freqassert # if filtering, check frequency is an Integer > 0
|
||||
$animsym = Animation() # init animation object
|
||||
$countersym = 1 # init iteration counter
|
||||
$animsym = Plots.Animation() # init animation object
|
||||
global $countersym = 1 # init iteration counter
|
||||
$forloop # for loop, saving a frame after each iteration
|
||||
$retval # return the animation object, or the gif
|
||||
end)
|
||||
|
||||
+48
-28
@@ -2,7 +2,7 @@
|
||||
const _arg_desc = KW(
|
||||
|
||||
# series args
|
||||
:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
|
||||
:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
|
||||
:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot",
|
||||
:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.",
|
||||
:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.",
|
||||
@@ -10,7 +10,7 @@ const _arg_desc = KW(
|
||||
:linewidth => "Number. Width of the line (in pixels)",
|
||||
:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).",
|
||||
:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.",
|
||||
:fillrange => "Number or AbstractVector. Fills area from this to y for line-types, sets the base for bar/stick types, and similar for other types.",
|
||||
:fillrange => "Number or AbstractVector. Fills area between fillrange and y for line-types, sets the base for bar/stick types, and similar for other types.",
|
||||
:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.",
|
||||
:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.",
|
||||
:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).",
|
||||
@@ -21,34 +21,36 @@ const _arg_desc = KW(
|
||||
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
||||
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
|
||||
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `linspace(extrema(x)..., 25)`",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`",
|
||||
:smooth => "Bool. Add a regression line?",
|
||||
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
|
||||
:x => "Various. Input data. First Dimension",
|
||||
:y => "Various. Input data. Second Dimension",
|
||||
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
|
||||
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
||||
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
|
||||
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
|
||||
:levels => "Integer, NTuple{2,Integer}. Number of levels (or x-levels/y-levels) for a contour type.",
|
||||
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
||||
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or Function `f(x,y) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
|
||||
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
|
||||
:levels => "Integer, NTuple{2,Integer}, or AbstractVector. Levels or number of levels (or x-levels/y-levels) for a contour type.",
|
||||
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
|
||||
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
|
||||
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
|
||||
:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?",
|
||||
:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
|
||||
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
|
||||
:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?",
|
||||
:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
|
||||
:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)",
|
||||
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
|
||||
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
|
||||
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
|
||||
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete Probability Density Function, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes).",
|
||||
:weights => "AbstractVector. Used in histogram types for weighted counts.",
|
||||
:show_empty_bins => "Bool. Whether empty bins in a 2D histogram are colored as 0 (true), or transparent (the default).",
|
||||
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
|
||||
:contour_labels => "Bool. Show labels at the contour lines?",
|
||||
:contour_labels => "Bool. Show labels at the contour lines?",
|
||||
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
|
||||
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
|
||||
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
|
||||
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
|
||||
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
|
||||
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
|
||||
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
|
||||
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
|
||||
:colorbar_entry => "Bool. Include this series in the color bar? Set to `false` to exclude.",
|
||||
|
||||
# plot args
|
||||
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
|
||||
@@ -63,10 +65,12 @@ const _arg_desc = KW(
|
||||
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
|
||||
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
|
||||
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
|
||||
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
|
||||
:dpi => "Number. Dots Per Inch of output figures",
|
||||
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
|
||||
:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
|
||||
:tex_output_standalone => "Bool. When writing tex output, should the source include a preamble for a standalone document class.",
|
||||
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
|
||||
:dpi => "Number. Dots Per Inch of output figures",
|
||||
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
|
||||
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
|
||||
:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
|
||||
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
|
||||
|
||||
# subplot args
|
||||
@@ -85,19 +89,26 @@ const _arg_desc = KW(
|
||||
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
|
||||
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
|
||||
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
|
||||
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
|
||||
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` , `:inline` (note: only some may be supported in each backend)",
|
||||
:legendfontfamily => "String or Symbol. Font family of legend entries.",
|
||||
:legendfontsize => "Integer. Font pointsize of legend entries.",
|
||||
:legendfonthalign => "Symbol. Font horizontal alignment of legend entries: :hcenter, :left, :right or :center",
|
||||
:legendfontvalign => "Symbol. Font vertical alignment of legend entries: :vcenter, :top, :bottom or :center",
|
||||
:legendfontrotation => "Real. Font rotation of legend entries",
|
||||
:legendfontcolor => "Color Type. Font color of legend entries",
|
||||
:legendtitlefontfamily => "String or Symbol. Font family of the legend title.",
|
||||
:legendtitlefontsize => "Integer. Font pointsize the legend title.",
|
||||
:legendtitlefonthalign => "Symbol. Font horizontal alignment of the legend title: :hcenter, :left, :right or :center",
|
||||
:legendtitlefontvalign => "Symbol. Font vertical alignment of the legend title: :vcenter, :top, :bottom or :center",
|
||||
:legendtitlefontrotation => "Real. Font rotation of the legend title",
|
||||
:legendtitlefontcolor => "Color Type. Font color of the legend title",
|
||||
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
|
||||
:clims => "`:auto` or NTuple{2,Number}. Fixes the limits of the colorbar.",
|
||||
:legendfont => "Font. Font of legend items.",
|
||||
:legendtitlefont => "Font. Font of the legend title.",
|
||||
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
|
||||
:projection => "Symbol or String. '3d' or 'polar'",
|
||||
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `apect_ratio` x-units.",
|
||||
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units.",
|
||||
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
|
||||
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
|
||||
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
|
||||
@@ -109,13 +120,14 @@ const _arg_desc = KW(
|
||||
:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots",
|
||||
|
||||
# axis args
|
||||
:guide => "String. Axis guide (label).",
|
||||
:lims => "NTuple{2,Number}. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).",
|
||||
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
|
||||
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
|
||||
:rotation => "Number. Degrees rotation of tick labels.",
|
||||
:flip => "Bool. Should we flip (reverse) the axis?",
|
||||
:formatter => "Function, :scientific, or :auto. A method which converts a number to a string for tick labeling.",
|
||||
:guide => "String. Axis guide (label).",
|
||||
:guide_position => "Symbol. Position of axis guides: :top, :bottom, :left or :right",
|
||||
:lims => "NTuple{2,Number} or Symbol. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example). `:round` widens the limit to the nearest round number ie. [0.1,3.6]=>[0.0,4.0]",
|
||||
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
|
||||
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
|
||||
:rotation => "Number. Degrees rotation of tick labels.",
|
||||
:flip => "Bool. Should we flip (reverse) the axis?",
|
||||
:formatter => "Function, :scientific, :plain or :auto. A method which converts a number to a string for tick labeling.",
|
||||
:tickfontfamily => "String or Symbol. Font family of tick labels.",
|
||||
:tickfontsize => "Integer. Font pointsize of tick labels.",
|
||||
:tickfonthalign => "Symbol. Font horizontal alignment of tick labels: :hcenter, :left, :right or :center",
|
||||
@@ -138,6 +150,14 @@ const _arg_desc = KW(
|
||||
:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.",
|
||||
:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)",
|
||||
:gridlinewidth => "Number. Width of the grid lines (in pixels)",
|
||||
:foreground_color_minor_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of minor grid lines.",
|
||||
:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines",
|
||||
:minorticks => "Integer. Intervals to divide the gap between major ticks into",
|
||||
:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.",
|
||||
:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)",
|
||||
:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)",
|
||||
:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`",
|
||||
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`"
|
||||
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
|
||||
:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.",
|
||||
:draw_arrow => "Bool. Draw arrow at the end of the axis.",
|
||||
)
|
||||
|
||||
+480
-352
File diff suppressed because it is too large
Load Diff
+515
-148
@@ -7,29 +7,20 @@
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
# init with values from _plot_defaults
|
||||
d = KW(
|
||||
explicit = KW(
|
||||
:letter => letter,
|
||||
# :extrema => (Inf, -Inf),
|
||||
:extrema => Extrema(),
|
||||
:discrete_map => Dict(), # map discrete values to discrete indices
|
||||
:continuous_values => zeros(0),
|
||||
:discrete_values => [],
|
||||
:use_minor => false,
|
||||
:show => true, # show or hide the axis? (useful for linked subplots)
|
||||
)
|
||||
|
||||
# get defaults from letter version, unless match
|
||||
for (k,v) in _axis_defaults
|
||||
lk = Symbol(letter, k)
|
||||
lv = _axis_defaults_byletter[lk]
|
||||
d[k] = (lv == :match ? v : lv)
|
||||
end
|
||||
|
||||
# merge!(d, _axis_defaults)
|
||||
d[:discrete_values] = []
|
||||
attr = Attr(explicit, _axis_defaults_byletter[letter])
|
||||
|
||||
# update the defaults
|
||||
attr!(Axis([sp], d), args...; kw...)
|
||||
attr!(Axis([sp], attr), args...; kw...)
|
||||
end
|
||||
|
||||
function get_axis(sp::Subplot, letter::Symbol)
|
||||
@@ -41,46 +32,46 @@ function get_axis(sp::Subplot, letter::Symbol)
|
||||
end::Axis
|
||||
end
|
||||
|
||||
function process_axis_arg!(d::KW, arg, letter = "")
|
||||
function process_axis_arg!(plotattributes::AKW, arg, letter = "")
|
||||
T = typeof(arg)
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
if typeof(arg) <: Font
|
||||
d[Symbol(letter,:tickfont)] = arg
|
||||
d[Symbol(letter,:guidefont)] = arg
|
||||
plotattributes[Symbol(letter,:tickfont)] = arg
|
||||
plotattributes[Symbol(letter,:guidefont)] = arg
|
||||
|
||||
elseif arg in _allScales
|
||||
d[Symbol(letter,:scale)] = arg
|
||||
plotattributes[Symbol(letter,:scale)] = arg
|
||||
|
||||
elseif arg in (:flip, :invert, :inverted)
|
||||
d[Symbol(letter,:flip)] = true
|
||||
plotattributes[Symbol(letter,:flip)] = true
|
||||
|
||||
elseif T <: AbstractString
|
||||
d[Symbol(letter,:guide)] = arg
|
||||
plotattributes[Symbol(letter,:guide)] = arg
|
||||
|
||||
# xlims/ylims
|
||||
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
|
||||
sym = typeof(arg[1]) <: Number ? :lims : :ticks
|
||||
d[Symbol(letter,sym)] = arg
|
||||
plotattributes[Symbol(letter,sym)] = arg
|
||||
|
||||
# xticks/yticks
|
||||
elseif T <: AVec
|
||||
d[Symbol(letter,:ticks)] = arg
|
||||
plotattributes[Symbol(letter,:ticks)] = arg
|
||||
|
||||
elseif arg == nothing
|
||||
d[Symbol(letter,:ticks)] = []
|
||||
elseif arg === nothing
|
||||
plotattributes[Symbol(letter,:ticks)] = []
|
||||
|
||||
elseif T <: Bool || arg in _allShowaxisArgs
|
||||
d[Symbol(letter,:showaxis)] = showaxis(arg, letter)
|
||||
plotattributes[Symbol(letter,:showaxis)] = showaxis(arg, letter)
|
||||
|
||||
elseif typeof(arg) <: Number
|
||||
d[Symbol(letter,:rotation)] = arg
|
||||
plotattributes[Symbol(letter,:rotation)] = arg
|
||||
|
||||
elseif typeof(arg) <: Function
|
||||
d[Symbol(letter,:formatter)] = arg
|
||||
plotattributes[Symbol(letter,:formatter)] = arg
|
||||
|
||||
elseif !handleColors!(d, arg, Symbol(letter, :foreground_color_axis))
|
||||
warn("Skipped $(letter)axis arg $arg")
|
||||
elseif !handleColors!(plotattributes, arg, Symbol(letter, :foreground_color_axis))
|
||||
@warn("Skipped $(letter)axis arg $arg")
|
||||
|
||||
end
|
||||
end
|
||||
@@ -88,28 +79,31 @@ end
|
||||
# update an Axis object with magic args and keywords
|
||||
function attr!(axis::Axis, args...; kw...)
|
||||
# first process args
|
||||
d = axis.d
|
||||
plotattributes = axis.plotattributes
|
||||
for arg in args
|
||||
process_axis_arg!(d, arg)
|
||||
process_axis_arg!(plotattributes, arg)
|
||||
end
|
||||
|
||||
# then override for any keywords... only those keywords that already exists in d
|
||||
# then preprocess keyword arguments
|
||||
preprocessArgs!(KW(kw))
|
||||
|
||||
# then override for any keywords... only those keywords that already exists in plotattributes
|
||||
for (k,v) in kw
|
||||
if haskey(d, k)
|
||||
if haskey(plotattributes, k)
|
||||
if k == :discrete_values
|
||||
# add these discrete values to the axis
|
||||
for vi in v
|
||||
discrete_value!(axis, vi)
|
||||
end
|
||||
else
|
||||
d[k] = v
|
||||
plotattributes[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# replace scale aliases
|
||||
if haskey(_scaleAliases, d[:scale])
|
||||
d[:scale] = _scaleAliases[d[:scale]]
|
||||
if haskey(_scaleAliases, plotattributes[:scale])
|
||||
plotattributes[:scale] = _scaleAliases[plotattributes[:scale]]
|
||||
end
|
||||
|
||||
axis
|
||||
@@ -117,10 +111,10 @@ end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Base.show(io::IO, axis::Axis) = dumpdict(axis.d, "Axis", true)
|
||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.d, k)
|
||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
|
||||
Base.show(io::IO, axis::Axis) = dumpdict(io, axis.plotattributes, "Axis", true)
|
||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.plotattributes, k)
|
||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.plotattributes, k)
|
||||
ignorenan_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
|
||||
|
||||
@@ -152,8 +146,8 @@ scalefunc(scale::Symbol) = x -> get(_scale_funcs, scale, identity)(Float64(x))
|
||||
invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float64(x))
|
||||
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
|
||||
|
||||
function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
amin,amax = axis_limits(axis)
|
||||
function optimal_ticks_and_labels(sp::Subplot, axis::Axis, ticks = nothing)
|
||||
amin, amax = axis_limits(sp, axis[:letter])
|
||||
|
||||
# scale the limits
|
||||
scale = axis[:scale]
|
||||
@@ -166,7 +160,7 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
# or DateTime) is chosen based on the time span between amin and amax
|
||||
# rather than on the input format
|
||||
# TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime
|
||||
if ticks == nothing && scale == :identity
|
||||
if ticks === nothing && scale == :identity
|
||||
if axis[:formatter] == dateformatter
|
||||
# optimize_datetime_ticks returns ticks and labels(!) based on
|
||||
# integers/floats corresponding to the DateTime type. Thus, the axes
|
||||
@@ -184,7 +178,7 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
end
|
||||
|
||||
# get a list of well-laid-out ticks
|
||||
if ticks == nothing
|
||||
if ticks === nothing
|
||||
scaled_ticks = optimize_ticks(
|
||||
sf(amin),
|
||||
sf(amax);
|
||||
@@ -212,12 +206,22 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
formatter = axis[:formatter]
|
||||
if formatter == :auto
|
||||
# the default behavior is to make strings of the scaled values and then apply the labelfunc
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :auto))
|
||||
elseif formatter == :plain
|
||||
# Leave the numbers in plain format
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :plain))
|
||||
elseif formatter == :scientific
|
||||
Showoff.showoff(unscaled_ticks, :scientific)
|
||||
elseif formatter == :latex
|
||||
map(x -> string("\$", replace(convert_sci_unicode(x), '×' => "\\times"), "\$"), Showoff.showoff(unscaled_ticks, :auto))
|
||||
else
|
||||
# there was an override for the formatter... use that on the unscaled ticks
|
||||
map(formatter, unscaled_ticks)
|
||||
# if the formatter left us with numbers, still apply the default formatter
|
||||
# However it leave us with the problem of unicode number decoding by the backend
|
||||
# if eltype(unscaled_ticks) <: Number
|
||||
# Showoff.showoff(unscaled_ticks, :auto)
|
||||
# end
|
||||
end
|
||||
else
|
||||
# no finite ticks to show...
|
||||
@@ -230,36 +234,39 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
end
|
||||
|
||||
# return (continuous_values, discrete_values) for the ticks on this axis
|
||||
function get_ticks(axis::Axis)
|
||||
function get_ticks(sp::Subplot, axis::Axis)
|
||||
ticks = _transform_ticks(axis[:ticks])
|
||||
ticks in (nothing, false) && return nothing
|
||||
ticks in (:none, nothing, false) && return nothing
|
||||
|
||||
# treat :native ticks as :auto
|
||||
ticks = ticks == :native ? :auto : ticks
|
||||
|
||||
dvals = axis[:discrete_values]
|
||||
cv, dv = if !isempty(dvals)
|
||||
# discrete ticks...
|
||||
n = length(dvals)
|
||||
rng = if ticks == :auto
|
||||
Int[round(Int,i) for i in linspace(1, n, 15)]
|
||||
elseif ticks == :all
|
||||
1:n
|
||||
elseif typeof(ticks) <: Int
|
||||
Int[round(Int,i) for i in linspace(1, n, ticks)]
|
||||
end
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
elseif typeof(ticks) <: Symbol
|
||||
if ispolar(axis.sps[1]) && axis[:letter] == :x
|
||||
cv, dv = if typeof(ticks) <: Symbol
|
||||
if !isempty(dvals)
|
||||
# discrete ticks...
|
||||
n = length(dvals)
|
||||
rng = if ticks == :auto
|
||||
Int[round(Int,i) for i in range(1, stop=n, length=min(n,15))]
|
||||
else # if ticks == :all
|
||||
1:n
|
||||
end
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
elseif ispolar(axis.sps[1]) && axis[:letter] == :x
|
||||
#force theta axis to be full circle
|
||||
(collect(0:pi/4:7pi/4), string.(0:45:315))
|
||||
else
|
||||
# compute optimal ticks and labels
|
||||
optimal_ticks_and_labels(axis)
|
||||
optimal_ticks_and_labels(sp, axis)
|
||||
end
|
||||
elseif typeof(ticks) <: Union{AVec, Int}
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
if !isempty(dvals) && typeof(ticks) <: Int
|
||||
rng = Int[round(Int,i) for i in range(1, stop=length(dvals), length=ticks)]
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
else
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(sp, axis, ticks)
|
||||
end
|
||||
elseif typeof(ticks) <: NTuple{2, Any}
|
||||
# assuming we're passed (ticks, labels)
|
||||
ticks
|
||||
@@ -275,6 +282,36 @@ _transform_ticks(ticks) = ticks
|
||||
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
|
||||
_transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
|
||||
|
||||
function get_minor_ticks(sp, axis, ticks)
|
||||
axis[:minorticks] in (:none, nothing, false) && !axis[:minorgrid] && return nothing
|
||||
ticks = ticks[1]
|
||||
length(ticks) < 2 && return nothing
|
||||
|
||||
amin, amax = axis_limits(sp, axis[:letter])
|
||||
#Add one phantom tick either side of the ticks to ensure minor ticks extend to the axis limits
|
||||
if length(ticks) > 2
|
||||
ratio = (ticks[3] - ticks[2])/(ticks[2] - ticks[1])
|
||||
elseif axis[:scale] in (:none, :identity)
|
||||
ratio = 1
|
||||
else
|
||||
return nothing
|
||||
end
|
||||
first_step = ticks[2] - ticks[1]
|
||||
last_step = ticks[end] - ticks[end-1]
|
||||
ticks = [ticks[1] - first_step/ratio; ticks; ticks[end] + last_step*ratio]
|
||||
|
||||
#Default to 5 intervals between major ticks
|
||||
n = typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] : 5
|
||||
minorticks = typeof(ticks[1])[]
|
||||
for (i,hi) in enumerate(ticks[2:end])
|
||||
lo = ticks[i]
|
||||
if isfinite(lo) && hi > lo
|
||||
append!(minorticks,collect(lo + (hi-lo)/n :(hi-lo)/n: hi - (hi-lo)/2n))
|
||||
end
|
||||
end
|
||||
minorticks[amin .<= minorticks .<= amax]
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -283,7 +320,7 @@ function reset_extrema!(sp::Subplot)
|
||||
sp[Symbol(asym,:axis)][:extrema] = Extrema()
|
||||
end
|
||||
for series in sp.series_list
|
||||
expand_extrema!(sp, series.d)
|
||||
expand_extrema!(sp, series.plotattributes)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -299,7 +336,7 @@ function expand_extrema!(axis::Axis, v::Number)
|
||||
end
|
||||
|
||||
# these shouldn't impact the extrema
|
||||
expand_extrema!(axis::Axis, ::Void) = axis[:extrema]
|
||||
expand_extrema!(axis::Axis, ::Nothing) = axis[:extrema]
|
||||
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
|
||||
|
||||
|
||||
@@ -318,17 +355,17 @@ function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(sp::Subplot, d::KW)
|
||||
vert = isvertical(d)
|
||||
function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
vert = isvertical(plotattributes)
|
||||
|
||||
# first expand for the data
|
||||
for letter in (:x, :y, :z)
|
||||
data = d[if vert
|
||||
data = plotattributes[if vert
|
||||
letter
|
||||
else
|
||||
letter == :x ? :y : letter == :y ? :x : :z
|
||||
end]
|
||||
if letter != :z && d[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
|
||||
if letter != :z && plotattributes[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
|
||||
data = [NaN]
|
||||
end
|
||||
axis = sp[Symbol(letter, "axis")]
|
||||
@@ -340,30 +377,30 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
elseif eltype(data) <: Number || (isa(data, Surface) && all(di -> isa(di, Number), data.surf))
|
||||
if !(eltype(data) <: Number)
|
||||
# huh... must have been a mis-typed surface? lets swap it out
|
||||
data = d[letter] = Surface(Matrix{Float64}(data.surf))
|
||||
data = plotattributes[letter] = Surface(Matrix{Float64}(data.surf))
|
||||
end
|
||||
expand_extrema!(axis, data)
|
||||
elseif data != nothing
|
||||
elseif data !== nothing
|
||||
# TODO: need more here... gotta track the discrete reference value
|
||||
# as well as any coord offset (think of boxplot shape coords... they all
|
||||
# correspond to the same x-value)
|
||||
d[letter], d[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
|
||||
expand_extrema!(axis, d[letter])
|
||||
plotattributes[letter], plotattributes[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
|
||||
expand_extrema!(axis, plotattributes[letter])
|
||||
end
|
||||
end
|
||||
|
||||
# # expand for fillrange/bar_width
|
||||
# fillaxis, baraxis = sp.attr[:yaxis], sp.attr[:xaxis]
|
||||
# if isvertical(d)
|
||||
# if isvertical(plotattributes)
|
||||
# fillaxis, baraxis = baraxis, fillaxis
|
||||
# end
|
||||
|
||||
# expand for fillrange
|
||||
fr = d[:fillrange]
|
||||
if fr == nothing && d[:seriestype] == :bar
|
||||
fr = plotattributes[:fillrange]
|
||||
if fr === nothing && plotattributes[:seriestype] == :bar
|
||||
fr = 0.0
|
||||
end
|
||||
if fr != nothing && !all3D(d)
|
||||
if fr !== nothing && !all3D(plotattributes)
|
||||
axis = sp.attr[vert ? :yaxis : :xaxis]
|
||||
if typeof(fr) <: Tuple
|
||||
for fri in fr
|
||||
@@ -375,13 +412,13 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
end
|
||||
|
||||
# expand for bar_width
|
||||
if d[:seriestype] == :bar
|
||||
if plotattributes[:seriestype] == :bar
|
||||
dsym = vert ? :x : :y
|
||||
data = d[dsym]
|
||||
data = plotattributes[dsym]
|
||||
|
||||
bw = d[:bar_width]
|
||||
if bw == nothing
|
||||
bw = d[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
|
||||
bw = plotattributes[:bar_width]
|
||||
if bw === nothing
|
||||
bw = plotattributes[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
|
||||
end
|
||||
axis = sp.attr[Symbol(dsym, :axis)]
|
||||
expand_extrema!(axis, ignorenan_maximum(data) + 0.5maximum(bw))
|
||||
@@ -389,11 +426,11 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
end
|
||||
|
||||
# expand for heatmaps
|
||||
if d[:seriestype] == :heatmap
|
||||
if plotattributes[:seriestype] == :heatmap
|
||||
for letter in (:x, :y)
|
||||
data = d[letter]
|
||||
data = plotattributes[letter]
|
||||
axis = sp[Symbol(letter, "axis")]
|
||||
scale = get(d, Symbol(letter, "scale"), :identity)
|
||||
scale = get(plotattributes, Symbol(letter, "scale"), :identity)
|
||||
expand_extrema!(axis, heatmap_edges(data, scale))
|
||||
end
|
||||
end
|
||||
@@ -407,21 +444,23 @@ end
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# push the limits out slightly
|
||||
function widen(lmin, lmax)
|
||||
span = lmax - lmin
|
||||
function widen(lmin, lmax, scale = :identity)
|
||||
f, invf = scalefunc(scale), invscalefunc(scale)
|
||||
span = f(lmax) - f(lmin)
|
||||
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
|
||||
eps = NaNMath.max(1e-16, 0.03span)
|
||||
lmin-eps, lmax+eps
|
||||
invf(f(lmin)-eps), invf(f(lmax)+eps)
|
||||
end
|
||||
|
||||
# figure out if widening is a good idea. if there's a scale set it's too tricky,
|
||||
# so lazy out and don't widen
|
||||
# figure out if widening is a good idea.
|
||||
const _widen_seriestypes = (:line, :path, :steppre, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d)
|
||||
|
||||
function default_should_widen(axis::Axis)
|
||||
should_widen = false
|
||||
if axis[:scale] == :identity && !is_2tuple(axis[:lims])
|
||||
if !(is_2tuple(axis[:lims]) || axis[:lims] == :round)
|
||||
for sp in axis.sps
|
||||
for series in series_list(sp)
|
||||
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
|
||||
if series.plotattributes[:seriestype] in _widen_seriestypes
|
||||
should_widen = true
|
||||
end
|
||||
end
|
||||
@@ -430,12 +469,21 @@ function default_should_widen(axis::Axis)
|
||||
should_widen
|
||||
end
|
||||
|
||||
function round_limits(amin,amax)
|
||||
scale = 10^(1-round(log10(amax - amin)))
|
||||
amin = floor(amin*scale)/scale
|
||||
amax = ceil(amax*scale)/scale
|
||||
amin, amax
|
||||
end
|
||||
|
||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
|
||||
function axis_limits(sp, letter, should_widen = default_should_widen(sp[Symbol(letter, :axis)]), consider_aspect = true)
|
||||
axis = sp[Symbol(letter, :axis)]
|
||||
ex = axis[:extrema]
|
||||
amin, amax = ex.emin, ex.emax
|
||||
lims = axis[:lims]
|
||||
if (isa(lims, Tuple) || isa(lims, AVec)) && length(lims) == 2
|
||||
has_user_lims = (isa(lims, Tuple) || isa(lims, AVec)) && length(lims) == 2
|
||||
if has_user_lims
|
||||
if isfinite(lims[1])
|
||||
amin = lims[1]
|
||||
end
|
||||
@@ -449,20 +497,48 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
|
||||
if !isfinite(amin) && !isfinite(amax)
|
||||
amin, amax = 0.0, 1.0
|
||||
end
|
||||
if ispolar(axis.sps[1])
|
||||
amin, amax = if ispolar(axis.sps[1])
|
||||
if axis[:letter] == :x
|
||||
amin, amax = 0, 2pi
|
||||
elseif lims == :auto
|
||||
#widen max radius so ticks dont overlap with theta axis
|
||||
amin, amax + 0.1 * abs(amax - amin)
|
||||
0, amax + 0.1 * abs(amax - amin)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
elseif should_widen
|
||||
widen(amin, amax)
|
||||
elseif should_widen && axis[:widen]
|
||||
widen(amin, amax, axis[:scale])
|
||||
elseif lims == :round
|
||||
round_limits(amin,amax)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
|
||||
if !has_user_lims && consider_aspect && letter in (:x, :y) && !(sp[:aspect_ratio] in (:none, :auto) || is3d(:sp))
|
||||
aspect_ratio = isa(sp[:aspect_ratio], Number) ? sp[:aspect_ratio] : 1
|
||||
plot_ratio = height(plotarea(sp)) / width(plotarea(sp))
|
||||
dist = amax - amin
|
||||
|
||||
if letter == :x
|
||||
yamin, yamax = axis_limits(sp, :y, default_should_widen(sp[:yaxis]), false)
|
||||
ydist = yamax - yamin
|
||||
axis_ratio = aspect_ratio * ydist / dist
|
||||
factor = axis_ratio / plot_ratio
|
||||
else
|
||||
xamin, xamax = axis_limits(sp, :x, default_should_widen(sp[:xaxis]), false)
|
||||
xdist = xamax - xamin
|
||||
axis_ratio = aspect_ratio * dist / xdist
|
||||
factor = plot_ratio / axis_ratio
|
||||
end
|
||||
|
||||
if factor > 1
|
||||
center = (amin + amax) / 2
|
||||
amin = center + factor * (amin - center)
|
||||
amax = center + factor * (amax - center)
|
||||
end
|
||||
end
|
||||
|
||||
return amin, amax
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -495,10 +571,10 @@ end
|
||||
|
||||
# add the discrete value for each item. return the continuous values and the indices
|
||||
function discrete_value!(axis::Axis, v::AVec)
|
||||
n = length(v)
|
||||
cvec = zeros(n)
|
||||
discrete_indices = zeros(Int, n)
|
||||
for i=1:n
|
||||
n = eachindex(v)
|
||||
cvec = zeros(axes(v))
|
||||
discrete_indices = similar(Array{Int}, axes(v))
|
||||
for i in n
|
||||
cvec[i], discrete_indices[i] = discrete_value!(axis, v[i])
|
||||
end
|
||||
cvec, discrete_indices
|
||||
@@ -506,10 +582,10 @@ end
|
||||
|
||||
# add the discrete value for each item. return the continuous values and the indices
|
||||
function discrete_value!(axis::Axis, v::AMat)
|
||||
n,m = size(v)
|
||||
cmat = zeros(n,m)
|
||||
discrete_indices = zeros(Int, n, m)
|
||||
for i=1:n, j=1:m
|
||||
n,m = axes(v)
|
||||
cmat = zeros(axes(v))
|
||||
discrete_indices = similar(Array{Int}, axes(v))
|
||||
for i in n, j in m
|
||||
cmat[i,j], discrete_indices[i,j] = discrete_value!(axis, v[i,j])
|
||||
end
|
||||
cmat, discrete_indices
|
||||
@@ -522,12 +598,12 @@ end
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function pie_labels(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
if haskey(d,:x_discrete_indices)
|
||||
dvals = sp.attr[:xaxis].d[:discrete_values]
|
||||
[dvals[idx] for idx in d[:x_discrete_indices]]
|
||||
plotattributes = series.plotattributes
|
||||
if haskey(plotattributes,:x_discrete_indices)
|
||||
dvals = sp.attr[:xaxis].plotattributes[:discrete_values]
|
||||
[dvals[idx] for idx in plotattributes[:x_discrete_indices]]
|
||||
else
|
||||
d[:x]
|
||||
plotattributes[:x]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -536,98 +612,389 @@ end
|
||||
# compute the line segments which should be drawn for this axis
|
||||
function axis_drawing_info(sp::Subplot)
|
||||
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
||||
xmin, xmax = axis_limits(xaxis)
|
||||
ymin, ymax = axis_limits(yaxis)
|
||||
xticks = get_ticks(xaxis)
|
||||
yticks = get_ticks(yaxis)
|
||||
xmin, xmax = axis_limits(sp, :x)
|
||||
ymin, ymax = axis_limits(sp, :y)
|
||||
xticks = get_ticks(sp, xaxis)
|
||||
yticks = get_ticks(sp, yaxis)
|
||||
xminorticks = get_minor_ticks(sp, xaxis, xticks)
|
||||
yminorticks = get_minor_ticks(sp, yaxis, yticks)
|
||||
xaxis_segs = Segments(2)
|
||||
yaxis_segs = Segments(2)
|
||||
xtick_segs = Segments(2)
|
||||
ytick_segs = Segments(2)
|
||||
xgrid_segs = Segments(2)
|
||||
ygrid_segs = Segments(2)
|
||||
xminorgrid_segs = Segments(2)
|
||||
yminorgrid_segs = Segments(2)
|
||||
xborder_segs = Segments(2)
|
||||
yborder_segs = Segments(2)
|
||||
|
||||
if sp[:framestyle] != :none
|
||||
# xaxis
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
if xaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
push!(xaxis_segs, (xmin, y1), (xmax, y1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(xticks in (nothing,false)) && length(xticks) > 1
|
||||
if sp[:framestyle] == :origin && !(xticks in (:none, nothing, false)) && length(xticks) > 1
|
||||
showticks = xticks[1] .!= 0
|
||||
xticks = (xticks[1][showticks], xticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2), (xmax, y2)) # top spine
|
||||
end
|
||||
if !(xaxis[:ticks] in (nothing, false))
|
||||
if !(xaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(ymin) + 0.015 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t2 = invf(f(ymax) - 0.015 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.015 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(y1) + 0.012 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
|
||||
for xtick in xticks[1]
|
||||
if xaxis[:showaxis]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
(0, t3)
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, t2) : (ymin, t1)
|
||||
end
|
||||
push!(xtick_segs, (xtick, tick_start), (xtick, tick_stop)) # bottom tick
|
||||
end
|
||||
# sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick
|
||||
xaxis[:grid] && push!(xgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
|
||||
end
|
||||
|
||||
if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(y1) + 0.006 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
for xtick in xminorticks
|
||||
if xaxis[:showaxis]
|
||||
push!(xtick_segs, (xtick, tick_start), (xtick, tick_stop)) # bottom tick
|
||||
end
|
||||
xaxis[:minorgrid] && push!(xminorgrid_segs, (xtick, ymin), (xtick, ymax)) # vertical grid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# yaxis
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
|
||||
end
|
||||
if yaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
|
||||
end
|
||||
push!(yaxis_segs, (x1, ymin), (x1, ymax))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(yticks in (nothing,false)) && length(yticks) > 1
|
||||
if sp[:framestyle] == :origin && !(yticks in (:none, nothing,false)) && length(yticks) > 1
|
||||
showticks = yticks[1] .!= 0
|
||||
yticks = (yticks[1][showticks], yticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin), (x2, ymax)) # right spine
|
||||
end
|
||||
if !(yaxis[:ticks] in (nothing, false))
|
||||
if !(yaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(xmin) + 0.015 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t2 = invf(f(xmax) - 0.015 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.015 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(x1) + 0.012 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
|
||||
for ytick in yticks[1]
|
||||
if yaxis[:showaxis]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
(0, t3)
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmax, t2) : (xmin, t1)
|
||||
end
|
||||
push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick
|
||||
end
|
||||
# sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick
|
||||
yaxis[:grid] && push!(ygrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||
end
|
||||
|
||||
if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(x1) + 0.006 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
for ytick in yminorticks
|
||||
if yaxis[:showaxis]
|
||||
push!(ytick_segs, (tick_start, ytick), (tick_stop, ytick)) # left tick
|
||||
end
|
||||
yaxis[:minorgrid] && push!(yminorgrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs
|
||||
xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs
|
||||
end
|
||||
|
||||
|
||||
function axis_drawing_info_3d(sp::Subplot)
|
||||
xaxis, yaxis, zaxis = sp[:xaxis], sp[:yaxis], sp[:zaxis]
|
||||
xmin, xmax = axis_limits(sp, :x)
|
||||
ymin, ymax = axis_limits(sp, :y)
|
||||
zmin, zmax = axis_limits(sp, :z)
|
||||
xticks = get_ticks(sp, xaxis)
|
||||
yticks = get_ticks(sp, yaxis)
|
||||
zticks = get_ticks(sp, zaxis)
|
||||
xminorticks = get_minor_ticks(sp, xaxis, xticks)
|
||||
yminorticks = get_minor_ticks(sp, yaxis, yticks)
|
||||
zminorticks = get_minor_ticks(sp, zaxis, zticks)
|
||||
xaxis_segs = Segments(3)
|
||||
yaxis_segs = Segments(3)
|
||||
zaxis_segs = Segments(3)
|
||||
xtick_segs = Segments(3)
|
||||
ytick_segs = Segments(3)
|
||||
ztick_segs = Segments(3)
|
||||
xgrid_segs = Segments(3)
|
||||
ygrid_segs = Segments(3)
|
||||
zgrid_segs = Segments(3)
|
||||
xminorgrid_segs = Segments(3)
|
||||
yminorgrid_segs = Segments(3)
|
||||
zminorgrid_segs = Segments(3)
|
||||
xborder_segs = Segments(3)
|
||||
yborder_segs = Segments(3)
|
||||
zborder_segs = Segments(3)
|
||||
|
||||
if sp[:framestyle] != :none
|
||||
|
||||
# xaxis
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
z1, z2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(xaxis[:mirror], zaxis[:flip]) ? (zmax, zmin) : (zmin, zmax)
|
||||
end
|
||||
if xaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
push!(xaxis_segs, (xmin, y1, z1), (xmax, y1, z1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(xticks in (:none, nothing, false)) && length(xticks) > 1
|
||||
showticks = xticks[1] .!= 0
|
||||
xticks = (xticks[1][showticks], xticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin, y2, z2), (xmax, y2, z2)) # top spine
|
||||
end
|
||||
if !(xaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(y1) + 0.012 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
|
||||
for xtick in xticks[1]
|
||||
if xaxis[:showaxis]
|
||||
push!(xtick_segs, (xtick, tick_start, z1), (xtick, tick_stop, z1)) # bottom tick
|
||||
end
|
||||
if xaxis[:grid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(xgrid_segs, (xtick, ymin, 0.0), (xtick, ymax, 0.0))
|
||||
push!(xgrid_segs, (xtick, 0.0, zmin), (xtick, 0.0, zmax))
|
||||
else
|
||||
push!(xgrid_segs, (xtick, y1, z1), (xtick, y2, z1))
|
||||
push!(xgrid_segs, (xtick, y2, z1), (xtick, y2, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !(xaxis[:minorticks] in (:none, nothing, false)) || xaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(y1) + 0.006 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
for xtick in xminorticks
|
||||
if xaxis[:showaxis]
|
||||
push!(xtick_segs, (xtick, tick_start, z1), (xtick, tick_stop, z1)) # bottom tick
|
||||
end
|
||||
if xaxis[:minorgrid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(xminorgrid_segs, (xtick, ymin, 0.0), (xtick, ymax, 0.0))
|
||||
push!(xminorgrid_segs, (xtick, 0.0, zmin), (xtick, 0.0, zmax))
|
||||
else
|
||||
push!(xminorgrid_segs, (xtick, y1, z1), (xtick, y2, z1))
|
||||
push!(xminorgrid_segs, (xtick, y2, z1), (xtick, y2, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# yaxis
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], xaxis[:flip]) ? (xmin, xmax) : (xmax, xmin)
|
||||
end
|
||||
z1, z2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(yaxis[:mirror], zaxis[:flip]) ? (zmax, zmin) : (zmin, zmax)
|
||||
end
|
||||
if yaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
push!(yaxis_segs, (x1, ymin, z1), (x1, ymax, z1))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(yticks in (:none, nothing,false)) && length(yticks) > 1
|
||||
showticks = yticks[1] .!= 0
|
||||
yticks = (yticks[1][showticks], yticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (x2, ymin, z2), (x2, ymax, z2)) # right spine
|
||||
end
|
||||
if !(yaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(x1) + 0.012 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
|
||||
for ytick in yticks[1]
|
||||
if yaxis[:showaxis]
|
||||
push!(ytick_segs, (tick_start, ytick, z1), (tick_stop, ytick, z1)) # left tick
|
||||
end
|
||||
if yaxis[:grid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(ygrid_segs, (xmin, ytick, 0.0), (xmax, ytick, 0.0))
|
||||
push!(ygrid_segs, (0.0, ytick, zmin), (0.0, ytick, zmax))
|
||||
else
|
||||
push!(ygrid_segs, (x1, ytick, z1), (x2, ytick, z1))
|
||||
push!(ygrid_segs, (x2, ytick, z1), (x2, ytick, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !(yaxis[:minorticks] in (:none, nothing, false)) || yaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(xmax) - f(xmin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(x1) + 0.006 * (f(x2) - f(x1)) * ticks_in)
|
||||
(x1, t)
|
||||
end
|
||||
for ytick in yminorticks
|
||||
if yaxis[:showaxis]
|
||||
push!(ytick_segs, (tick_start, ytick, z1), (tick_stop, ytick, z1)) # left tick
|
||||
end
|
||||
if yaxis[:minorgrid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(yminorgrid_segs, (xmin, ytick, 0.0), (xmax, ytick, 0.0))
|
||||
push!(yminorgrid_segs, (0.0, ytick, zmin), (0.0, ytick, zmax))
|
||||
else
|
||||
push!(yminorgrid_segs, (x1, ytick, z1), (x2, ytick, z1))
|
||||
push!(yminorgrid_segs, (x2, ytick, z1), (x2, ytick, z2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# zaxis
|
||||
x1, x2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(zaxis[:mirror], xaxis[:flip]) ? (xmax, xmin) : (xmin, xmax)
|
||||
end
|
||||
y1, y2 = if sp[:framestyle] in (:origin, :zerolines)
|
||||
0.0, 0.0
|
||||
else
|
||||
xor(zaxis[:mirror], yaxis[:flip]) ? (ymax, ymin) : (ymin, ymax)
|
||||
end
|
||||
if zaxis[:showaxis]
|
||||
if sp[:framestyle] != :grid
|
||||
push!(zaxis_segs, (x1, y1, zmin), (x1, y1, zmax))
|
||||
# don't show the 0 tick label for the origin framestyle
|
||||
if sp[:framestyle] == :origin && !(zticks in (:none, nothing,false)) && length(zticks) > 1
|
||||
showticks = zticks[1] .!= 0
|
||||
zticks = (zticks[1][showticks], zticks[2][showticks])
|
||||
end
|
||||
end
|
||||
sp[:framestyle] in (:semi, :box) && push!(zborder_segs, (x2, y2, zmin), (x2, y2, zmax))
|
||||
end
|
||||
if !(zaxis[:ticks] in (:none, nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.012 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
ticks_in = zaxis[:tick_direction] == :out ? -1 : 1
|
||||
t = invf(f(y1) + 0.012 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
|
||||
for ztick in zticks[1]
|
||||
if zaxis[:showaxis]
|
||||
push!(ztick_segs, (x1, tick_start, ztick), (x1, tick_stop, ztick)) # left tick
|
||||
end
|
||||
if zaxis[:grid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(zgrid_segs, (xmin, 0.0, ztick), (xmax, 0.0, ztick))
|
||||
push!(ygrid_segs, (0.0, ymin, ztick), (0.0, ymax, ztick))
|
||||
else
|
||||
push!(ygrid_segs, (x1, y1, ztick), (x1, y2, ztick))
|
||||
push!(ygrid_segs, (x1, y2, ztick), (x2, y2, ztick))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !(zaxis[:minorticks] in (:none, nothing, false)) || zaxis[:minorgrid]
|
||||
tick_start, tick_stop = if sp[:framestyle] == :origin
|
||||
t = invf(f(0) + 0.006 * (f(ymax) - f(ymin)))
|
||||
(-t, t)
|
||||
else
|
||||
t = invf(f(y1) + 0.006 * (f(y2) - f(y1)) * ticks_in)
|
||||
(y1, t)
|
||||
end
|
||||
for ztick in zminorticks
|
||||
if zaxis[:showaxis]
|
||||
push!(ztick_segs, (x1, tick_start, ztick), (x1, tick_stop, ztick)) # left tick
|
||||
end
|
||||
if zaxis[:minorgrid]
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
push!(zminorgrid_segs, (xmin, 0.0, ztick), (xmax, 0.0, ztick))
|
||||
push!(zminorgrid_segs, (0.0, ymin, ztick), (0.0, ymax, ztick))
|
||||
else
|
||||
push!(zminorgrid_segs, (x1, y1, ztick), (x1, y2, ztick))
|
||||
push!(zminorgrid_segs, (x1, y2, ztick), (x2, y2, ztick))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xticks, yticks, zticks, xaxis_segs, yaxis_segs, zaxis_segs, xtick_segs, ytick_segs, ztick_segs, xgrid_segs, ygrid_segs, zgrid_segs, xminorgrid_segs, yminorgrid_segs, zminorgrid_segs, xborder_segs, yborder_segs, zborder_segs
|
||||
end
|
||||
|
||||
+574
-82
@@ -1,3 +1,4 @@
|
||||
using Pkg
|
||||
|
||||
struct NoBackend <: AbstractBackend end
|
||||
|
||||
@@ -5,42 +6,45 @@ const _backendType = Dict{Symbol, DataType}(:none => NoBackend)
|
||||
const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
|
||||
const _backends = Symbol[]
|
||||
const _initialized_backends = Set{Symbol}()
|
||||
const _default_backends = (:none, :gr, :plotly)
|
||||
|
||||
const _backend_packages = Dict{Symbol, Symbol}()
|
||||
|
||||
"Returns a list of supported backends"
|
||||
backends() = _backends
|
||||
|
||||
"Returns the name of the current backend"
|
||||
backend_name() = CURRENT_BACKEND.sym
|
||||
_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
|
||||
|
||||
function _backend_instance(sym::Symbol)::AbstractBackend
|
||||
haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
|
||||
end
|
||||
|
||||
backend_package_name(sym::Symbol) = _backend_packages[sym]
|
||||
|
||||
macro init_backend(s)
|
||||
str = lowercase(string(s))
|
||||
package_str = string(s)
|
||||
str = lowercase(package_str)
|
||||
sym = Symbol(str)
|
||||
T = Symbol(string(s) * "Backend")
|
||||
esc(quote
|
||||
struct $T <: AbstractBackend end
|
||||
export $sym
|
||||
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
|
||||
$sym(; kw...) = (default(; kw...); backend($T()))
|
||||
backend_name(::$T) = Symbol($str)
|
||||
backend_package_name(pkg::$T) = backend_package_name(Symbol($str))
|
||||
push!(_backends, Symbol($str))
|
||||
_backendType[Symbol($str)] = $T
|
||||
_backendSymbol[$T] = Symbol($str)
|
||||
include("backends/" * $str * ".jl")
|
||||
_backend_packages[Symbol($str)] = Symbol($package_str)
|
||||
end)
|
||||
end
|
||||
|
||||
include("backends/web.jl")
|
||||
# include("backends/web.jl")
|
||||
# include("backends/supported.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function add_backend(pkg::Symbol)
|
||||
info("To do a standard install of $pkg, copy and run this:\n\n")
|
||||
println(add_backend_string(_backend_instance(pkg)))
|
||||
println()
|
||||
end
|
||||
add_backend_string(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))")
|
||||
|
||||
# don't do anything as a default
|
||||
_create_backend_figure(plt::Plot) = nothing
|
||||
_prepare_plot_object(plt::Plot) = nothing
|
||||
@@ -70,9 +74,9 @@ end
|
||||
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
|
||||
|
||||
# account for the size/length/rotation of tick labels
|
||||
function tick_padding(axis::Axis)
|
||||
ticks = get_ticks(axis)
|
||||
if ticks == nothing
|
||||
function tick_padding(sp::Subplot, axis::Axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
if ticks === nothing
|
||||
0mm
|
||||
else
|
||||
vals, labs = ticks
|
||||
@@ -101,10 +105,10 @@ end
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot)
|
||||
# TODO: something different when `is3d(sp) == true`
|
||||
leftpad = tick_padding(sp[:yaxis]) + sp[:left_margin] + guide_padding(sp[:yaxis])
|
||||
leftpad = tick_padding(sp, sp[:yaxis]) + sp[:left_margin] + guide_padding(sp[:yaxis])
|
||||
toppad = sp[:top_margin] + title_padding(sp)
|
||||
rightpad = sp[:right_margin]
|
||||
bottompad = tick_padding(sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
||||
bottompad = tick_padding(sp, sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
||||
|
||||
# switch them?
|
||||
if sp[:xaxis][:mirror]
|
||||
@@ -131,34 +135,22 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function pickDefaultBackend()
|
||||
_fallback_default_backend() = backend(GRBackend())
|
||||
|
||||
function _pick_default_backend()
|
||||
env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "")
|
||||
if env_default != ""
|
||||
try
|
||||
Pkg.installed(env_default) # this will error if not installed
|
||||
sym = Symbol(lowercase(env_default))
|
||||
if haskey(_backendType, sym)
|
||||
return backend(sym)
|
||||
else
|
||||
warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t",
|
||||
join(sort(_backends), "\n\t"))
|
||||
end
|
||||
catch
|
||||
warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not installed.")
|
||||
sym = Symbol(lowercase(env_default))
|
||||
if sym in _backends
|
||||
backend(sym)
|
||||
else
|
||||
@warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t" *
|
||||
join(sort(_backends), "\n\t"))
|
||||
_fallback_default_backend()
|
||||
end
|
||||
else
|
||||
_fallback_default_backend()
|
||||
end
|
||||
|
||||
# the ordering/inclusion of this package list is my semi-arbitrary guess at
|
||||
# which one someone will want to use if they have the package installed...accounting for
|
||||
# features, speed, and robustness
|
||||
for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize")
|
||||
if Pkg.installed(pkgstr) != nothing
|
||||
return backend(Symbol(lowercase(pkgstr)))
|
||||
end
|
||||
end
|
||||
|
||||
# the default if nothing else is installed
|
||||
backend(:plotly)
|
||||
end
|
||||
|
||||
|
||||
@@ -168,55 +160,41 @@ end
|
||||
Returns the current plotting package name. Initializes package on first call.
|
||||
"""
|
||||
function backend()
|
||||
|
||||
global CURRENT_BACKEND
|
||||
if CURRENT_BACKEND.sym == :none
|
||||
pickDefaultBackend()
|
||||
end
|
||||
|
||||
sym = CURRENT_BACKEND.sym
|
||||
if !(sym in _initialized_backends)
|
||||
|
||||
# # initialize
|
||||
# println("[Plots.jl] Initializing backend: ", sym)
|
||||
|
||||
inst = _backend_instance(sym)
|
||||
try
|
||||
_initialize_backend(inst)
|
||||
catch err
|
||||
warn("Couldn't initialize $sym. (might need to install it?)")
|
||||
add_backend(sym)
|
||||
rethrow(err)
|
||||
if CURRENT_BACKEND.sym == :none
|
||||
_pick_default_backend()
|
||||
end
|
||||
|
||||
push!(_initialized_backends, sym)
|
||||
|
||||
end
|
||||
CURRENT_BACKEND.pkg
|
||||
CURRENT_BACKEND.pkg
|
||||
end
|
||||
|
||||
"""
|
||||
Set the plot backend.
|
||||
"""
|
||||
function backend(pkg::AbstractBackend)
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
warn_on_deprecated_backend(CURRENT_BACKEND.sym)
|
||||
sym = backend_name(pkg)
|
||||
if !(sym in _initialized_backends)
|
||||
_initialize_backend(pkg)
|
||||
push!(_initialized_backends, sym)
|
||||
end
|
||||
CURRENT_BACKEND.sym = sym
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
backend()
|
||||
pkg
|
||||
end
|
||||
|
||||
function backend(modname::Symbol)
|
||||
warn_on_deprecated_backend(modname)
|
||||
CURRENT_BACKEND.sym = modname
|
||||
CURRENT_BACKEND.pkg = _backend_instance(modname)
|
||||
backend()
|
||||
function backend(sym::Symbol)
|
||||
if sym in _backends
|
||||
backend(_backend_instance(sym))
|
||||
else
|
||||
@warn("`:$sym` is not a supported backend.")
|
||||
backend()
|
||||
end
|
||||
end
|
||||
|
||||
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
|
||||
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse, :glvisualize]
|
||||
|
||||
function warn_on_deprecated_backend(bsym::Symbol)
|
||||
if bsym in _deprecated_backends
|
||||
warn("Backend $bsym has been deprecated. It may not work as originally intended.")
|
||||
@warn("Backend $bsym has been deprecated.")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -233,7 +211,7 @@ const _base_supported_args = [
|
||||
:seriestype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:smooth,
|
||||
:xerror, :yerror,
|
||||
:xerror, :yerror, :zerror,
|
||||
:subplot,
|
||||
:x, :y, :z,
|
||||
:show, :size,
|
||||
@@ -250,7 +228,7 @@ const _base_supported_args = [
|
||||
:subplot_index,
|
||||
:discrete_values,
|
||||
:projection,
|
||||
|
||||
:show_empty_bins
|
||||
]
|
||||
|
||||
function merge_with_base_supported(v::AVec)
|
||||
@@ -267,18 +245,13 @@ end
|
||||
|
||||
|
||||
|
||||
# @init_backend Immerse
|
||||
# @init_backend Gadfly
|
||||
@init_backend PyPlot
|
||||
# @init_backend Qwt
|
||||
@init_backend UnicodePlots
|
||||
# @init_backend Winston
|
||||
# @init_backend Bokeh
|
||||
@init_backend Plotly
|
||||
@init_backend PlotlyJS
|
||||
@init_backend GR
|
||||
@init_backend GLVisualize
|
||||
@init_backend PGFPlots
|
||||
@init_backend PGFPlotsX
|
||||
@init_backend InspectDR
|
||||
@init_backend HDF5
|
||||
|
||||
@@ -308,3 +281,522 @@ end
|
||||
|
||||
# is_subplot_supported(::AbstractBackend) = false
|
||||
# is_subplot_supported() = is_subplot_supported(backend())
|
||||
|
||||
|
||||
################################################################################
|
||||
# initialize the backends
|
||||
|
||||
function _initialize_backend(pkg::AbstractBackend)
|
||||
sym = backend_package_name(pkg)
|
||||
@eval Main begin
|
||||
import $sym
|
||||
export $sym
|
||||
end
|
||||
end
|
||||
|
||||
_initialize_backend(pkg::GRBackend) = nothing
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# gr
|
||||
|
||||
const _gr_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
:layout,
|
||||
:title, :window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip,
|
||||
:match_dimensions,
|
||||
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
||||
:titlefontrotation, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfonthalign, :legendfontvalign,
|
||||
:legendfontrotation, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfonthalign, :tickfontvalign,
|
||||
:tickfontrotation, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign,
|
||||
:guidefontrotation, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:aspect_ratio,
|
||||
:normalize, :weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _gr_seriestype = [
|
||||
:path, :scatter, :straightline,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :path3d, :scatter3d, :surface, :wireframe, :volume,
|
||||
:shape
|
||||
]
|
||||
const _gr_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _gr_marker = _allMarkers
|
||||
const _gr_scale = [:identity, :log10]
|
||||
is_marker_supported(::GRBackend, shape::Shape) = true
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# plotly
|
||||
|
||||
function _initialize_backend(pkg::PlotlyBackend)
|
||||
try
|
||||
@eval Main begin
|
||||
import ORCA
|
||||
end
|
||||
catch
|
||||
@info "For saving to png with the Plotly backend ORCA has to be installed."
|
||||
end
|
||||
end
|
||||
|
||||
const _plotly_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_legend, :foreground_color_guide,
|
||||
:foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:foreground_color_title,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
:title, :title_location,
|
||||
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :gridalpha, :gridlinewidth,
|
||||
:legend, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :fill_z, :line_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
# :contours,
|
||||
:aspect_ratio,
|
||||
:hover,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:clims,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
|
||||
const _plotly_seriestype = [
|
||||
:path, :scatter, :pie, :heatmap,
|
||||
:contour, :surface, :wireframe, :path3d, :scatter3d, :shape, :scattergl,
|
||||
:straightline
|
||||
]
|
||||
const _plotly_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _plotly_marker = [
|
||||
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
|
||||
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
|
||||
]
|
||||
const _plotly_scale = [:identity, :log10]
|
||||
|
||||
defaultOutputFormat(plt::Plot{Plots.PlotlyBackend}) = "html"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# pgfplots
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
# :foreground_color_legend,
|
||||
:foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
# :bar_width, :bar_edges,
|
||||
:title,
|
||||
# :window_title,
|
||||
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
:colorbar, :colorbar_title,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights, :contours,
|
||||
:aspect_ratio,
|
||||
# :match_dimensions,
|
||||
:tick_direction,
|
||||
:framestyle,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
|
||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline, :vline] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# plotlyjs
|
||||
|
||||
function _initialize_backend(pkg::PlotlyJSBackend)
|
||||
@eval Main begin
|
||||
import PlotlyJS, ORCA
|
||||
export PlotlyJS
|
||||
end
|
||||
end
|
||||
|
||||
const _plotlyjs_attr = _plotly_attr
|
||||
const _plotlyjs_seriestype = _plotly_seriestype
|
||||
const _plotlyjs_style = _plotly_style
|
||||
const _plotlyjs_marker = _plotly_marker
|
||||
const _plotlyjs_scale = _plotly_scale
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# pyplot
|
||||
|
||||
function _initialize_backend(::PyPlotBackend)
|
||||
@eval Main begin
|
||||
import PyPlot
|
||||
|
||||
export PyPlot
|
||||
|
||||
# we don't want every command to update the figure
|
||||
PyPlot.ioff()
|
||||
end
|
||||
end
|
||||
|
||||
const _pyplot_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:window_title,
|
||||
:guide, :guide_position, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
:stride,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pyplot_seriestype = [
|
||||
:path, :steppre, :steppost, :shape, :straightline,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
const _pyplot_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _pyplot_marker = vcat(_allMarkers, :pixel)
|
||||
const _pyplot_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# unicodeplots
|
||||
|
||||
const _unicodeplots_attr = merge_with_base_supported([
|
||||
:label,
|
||||
:legend,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linestyle,
|
||||
:markershape,
|
||||
:bins,
|
||||
:title,
|
||||
:guide, :lims,
|
||||
])
|
||||
const _unicodeplots_seriestype = [
|
||||
:path, :scatter, :straightline,
|
||||
# :bar,
|
||||
:shape,
|
||||
:histogram2d,
|
||||
:spy
|
||||
]
|
||||
const _unicodeplots_style = [:auto, :solid]
|
||||
const _unicodeplots_marker = [:none, :auto, :circle]
|
||||
const _unicodeplots_scale = [:identity]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# hdf5
|
||||
|
||||
const _hdf5_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
:colorbar_title,
|
||||
])
|
||||
const _hdf5_seriestype = [
|
||||
:path, :steppre, :steppost, :shape, :straightline,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _hdf5_marker = vcat(_allMarkers, :pixel)
|
||||
const _hdf5_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# inspectdr
|
||||
|
||||
const _inspectdr_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
# :foreground_color_grid,
|
||||
:foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:markerstrokestyle, #Causes warning not to have it... what is this?
|
||||
:fillcolor, :fillalpha, #:fillrange,
|
||||
# :bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location,
|
||||
:window_title,
|
||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :legend, #:colorbar,
|
||||
# :marker_z,
|
||||
# :line_z,
|
||||
# :levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights,
|
||||
# :contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
# :clims,
|
||||
# :inset_subplots,
|
||||
:dpi,
|
||||
# :colorbar_title,
|
||||
])
|
||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _inspectdr_seriestype = [
|
||||
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
|
||||
]
|
||||
#see: _allMarkers, _shape_keys
|
||||
const _inspectdr_marker = Symbol[
|
||||
:none, :auto,
|
||||
:circle, :rect, :diamond,
|
||||
:cross, :xcross,
|
||||
:utriangle, :dtriangle, :rtriangle, :ltriangle,
|
||||
:pentagon, :hexagon, :heptagon, :octagon,
|
||||
:star4, :star5, :star6, :star7, :star8,
|
||||
:vline, :hline, :+, :x,
|
||||
]
|
||||
|
||||
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||
# ------------------------------------------------------------------------------
|
||||
# pgfplotsx
|
||||
|
||||
const _pgfplotsx_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_text,
|
||||
:foreground_color_border,
|
||||
:label,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
:layout,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:match_dimensions,
|
||||
:titlefontfamily,
|
||||
:titlefontsize,
|
||||
:titlefonthalign,
|
||||
:titlefontvalign,
|
||||
:titlefontrotation,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily,
|
||||
:legendfontsize,
|
||||
:legendfonthalign,
|
||||
:legendfontvalign,
|
||||
:legendfontrotation,
|
||||
:legendfontcolor,
|
||||
:tickfontfamily,
|
||||
:tickfontsize,
|
||||
:tickfonthalign,
|
||||
:tickfontvalign,
|
||||
:tickfontrotation,
|
||||
:tickfontcolor,
|
||||
:guidefontfamily,
|
||||
:guidefontsize,
|
||||
:guidefonthalign,
|
||||
:guidefontvalign,
|
||||
:guidefontrotation,
|
||||
:guidefontcolor,
|
||||
:grid,
|
||||
:gridalpha,
|
||||
:gridstyle,
|
||||
:gridlinewidth,
|
||||
:legend,
|
||||
:legendtitle,
|
||||
:colorbar,
|
||||
:colorbar_title,
|
||||
:colorbar_entry,
|
||||
:fill_z,
|
||||
:line_z,
|
||||
:marker_z,
|
||||
:levels,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:aspect_ratio,
|
||||
:normalize,
|
||||
:weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplotsx_seriestype = [
|
||||
:path,
|
||||
:scatter,
|
||||
:straightline,
|
||||
:path3d,
|
||||
:scatter3d,
|
||||
:surface,
|
||||
:wireframe,
|
||||
:heatmap,
|
||||
:contour,
|
||||
:contour3d,
|
||||
:shape,
|
||||
:steppre,
|
||||
:stepmid,
|
||||
:steppost,
|
||||
:ysticks,
|
||||
:xsticks,
|
||||
]
|
||||
const _pgfplotsx_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplotsx_marker = [
|
||||
:none,
|
||||
:auto,
|
||||
:circle,
|
||||
:rect,
|
||||
:diamond,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:ltriangle,
|
||||
:rtriangle,
|
||||
:cross,
|
||||
:xcross,
|
||||
:star5,
|
||||
:pentagon,
|
||||
:hline,
|
||||
:vline,
|
||||
Shape,
|
||||
]
|
||||
const _pgfplotsx_scale = [:identity, :ln, :log2, :log10]
|
||||
is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1004
-444
File diff suppressed because it is too large
Load Diff
+178
-169
@@ -16,22 +16,21 @@ Read from .hdf5 file using:
|
||||
|
||||
#==TODO
|
||||
===============================================================================
|
||||
1. Support more features
|
||||
- SeriesAnnotations & GridLayout known to be missing.
|
||||
3. Improve error handling.
|
||||
1. Support more features.
|
||||
- GridLayout known not to be working.
|
||||
2. Improve error handling.
|
||||
- Will likely crash if file format is off.
|
||||
2. Save data in a folder parallel to "plot".
|
||||
3. Save data in a folder parallel to "plot".
|
||||
- Will make it easier for users to locate data.
|
||||
- Use HDF5 reference to link data?
|
||||
3. Develop an actual versioned file format.
|
||||
4. Develop an actual versioned file format.
|
||||
- Should have some form of backward compatibility.
|
||||
- Should be reliable for archival purposes.
|
||||
5. Fix construction of plot object with hdf5plot_read.
|
||||
- Not building object correctly when backends do not natively support
|
||||
a certain feature (ex: :steppre)
|
||||
==#
|
||||
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "hdf5.jl"))
|
||||
end
|
||||
|
||||
import FixedPointNumbers: N0f8 #In core Julia
|
||||
|
||||
#Dispatch types:
|
||||
@@ -39,12 +38,13 @@ struct HDF5PlotNative; end #Indentifies a data element that can natively be hand
|
||||
struct HDF5CTuple; end #Identifies a "complex" tuple structure
|
||||
|
||||
mutable struct HDF5Plot_PlotRef
|
||||
ref::Union{Plot, Void}
|
||||
ref::Union{Plot, Nothing}
|
||||
end
|
||||
|
||||
|
||||
#==Useful constants
|
||||
===============================================================================#
|
||||
const _hdf5_nullable{T} = Union{T, Nothing}
|
||||
const _hdf5_plotroot = "plot"
|
||||
const _hdf5_dataroot = "data" #TODO: Eventually move data to different root (easier to locate)?
|
||||
const _hdf5plot_datatypeid = "TYPE" #Attribute identifying type
|
||||
@@ -59,61 +59,46 @@ const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
|
||||
|
||||
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
|
||||
const HDF5PLOT_SIMPLESUBSTRUCT = Union{Font, BoundingBox,
|
||||
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText
|
||||
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText,
|
||||
Shape,
|
||||
}
|
||||
|
||||
|
||||
#==
|
||||
===============================================================================#
|
||||
|
||||
const _hdf5_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:normalize, :weights,
|
||||
:contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
:colorbar_title,
|
||||
])
|
||||
const _hdf5_seriestype = [
|
||||
:path, :steppre, :steppost, :shape, :straightline,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
const _hdf5_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _hdf5_marker = vcat(_allMarkers, :pixel)
|
||||
const _hdf5_scale = [:identity, :ln, :log2, :log10]
|
||||
is_marker_supported(::HDF5Backend, shape::Shape) = true
|
||||
|
||||
function add_backend_string(::HDF5Backend)
|
||||
"""
|
||||
if !Plots.is_installed("HDF5")
|
||||
Pkg.add("HDF5")
|
||||
end
|
||||
"""
|
||||
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
#Possible element types of high-level data types:
|
||||
telem2str = Dict{String, Type}(
|
||||
"NATIVE" => HDF5PlotNative,
|
||||
"VOID" => Nothing,
|
||||
"BOOL" => Bool,
|
||||
"SYMBOL" => Symbol,
|
||||
"TUPLE" => Tuple,
|
||||
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
|
||||
"RGBA" => ARGB{N0f8},
|
||||
"EXTREMA" => Extrema,
|
||||
"LENGTH" => Length,
|
||||
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
||||
|
||||
#Sub-structure types:
|
||||
"ATTR" => Attr,
|
||||
"FONT" => Font,
|
||||
"BOUNDINGBOX" => BoundingBox,
|
||||
"GRIDLAYOUT" => GridLayout,
|
||||
"ROOTLAYOUT" => RootLayout,
|
||||
"SERIESANNOTATIONS" => SeriesAnnotations,
|
||||
"PLOTTEXT" => PlotText,
|
||||
"SHAPE" => Shape,
|
||||
"COLORGRADIENT" => ColorGradient,
|
||||
"AXIS" => Axis,
|
||||
"SURFACE" => Surface,
|
||||
"SUBPLOT" => Subplot,
|
||||
"NULLABLE" => _hdf5_nullable,
|
||||
)
|
||||
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
|
||||
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
|
||||
end
|
||||
|
||||
|
||||
@@ -125,10 +110,10 @@ _hdf5_datapath(subpath::String) = "$_hdf5_dataroot/$subpath"
|
||||
_hdf5_map_str2telem(k::String) = HDF5PLOT_MAP_STR2TELEM[k]
|
||||
_hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
||||
|
||||
function _hdf5_merge!(dest::Dict, src::Dict)
|
||||
function _hdf5_merge!(dest::AKW, src::AKW)
|
||||
for (k, v) in src
|
||||
if isa(v, Axis)
|
||||
_hdf5_merge!(dest[k].d, v.d)
|
||||
_hdf5_merge!(dest[k].plotattributes, v.plotattributes)
|
||||
else
|
||||
dest[k] = v
|
||||
end
|
||||
@@ -140,43 +125,6 @@ end
|
||||
#==
|
||||
===============================================================================#
|
||||
|
||||
function _initialize_backend(::HDF5Backend)
|
||||
@eval begin
|
||||
import HDF5
|
||||
export HDF5
|
||||
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
#Possible element types of high-level data types:
|
||||
const telem2str = Dict{String, Type}(
|
||||
"NATIVE" => HDF5PlotNative,
|
||||
"VOID" => Void,
|
||||
"BOOL" => Bool,
|
||||
"SYMBOL" => Symbol,
|
||||
"TUPLE" => Tuple,
|
||||
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
|
||||
"RGBA" => ARGB{N0f8},
|
||||
"EXTREMA" => Extrema,
|
||||
"LENGTH" => Length,
|
||||
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
||||
|
||||
#Sub-structure types:
|
||||
"FONT" => Font,
|
||||
"BOUNDINGBOX" => BoundingBox,
|
||||
"GRIDLAYOUT" => GridLayout,
|
||||
"ROOTLAYOUT" => RootLayout,
|
||||
"SERIESANNOTATIONS" => SeriesAnnotations,
|
||||
# "PLOTTEXT" => PlotText,
|
||||
"COLORGRADIENT" => ColorGradient,
|
||||
"AXIS" => Axis,
|
||||
"SUBPLOT" => Subplot,
|
||||
"NULLABLE" => Nullable,
|
||||
)
|
||||
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
|
||||
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Create the window/figure for this backend.
|
||||
function _create_backend_figure(plt::Plot{HDF5Backend})
|
||||
@@ -232,22 +180,18 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{HDF5Backend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{HDF5Backend})
|
||||
msg = "HDF5 interface does not support `display()` function."
|
||||
msg *= "\nUse `Plots.hdf5plot_write(::String)` method to write to .HDF5 \"plot\" file instead."
|
||||
warn(msg)
|
||||
@warn(msg)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -292,7 +236,7 @@ function _hdf5plot_writecount(grp, n::Int) #Write directly to group
|
||||
end
|
||||
function _hdf5plot_gwritefields(grp, k::String, v)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
for _k in fieldnames(v)
|
||||
for _k in fieldnames(typeof(v))
|
||||
_v = getfield(v, _k)
|
||||
kstr = string(_k)
|
||||
_hdf5plot_gwrite(grp, kstr, _v)
|
||||
@@ -305,6 +249,14 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_gwrite(grp, k::String, v) #Default
|
||||
T = typeof(v)
|
||||
if !(T <: Number || T <: String)
|
||||
tstr = string(T)
|
||||
path = HDF5.name(grp) * "/" * k
|
||||
@info("Type not supported: $tstr\npath: $path")
|
||||
# @show v
|
||||
return
|
||||
end
|
||||
grp[k] = v
|
||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||
end
|
||||
@@ -312,15 +264,24 @@ function _hdf5plot_gwrite(grp, k::String, v::Array{T}) where T<:Number #Default
|
||||
grp[k] = v
|
||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Dict)
|
||||
#=
|
||||
tstr = string(Dict)
|
||||
path = HDF5.name(grp) * "/" * k
|
||||
@info("Type not supported: $tstr\npath: $path")
|
||||
=#
|
||||
#No support for structures with Dicts different than KW (plotattributes)
|
||||
end
|
||||
|
||||
#=
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
|
||||
# @show grp, k
|
||||
warn("Cannot write Array: $k=$v")
|
||||
@warn("Cannot write Array: $k=$v")
|
||||
end
|
||||
=#
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Void)
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Nothing)
|
||||
grp[k] = 0
|
||||
_hdf5plot_writetype(grp, k, Void)
|
||||
_hdf5plot_writetype(grp, k, Nothing)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Bool)
|
||||
grp[k] = Int(v)
|
||||
@@ -344,10 +305,11 @@ function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
||||
end
|
||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, d::Dict)
|
||||
# warn("Cannot write dict: $k=$d")
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::KW)
|
||||
#NOTE: Can only write directly to group, not a subi-item
|
||||
# @warn("Cannot write dict: $k=$plotattributes")
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Range)
|
||||
function _hdf5plot_gwrite(grp, k::String, v::AbstractRange)
|
||||
_hdf5plot_gwrite(grp, k, collect(v)) #For now
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::ARGB{N0f8})
|
||||
@@ -359,19 +321,16 @@ function _hdf5plot_gwrite(grp, k::String, v::Colorant)
|
||||
end
|
||||
#Custom vector (when not using simple numeric type):
|
||||
function _hdf5plot_gwritearray(grp, k::String, v::Array{T}) where T
|
||||
if "annotations" == k;
|
||||
return #Hack. Does not yet support annotations.
|
||||
end
|
||||
|
||||
vgrp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_writetype(vgrp, Array) #ANY
|
||||
sz = size(v)
|
||||
lidx = LinearIndices(sz)
|
||||
|
||||
for iter in eachindex(v)
|
||||
coord = ind2sub(sz, iter)
|
||||
coord = lidx[iter]
|
||||
elem = v[iter]
|
||||
idxstr = join(coord, "_")
|
||||
_hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
|
||||
_hdf5plot_gwrite(vgrp, "v$idxstr", elem)
|
||||
end
|
||||
|
||||
_hdf5plot_gwrite(vgrp, "dim", [sz...])
|
||||
@@ -400,40 +359,50 @@ function _hdf5plot_gwrite(grp, k::String, v::HDF5PLOT_SIMPLESUBSTRUCT)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Axis)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
for (_k, _v) in v.d
|
||||
for (_k, _v) in v.plotattributes
|
||||
kstr = string(_k)
|
||||
_hdf5plot_gwrite(grp, kstr, _v)
|
||||
end
|
||||
_hdf5plot_writetype(grp, Axis)
|
||||
return
|
||||
end
|
||||
#TODO: "Properly" support Nullable using _hdf5plot_writetype?
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Nullable)
|
||||
if isnull(v)
|
||||
_hdf5plot_gwrite(grp, k, nothing)
|
||||
else
|
||||
_hdf5plot_gwrite(grp, k, v.value)
|
||||
end
|
||||
return
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Surface)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_gwrite(grp, "data2d", v.surf)
|
||||
_hdf5plot_writetype(grp, Surface)
|
||||
end
|
||||
# #TODO: "Properly" support Nullable using _hdf5plot_writetype?
|
||||
# function _hdf5plot_gwrite(grp, k::String, v::_hdf5_nullable)
|
||||
# if isnull(v)
|
||||
# _hdf5plot_gwrite(grp, k, nothing)
|
||||
# else
|
||||
# _hdf5plot_gwrite(grp, k, v.value)
|
||||
# end
|
||||
# return
|
||||
# end
|
||||
|
||||
function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
|
||||
#Currently no support for SeriesAnnotations
|
||||
return
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_gwrite(grp, "index", v[:subplot_index])
|
||||
_hdf5plot_writetype(grp, Subplot)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, d::Dict)
|
||||
for (k, v) in d
|
||||
|
||||
function _hdf5plot_write(grp, plotattributes::KW)
|
||||
for (k, v) in plotattributes
|
||||
kstr = string(k)
|
||||
_hdf5plot_gwrite(grp, kstr, v)
|
||||
end
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, plotattributes::Attr)
|
||||
for (k, v) in plotattributes
|
||||
kstr = string(k)
|
||||
_hdf5plot_gwrite(grp, kstr, v)
|
||||
end
|
||||
_hdf5plot_writetype(grp, Attr)
|
||||
end
|
||||
|
||||
|
||||
# Write main plot structures:
|
||||
# ----------------------------------------------------------------
|
||||
@@ -446,7 +415,7 @@ function _hdf5plot_write(sp::Subplot{HDF5Backend}, subpath::String, f)
|
||||
_hdf5plot_writecount(grp, length(sp.series_list))
|
||||
for (i, series) in enumerate(sp.series_list)
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
||||
_hdf5plot_write(grp, series.d)
|
||||
_hdf5plot_write(grp, series.plotattributes)
|
||||
end
|
||||
|
||||
return
|
||||
@@ -483,7 +452,7 @@ function _hdf5plot_readcount(grp) #Read directly from group
|
||||
end
|
||||
|
||||
_hdf5plot_convert(T::Type{HDF5PlotNative}, v) = v
|
||||
_hdf5plot_convert(T::Type{Void}, v) = nothing
|
||||
_hdf5plot_convert(T::Type{Nothing}, v) = nothing
|
||||
_hdf5plot_convert(T::Type{Bool}, v) = (v!=0)
|
||||
_hdf5plot_convert(T::Type{Symbol}, v) = Symbol(v)
|
||||
_hdf5plot_convert(T::Type{Tuple}, v) = tuple(v...) #With Vector{T<:Number}
|
||||
@@ -496,20 +465,14 @@ _hdf5plot_convert(T::Type{Extrema}, v) = Extrema(v[1], v[2])
|
||||
# Read data structures:
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _hdf5plot_read(grp, k::String, T::Type, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type)
|
||||
v = HDF5.d_read(grp, k)
|
||||
return _hdf5plot_convert(T, v)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Length}, dtid::Vector)
|
||||
v = HDF5.d_read(grp, k)
|
||||
TU = Symbol(dtid[2])
|
||||
T = typeof(v)
|
||||
return Length{TU,T}(v)
|
||||
end
|
||||
|
||||
# Read more complex data structures:
|
||||
# ----------------------------------------------------------------
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Font})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
family = _hdf5plot_read(grp, "family")
|
||||
@@ -520,15 +483,16 @@ function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
|
||||
color = _hdf5plot_read(grp, "color")
|
||||
return Font(family, pointsize, halign, valign, rotation, color)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Array}) #ANY
|
||||
grp = HDF5.g_open(grp, k)
|
||||
sz = _hdf5plot_read(grp, "dim")
|
||||
if [0] == sz; return []; end
|
||||
sz = tuple(sz...)
|
||||
result = Array{Any}(sz)
|
||||
result = Array{Any}(undef, sz)
|
||||
lidx = LinearIndices(sz)
|
||||
|
||||
for iter in eachindex(result)
|
||||
coord = ind2sub(sz, iter)
|
||||
coord = lidx[iter]
|
||||
idxstr = join(coord, "_")
|
||||
result[iter] = _hdf5plot_read(grp, "v$idxstr")
|
||||
end
|
||||
@@ -538,26 +502,49 @@ function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
|
||||
result = [result[iter] for iter in eachindex(result)] #Potentially make more specific
|
||||
return reshape(result, sz)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple}, dtid)
|
||||
v = _hdf5plot_read(grp, k, Array, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple})
|
||||
v = _hdf5plot_read(grp, k, Array)
|
||||
return tuple(v...)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{PlotText})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
str = _hdf5plot_read(grp, "str")
|
||||
font = _hdf5plot_read(grp, "font")
|
||||
return PlotText(str, font)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
strs = _hdf5plot_read(grp, "strs")
|
||||
font = _hdf5plot_read(grp, "font")
|
||||
baseshape = _hdf5plot_read(grp, "baseshape")
|
||||
scalefactor = _hdf5plot_read(grp, "scalefactor")
|
||||
return SeriesAnnotations(strs, font, baseshape, scalefactor)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Shape})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
x = _hdf5plot_read(grp, "x")
|
||||
y = _hdf5plot_read(grp, "y")
|
||||
return Shape(x, y)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
colors = _hdf5plot_read(grp, "colors")
|
||||
values = _hdf5plot_read(grp, "values")
|
||||
return ColorGradient(colors, values)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox}, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
x0 = _hdf5plot_read(grp, "x0")
|
||||
a = _hdf5plot_read(grp, "a")
|
||||
return BoundingBox(x0, a)
|
||||
end
|
||||
_hdf5plot_read(grp, k::String, T::Type{RootLayout}, dtid) = RootLayout()
|
||||
function _hdf5plot_read(grp, k::String, T::Type{GridLayout}, dtid)
|
||||
_hdf5plot_read(grp, k::String, T::Type{RootLayout}) = RootLayout()
|
||||
function _hdf5plot_read(grp, k::String, T::Type{GridLayout})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
# parent = _hdf5plot_read(grp, "parent")
|
||||
@@ -571,38 +558,59 @@ parent = RootLayout()
|
||||
|
||||
return GridLayout(parent, minpad, bbox, grid, widths, heights, attr)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
return Axis([], kwlist)
|
||||
function _hdf5plot_read(grp, T::Type{Attr})
|
||||
attr = Attr(KW(), _plot_defaults)
|
||||
v = _hdf5plot_read(grp, attr)
|
||||
return attr
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Subplot}, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Axis})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
plotattributes = Attr(KW(), _plot_defaults)
|
||||
_hdf5plot_read(grp, plotattributes)
|
||||
return Axis([], plotattributes)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Surface})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
data2d = _hdf5plot_read(grp, "data2d")
|
||||
return Surface(data2d)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Subplot})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
idx = _hdf5plot_read(grp, "index")
|
||||
return HDF5PLOT_PLOTREF.ref.subplots[idx]
|
||||
end
|
||||
|
||||
#Most types don't need dtid for read!!:
|
||||
_hdf5plot_read(grp, k::String, T::Type, dtid) = _hdf5plot_read(grp, k, T)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Length}, dtid::Vector)
|
||||
v = HDF5.d_read(grp, k)
|
||||
TU = Symbol(dtid[2])
|
||||
T = typeof(v)
|
||||
return Length{TU,T}(v)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String)
|
||||
dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid)
|
||||
T = _hdf5_map_str2telem(dtid) #expect exception
|
||||
return _hdf5plot_read(grp, k, T, dtid)
|
||||
end
|
||||
|
||||
#Read in values in group to populate d:
|
||||
function _hdf5plot_read(grp, d::Dict)
|
||||
#Read in values in group to populate plotattributes:
|
||||
function _hdf5plot_readattr(grp, plotattributes::AbstractDict)
|
||||
gnames = names(grp)
|
||||
for k in gnames
|
||||
try
|
||||
v = _hdf5plot_read(grp, k)
|
||||
d[Symbol(k)] = v
|
||||
plotattributes[Symbol(k)] = v
|
||||
catch e
|
||||
@show e
|
||||
@show grp
|
||||
warn("Could not read field $k")
|
||||
@warn("Could not read field $k")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
_hdf5plot_read(grp, plotattributes::KW) = _hdf5plot_readattr(grp, plotattributes)
|
||||
_hdf5plot_read(grp, plotattributes::Attr) = _hdf5plot_readattr(grp, plotattributes)
|
||||
|
||||
# Read main plot structures:
|
||||
# ----------------------------------------------------------------
|
||||
@@ -610,22 +618,23 @@ end
|
||||
function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
||||
f = f::HDF5.HDF5File #Assert
|
||||
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
_hdf5_merge!(sp.attr, kwlist)
|
||||
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list"))
|
||||
nseries = _hdf5plot_readcount(grp)
|
||||
|
||||
for i in 1:nseries
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
plot!(sp, kwlist[:x], kwlist[:y]) #Add data & create data structures
|
||||
_hdf5_merge!(sp.series_list[end].d, kwlist)
|
||||
seriesinfo = Attr(KW(), _plot_defaults)
|
||||
_hdf5plot_read(grp, seriesinfo)
|
||||
plot!(sp, seriesinfo[:x], seriesinfo[:y]) #Add data & create data structures
|
||||
_hdf5_merge!(sp.series_list[end].plotattributes, seriesinfo)
|
||||
end
|
||||
|
||||
#Perform after adding series... otherwise values get overwritten:
|
||||
grp = HDF5.g_open(f, _hdf5_plotelempath("$subpath/attr"))
|
||||
attr = Attr(KW(), _plot_defaults)
|
||||
_hdf5plot_read(grp, attr)
|
||||
_hdf5_merge!(sp.attr, attr)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
+38
-106
@@ -13,64 +13,7 @@ Add in functionality to Plots.jl:
|
||||
:aspect_ratio,
|
||||
=#
|
||||
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "inspectdr.jl"))
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
#TODO: remove features
|
||||
const _inspectdr_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
# :foreground_color_grid,
|
||||
:foreground_color_legend, :foreground_color_title,
|
||||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||||
:label,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:markerstrokestyle, #Causes warning not to have it... what is this?
|
||||
:fillcolor, :fillalpha, #:fillrange,
|
||||
# :bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location,
|
||||
:window_title,
|
||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :legend, #:colorbar,
|
||||
# :marker_z,
|
||||
# :line_z,
|
||||
# :levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights,
|
||||
# :contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
# :clims,
|
||||
# :inset_subplots,
|
||||
:dpi,
|
||||
# :colorbar_title,
|
||||
])
|
||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _inspectdr_seriestype = [
|
||||
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
|
||||
]
|
||||
#see: _allMarkers, _shape_keys
|
||||
const _inspectdr_marker = Symbol[
|
||||
:none, :auto,
|
||||
:circle, :rect, :diamond,
|
||||
:cross, :xcross,
|
||||
:utriangle, :dtriangle, :rtriangle, :ltriangle,
|
||||
:pentagon, :hexagon, :heptagon, :octagon,
|
||||
:star4, :star5, :star6, :star7, :star8,
|
||||
:vline, :hline, :+, :x,
|
||||
]
|
||||
|
||||
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
is_marker_supported(::InspectDRBackend, shape::Shape) = true
|
||||
|
||||
@@ -90,24 +33,24 @@ end
|
||||
|
||||
# py_marker(markers::AVec) = map(py_marker, markers)
|
||||
function _inspectdr_mapglyph(markers::AVec)
|
||||
warn("Vectors of markers are currently unsupported in InspectDR.")
|
||||
@warn("Vectors of markers are currently unsupported in InspectDR.")
|
||||
_inspectdr_mapglyph(markers[1])
|
||||
end
|
||||
|
||||
_inspectdr_mapglyphsize(v::Real) = v
|
||||
function _inspectdr_mapglyphsize(v::Vector)
|
||||
warn("Vectors of marker sizes are currently unsupported in InspectDR.")
|
||||
@warn("Vectors of marker sizes are currently unsupported in InspectDR.")
|
||||
_inspectdr_mapglyphsize(v[1])
|
||||
end
|
||||
|
||||
_inspectdr_mapcolor(v::Colorant) = v
|
||||
function _inspectdr_mapcolor(g::PlotUtils.ColorGradient)
|
||||
warn("Color gradients are currently unsupported in InspectDR.")
|
||||
@warn("Color gradients are currently unsupported in InspectDR.")
|
||||
#Pick middle color:
|
||||
_inspectdr_mapcolor(g.colors[div(1+end,2)])
|
||||
end
|
||||
function _inspectdr_mapcolor(v::AVec)
|
||||
warn("Vectors of colors are currently unsupported in InspectDR.")
|
||||
@warn("Vectors of colors are currently unsupported in InspectDR.")
|
||||
#Pick middle color:
|
||||
_inspectdr_mapcolor(v[div(1+end,2)])
|
||||
end
|
||||
@@ -152,37 +95,23 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::InspectDRBackend)
|
||||
"""
|
||||
if !Plots.is_installed("InspectDR")
|
||||
Pkg.add("InspectDR")
|
||||
end
|
||||
"""
|
||||
#Glyph used when plotting "Shape"s:
|
||||
INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
|
||||
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
|
||||
)
|
||||
|
||||
mutable struct InspecDRPlotRef
|
||||
mplot::Union{Nothing, InspectDR.Multiplot}
|
||||
gui::Union{Nothing, InspectDR.GtkPlot}
|
||||
end
|
||||
|
||||
function _initialize_backend(::InspectDRBackend; kw...)
|
||||
@eval begin
|
||||
import InspectDR
|
||||
export InspectDR
|
||||
_inspectdr_getmplot(::Any) = nothing
|
||||
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
|
||||
|
||||
#Glyph used when plotting "Shape"s:
|
||||
const INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
|
||||
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
|
||||
)
|
||||
|
||||
mutable struct InspecDRPlotRef
|
||||
mplot::Union{Void, InspectDR.Multiplot}
|
||||
gui::Union{Void, InspectDR.GtkPlot}
|
||||
end
|
||||
|
||||
_inspectdr_getmplot(::Any) = nothing
|
||||
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
|
||||
|
||||
_inspectdr_getgui(::Any) = nothing
|
||||
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed ? nothing : gplot)
|
||||
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
|
||||
end
|
||||
end
|
||||
_inspectdr_getgui(::Any) = nothing
|
||||
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed ? nothing : gplot)
|
||||
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
|
||||
push!(_initialized_backends, :inspectdr)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -192,9 +121,9 @@ function _create_backend_figure(plt::Plot{InspectDRBackend})
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
|
||||
#:overwrite_figure: want to reuse current figure
|
||||
if plt[:overwrite_figure] && mplot != nothing
|
||||
if plt[:overwrite_figure] && mplot !== nothing
|
||||
mplot.subplots = [] #Reset
|
||||
if gplot != nothing #Ensure still references current plot
|
||||
if gplot !== nothing #Ensure still references current plot
|
||||
gplot.src = mplot
|
||||
end
|
||||
else #want new one:
|
||||
@@ -278,8 +207,10 @@ For st in :shape:
|
||||
nmax = i
|
||||
if length(rng) > 1
|
||||
linewidth = series[:linewidth]
|
||||
linecolor = _inspectdr_mapcolor(_cycle(series[:linecolor], i))
|
||||
fillcolor = _inspectdr_mapcolor(_cycle(series[:fillcolor], i))
|
||||
c = plot_color(get_linecolor(series), get_linealpha(series))
|
||||
linecolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
c = plot_color(get_fillcolor(series), get_fillalpha(series))
|
||||
fillcolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
line = InspectDR.line(
|
||||
style=:solid, width=linewidth, color=linecolor
|
||||
)
|
||||
@@ -293,8 +224,10 @@ For st in :shape:
|
||||
i = (nmax >= 2 ? div(nmax, 2) : nmax) #Must pick one set of colors for legend
|
||||
if i > 1 #Add dummy waveform for legend entry:
|
||||
linewidth = series[:linewidth]
|
||||
linecolor = _inspectdr_mapcolor(_cycle(series[:linecolor], i))
|
||||
fillcolor = _inspectdr_mapcolor(_cycle(series[:fillcolor], i))
|
||||
c = plot_color(get_linecolor(series), get_linealpha(series))
|
||||
linecolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
c = plot_color(get_fillcolor(series), get_fillalpha(series))
|
||||
fillcolor = _inspectdr_mapcolor(_cycle(c, i))
|
||||
wfrm = InspectDR.add(plot, Float64[], Float64[], id=series[:label])
|
||||
wfrm.line = InspectDR.line(
|
||||
style=:none, width=linewidth, #linewidth affects glyph
|
||||
@@ -313,7 +246,7 @@ For st in :shape:
|
||||
wfrm.line = InspectDR.line(
|
||||
style = _style,
|
||||
width = series[:linewidth],
|
||||
color = series[:linecolor],
|
||||
color = plot_color(get_linecolor(series), get_linealpha(series)),
|
||||
)
|
||||
#InspectDR does not control markerstrokewidth independently.
|
||||
if :none == _style
|
||||
@@ -323,8 +256,8 @@ For st in :shape:
|
||||
wfrm.glyph = InspectDR.glyph(
|
||||
shape = _inspectdr_mapglyph(series[:markershape]),
|
||||
size = _inspectdr_mapglyphsize(series[:markersize]),
|
||||
color = _inspectdr_mapcolor(series[:markerstrokecolor]),
|
||||
fillcolor = _inspectdr_mapcolor(series[:markercolor]),
|
||||
color = _inspectdr_mapcolor(plot_color(series[:markerstrokecolor], series[:markerstrokealpha])),
|
||||
fillcolor = _inspectdr_mapcolor(plot_color(series[:markercolor], series[:markeralpha])),
|
||||
)
|
||||
end
|
||||
|
||||
@@ -347,8 +280,8 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
const plot = sp.o
|
||||
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
plot = sp.o
|
||||
strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
|
||||
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
|
||||
xgrid_show = xaxis[:grid]
|
||||
@@ -361,8 +294,8 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
|
||||
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
|
||||
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
|
||||
xmin, xmax = axis_limits(xaxis)
|
||||
ymin, ymax = axis_limits(yaxis)
|
||||
xmin, xmax = axis_limits(sp, :x)
|
||||
ymin, ymax = axis_limits(sp, :y)
|
||||
if ispolar(sp)
|
||||
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
|
||||
rmax = NaNMath.max(abs(ymin), abs(ymax))
|
||||
@@ -406,7 +339,7 @@ end
|
||||
# called just before updating layout bounding boxes... in case you need to prep
|
||||
# for the calcs
|
||||
function _before_layout_calcs(plt::Plot{InspectDRBackend})
|
||||
const mplot = _inspectdr_getmplot(plt.o)
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
|
||||
mplot.title = plt[:plot_title]
|
||||
@@ -475,7 +408,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
@@ -506,7 +439,7 @@ const _inspectdr_mimeformats_nodpi = Dict(
|
||||
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
|
||||
"application/pdf" => "pdf"
|
||||
)
|
||||
_inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
|
||||
_inspectdr_show(io::IO, mime::MIME, ::Nothing, w, h) =
|
||||
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
|
||||
function _inspectdr_show(io::IO, mime::MIME, mplot, w, h)
|
||||
InspectDR._show(io, mime, mplot, Float64(w), Float64(h))
|
||||
@@ -523,7 +456,6 @@ for (mime, fmt) in _inspectdr_mimeformats_nodpi
|
||||
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
|
||||
end
|
||||
end
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
function plotlybase_syncplot(plt::Plot)
|
||||
plt.o = ORCA.PlotlyBase.Plot()
|
||||
traces = ORCA.PlotlyBase.GenericTrace[]
|
||||
for series_dict in plotly_series(plt)
|
||||
plotly_type = pop!(series_dict, :type)
|
||||
push!(traces, ORCA.PlotlyBase.GenericTrace(plotly_type; series_dict...))
|
||||
end
|
||||
ORCA.PlotlyBase.addtraces!(plt.o, traces...)
|
||||
layout = plotly_layout(plt)
|
||||
w, h = plt[:size]
|
||||
ORCA.PlotlyBase.relayout!(plt.o, layout, width = w, height = h)
|
||||
return plt.o
|
||||
end
|
||||
|
||||
const _orca_mimeformats = Dict(
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"image/svg+xml" => "svg",
|
||||
"image/eps" => "eps",
|
||||
)
|
||||
|
||||
for (mime, fmt) in _orca_mimeformats
|
||||
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyBackend}) = ORCA.PlotlyBase.savefig(io, plotlybase_syncplot(plt), format = $fmt)
|
||||
end
|
||||
+241
-161
@@ -2,66 +2,6 @@
|
||||
|
||||
# significant contributions by: @pkofod
|
||||
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "pgfplots.jl"))
|
||||
end
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
# :background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
# :bar_width, :bar_edges,
|
||||
:title,
|
||||
# :window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
:colorbar,
|
||||
:marker_z, #:levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights, :contours,
|
||||
:aspect_ratio,
|
||||
# :match_dimensions,
|
||||
:tick_direction,
|
||||
:framestyle,
|
||||
:camera,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape, :straightline,]
|
||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::PGFPlotsBackend)
|
||||
"""
|
||||
Pkg.add("PGFPlots")
|
||||
Pkg.build("PGFPlots")
|
||||
"""
|
||||
end
|
||||
|
||||
function _initialize_backend(::PGFPlotsBackend; kw...)
|
||||
@eval begin
|
||||
import PGFPlots
|
||||
export PGFPlots
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
const _pgfplots_linestyles = KW(
|
||||
@@ -86,7 +26,8 @@ const _pgfplots_markers = KW(
|
||||
:star6 => "asterisk",
|
||||
:diamond => "diamond*",
|
||||
:pentagon => "pentagon*",
|
||||
:hline => "-"
|
||||
:hline => "-",
|
||||
:vline => "|"
|
||||
)
|
||||
|
||||
const _pgfplots_legend_pos = KW(
|
||||
@@ -122,7 +63,7 @@ function pgf_framestyle(style::Symbol)
|
||||
return style
|
||||
else
|
||||
default_style = get(_pgf_framestyle_defaults, style, :axes)
|
||||
warn("Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead.")
|
||||
@warn("Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead.")
|
||||
default_style
|
||||
end
|
||||
end
|
||||
@@ -148,127 +89,227 @@ function pgf_colormap(grad::ColorGradient)
|
||||
end,", ")
|
||||
end
|
||||
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor])
|
||||
pgf_thickness_scaling(plt::Plot) = plt[:thickness_scaling]
|
||||
pgf_thickness_scaling(sp::Subplot) = pgf_thickness_scaling(sp.plt)
|
||||
pgf_thickness_scaling(series) = pgf_thickness_scaling(series[:subplot])
|
||||
|
||||
function pgf_fillstyle(plotattributes, i = 1)
|
||||
cstr,a = pgf_color(get_fillcolor(plotattributes, i))
|
||||
fa = get_fillalpha(plotattributes, i)
|
||||
if fa !== nothing
|
||||
a = fa
|
||||
end
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
end
|
||||
|
||||
function pgf_linestyle(d::KW)
|
||||
cstr,a = pgf_color(d[:linecolor])
|
||||
function pgf_linestyle(linewidth::Real, color, α = 1, linestyle = "solid")
|
||||
cstr, a = pgf_color(plot_color(color, α))
|
||||
"""
|
||||
color = $cstr,
|
||||
draw opacity=$a,
|
||||
line width=$(d[:linewidth]),
|
||||
$(get(_pgfplots_linestyles, d[:linestyle], "solid"))"""
|
||||
draw opacity = $a,
|
||||
line width = $linewidth,
|
||||
$(get(_pgfplots_linestyles, linestyle, "solid"))"""
|
||||
end
|
||||
|
||||
function pgf_marker(d::KW)
|
||||
shape = d[:markershape]
|
||||
cstr, a = pgf_color(d[:markercolor])
|
||||
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor])
|
||||
"""
|
||||
mark = $(get(_pgfplots_markers, shape, "*")),
|
||||
mark size = $(0.5 * d[:markersize]),
|
||||
mark options = {
|
||||
color = $cstr_stroke, draw opacity = $a_stroke,
|
||||
fill = $cstr, fill opacity = $a,
|
||||
line width = $(d[:markerstrokewidth]),
|
||||
rotate = $(shape == :dtriangle ? 180 : 0),
|
||||
$(get(_pgfplots_linestyles, d[:markerstrokestyle], "solid"))
|
||||
}"""
|
||||
function pgf_linestyle(plotattributes, i = 1)
|
||||
lw = pgf_thickness_scaling(plotattributes) * get_linewidth(plotattributes, i)
|
||||
lc = get_linecolor(plotattributes, i)
|
||||
la = get_linealpha(plotattributes, i)
|
||||
ls = get_linestyle(plotattributes, i)
|
||||
return pgf_linestyle(lw, lc, la, ls)
|
||||
end
|
||||
|
||||
function pgf_add_annotation!(o,x,y,val)
|
||||
function pgf_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
|
||||
fs = fontsize * thickness_scaling
|
||||
return string("{\\fontsize{", fs, " pt}{", 1.3fs, " pt}", font, "}")
|
||||
end
|
||||
|
||||
function pgf_marker(plotattributes, i = 1)
|
||||
shape = _cycle(plotattributes[:markershape], i)
|
||||
cstr, a = pgf_color(plot_color(get_markercolor(plotattributes, i), get_markeralpha(plotattributes, i)))
|
||||
cstr_stroke, a_stroke = pgf_color(plot_color(get_markerstrokecolor(plotattributes, i), get_markerstrokealpha(plotattributes, i)))
|
||||
return string(
|
||||
"mark = $(get(_pgfplots_markers, shape, "*")),\n",
|
||||
"mark size = $(pgf_thickness_scaling(plotattributes) * 0.5 * _cycle(plotattributes[:markersize], i)),\n",
|
||||
plotattributes[:seriestype] == :scatter ? "only marks,\n" : "",
|
||||
"mark options = {
|
||||
color = $cstr_stroke, draw opacity = $a_stroke,
|
||||
fill = $cstr, fill opacity = $a,
|
||||
line width = $(pgf_thickness_scaling(plotattributes) * _cycle(plotattributes[:markerstrokewidth], i)),
|
||||
rotate = $(shape == :dtriangle ? 180 : 0),
|
||||
$(get(_pgfplots_linestyles, _cycle(plotattributes[:markerstrokestyle], i), "solid"))
|
||||
}"
|
||||
)
|
||||
end
|
||||
|
||||
function pgf_add_annotation!(o, x, y, val, thickness_scaling = 1)
|
||||
# Construct the style string.
|
||||
# Currently supports color and orientation
|
||||
cstr,a = pgf_color(val.font.color)
|
||||
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation)
|
||||
"""))
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation),
|
||||
font=$(pgf_font(val.font.pointsize, thickness_scaling))
|
||||
"""))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function pgf_series(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
|
||||
if d[:fillrange] != nothing || st in (:shape,)
|
||||
push!(style, pgf_fillstyle(d))
|
||||
end
|
||||
|
||||
# add to legend?
|
||||
if sp[:legend] != :none && should_add_to_legend(series)
|
||||
kw[:legendentry] = d[:label]
|
||||
if st == :shape || d[:fillrange] != nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
else
|
||||
push!(style, "forget plot")
|
||||
end
|
||||
plotattributes = series.plotattributes
|
||||
st = plotattributes[:seriestype]
|
||||
series_collection = PGFPlots.Plot[]
|
||||
|
||||
# function args
|
||||
args = if st == :contour
|
||||
d[:z].surf, d[:x], d[:y]
|
||||
args = if st == :contour
|
||||
plotattributes[:z].surf, plotattributes[:x], plotattributes[:y]
|
||||
elseif is3d(st)
|
||||
d[:x], d[:y], d[:z]
|
||||
plotattributes[:x], plotattributes[:y], plotattributes[:z]
|
||||
elseif st == :straightline
|
||||
straightline_data(series)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
elseif d[:marker_z] != nothing
|
||||
# If a marker_z is used pass it as third coordinate to a 2D plot.
|
||||
# See "Scatter Plots" in PGFPlots documentation
|
||||
d[:x], d[:y], d[:marker_z]
|
||||
elseif ispolar(sp)
|
||||
theta, r = filter_radial_data(d[:x], d[:y], axis_limits(sp[:yaxis]))
|
||||
theta, r = plotattributes[:x], plotattributes[:y]
|
||||
rad2deg.(theta), r
|
||||
else
|
||||
d[:x], d[:y]
|
||||
plotattributes[:x], plotattributes[:y]
|
||||
end
|
||||
|
||||
# PGFPlots can't handle non-Vector?
|
||||
args = map(a -> if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
collect(a)
|
||||
else
|
||||
a
|
||||
end, args)
|
||||
# for (i,a) in enumerate(args)
|
||||
# if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
# args[i] = collect(a)
|
||||
# end
|
||||
# end
|
||||
# args = map(a -> if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
# collect(a)
|
||||
# else
|
||||
# a
|
||||
# end, args)
|
||||
|
||||
# include additional style, then add to the kw
|
||||
if st in (:contour, :histogram2d)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(plotattributes))
|
||||
push!(style, pgf_marker(plotattributes))
|
||||
push!(style, "forget plot")
|
||||
|
||||
kw[:style] = join(style, ',')
|
||||
func = if st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
else
|
||||
kw[:labels] = series[:contour_labels]
|
||||
kw[:levels] = series[:levels]
|
||||
PGFPlots.Contour
|
||||
end
|
||||
push!(series_collection, func(args...; kw...))
|
||||
|
||||
else
|
||||
# series segments
|
||||
segments = iter_segments(series)
|
||||
for (i, rng) in enumerate(segments)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(plotattributes, i))
|
||||
push!(style, pgf_marker(plotattributes, i))
|
||||
|
||||
if st == :shape
|
||||
push!(style, pgf_fillstyle(plotattributes, i))
|
||||
end
|
||||
|
||||
# add to legend?
|
||||
if i == 1 && sp[:legend] != :none && should_add_to_legend(series)
|
||||
if plotattributes[:fillrange] !== nothing
|
||||
push!(style, "forget plot")
|
||||
push!(series_collection, pgf_fill_legend_hack(plotattributes, args))
|
||||
else
|
||||
kw[:legendentry] = plotattributes[:label]
|
||||
if st == :shape # || plotattributes[:fillrange] !== nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
end
|
||||
else
|
||||
push!(style, "forget plot")
|
||||
end
|
||||
|
||||
seg_args = (arg[rng] for arg in args)
|
||||
|
||||
# include additional style, then add to the kw
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
|
||||
# add fillrange
|
||||
if series[:fillrange] !== nothing && st != :shape
|
||||
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
push!(series_collection, func(seg_args...; kw...))
|
||||
end
|
||||
end
|
||||
series_collection
|
||||
end
|
||||
|
||||
function pgf_fillrange_series(series, i, fillrange, args...)
|
||||
st = series[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, "line width = 0")
|
||||
push!(style, "draw opacity = 0")
|
||||
push!(style, pgf_fillstyle(series, i))
|
||||
push!(style, pgf_marker(series, i))
|
||||
push!(style, "forget plot")
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
func = is3d(series) ? PGFPlots.Linear3 : PGFPlots.Linear
|
||||
return func(pgf_fillrange_args(fillrange, args...)...; kw...)
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
function pgf_fillrange_args(fillrange, x, y)
|
||||
n = length(x)
|
||||
x_fill = [x; x[n:-1:1]; x[1]]
|
||||
y_fill = [y; _cycle(fillrange, n:-1:1); y[1]]
|
||||
return x_fill, y_fill
|
||||
end
|
||||
|
||||
function pgf_fillrange_args(fillrange, x, y, z)
|
||||
n = length(x)
|
||||
x_fill = [x; x[n:-1:1]; x[1]]
|
||||
y_fill = [y; y[n:-1:1]; x[1]]
|
||||
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
|
||||
return x_fill, y_fill, z_fill
|
||||
end
|
||||
|
||||
function pgf_fill_legend_hack(plotattributes, args)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(plotattributes, 1))
|
||||
push!(style, pgf_marker(plotattributes, 1))
|
||||
push!(style, pgf_fillstyle(plotattributes, 1))
|
||||
push!(style, "area legend")
|
||||
kw[:legendentry] = plotattributes[:label]
|
||||
kw[:style] = join(style, ',')
|
||||
st = plotattributes[:seriestype]
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
elseif st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
elseif st == :contour
|
||||
PGFPlots.Contour
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
func(args...; kw...)
|
||||
return func(([arg[1]] for arg in args)...; kw...)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function pgf_axis(sp::Subplot, letter)
|
||||
@@ -285,8 +326,17 @@ function pgf_axis(sp::Subplot, letter)
|
||||
# axis guide
|
||||
kw[Symbol(letter,:label)] = axis[:guide]
|
||||
|
||||
# Add ticklabel rotations
|
||||
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
|
||||
# axis label position
|
||||
labelpos = ""
|
||||
if letter == :x && axis[:guide_position] == :top
|
||||
labelpos = "at={(0.5,1)},above,"
|
||||
elseif letter == :y && axis[:guide_position] == :right
|
||||
labelpos = "at={(1,0.5)},below,"
|
||||
end
|
||||
|
||||
# Add label font
|
||||
cstr, α = pgf_color(plot_color(axis[:guidefontcolor]))
|
||||
push!(style, string(letter, "label style = {", labelpos ,"font = ", pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:guidefontrotation], "}"))
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
@@ -313,45 +363,56 @@ function pgf_axis(sp::Subplot, letter)
|
||||
# limits
|
||||
# TODO: support zlims
|
||||
if letter != :z
|
||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(axis)) : axis_limits(axis)
|
||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) : axis_limits(sp, letter)
|
||||
kw[Symbol(letter,:min)] = lims[1]
|
||||
kw[Symbol(letter,:max)] = lims[2]
|
||||
end
|
||||
|
||||
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
||||
ticks = get_ticks(axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
||||
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
||||
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
|
||||
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
|
||||
# wrap the power part of label with }
|
||||
tick_labels = String[begin
|
||||
tick_labels = Vector{String}(undef, length(ticks[2]))
|
||||
for (i, label) in enumerate(ticks[2])
|
||||
base, power = split(label, "^")
|
||||
power = string("{", power, "}")
|
||||
string(base, "^", power)
|
||||
end for label in ticks[2]]
|
||||
tick_labels[i] = string(base, "^", power)
|
||||
end
|
||||
push!(style, string(letter, "ticklabels = {\$", join(tick_labels,"\$,\$"), "\$}"))
|
||||
elseif axis[:showaxis]
|
||||
tick_labels = ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
tick_labels = axis[:formatter] == :scientific ? string.("\$", convert_sci_unicode.(tick_labels), "\$") : tick_labels
|
||||
if axis[:formatter] in (:scientific, :auto)
|
||||
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
|
||||
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
|
||||
end
|
||||
push!(style, string(letter, "ticklabels = {", join(tick_labels,","), "}"))
|
||||
else
|
||||
push!(style, string(letter, "ticklabels = {}"))
|
||||
end
|
||||
push!(style, string(letter, "tick align = ", (axis[:tick_direction] == :out ? "outside" : "inside")))
|
||||
cstr, α = pgf_color(plot_color(axis[:tickfontcolor]))
|
||||
push!(style, string(letter, "ticklabel style = {font = ", pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:tickfontrotation], "}"))
|
||||
push!(style, string(letter, " grid style = {", pgf_linestyle(pgf_thickness_scaling(sp) * axis[:gridlinewidth], axis[:foreground_color_grid], axis[:gridalpha], axis[:gridstyle]), "}"))
|
||||
end
|
||||
|
||||
# framestyle
|
||||
if framestyle in (:axes, :origin)
|
||||
axispos = framestyle == :axes ? "left" : "middle"
|
||||
# the * after lines disables the arrows at the axes
|
||||
push!(style, string("axis lines* = ", axispos))
|
||||
if axis[:draw_arrow]
|
||||
push!(style, string("axis ", letter, " line = ", axispos))
|
||||
else
|
||||
# the * after line disables the arrow at the axis
|
||||
push!(style, string("axis ", letter, " line* = ", axispos))
|
||||
end
|
||||
end
|
||||
|
||||
if framestyle == :zerolines
|
||||
push!(style, string("extra ", letter, " ticks = 0"))
|
||||
push!(style, string("extra ", letter, " tick labels = "))
|
||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {color = black, draw opacity=1.0, line width=0.5), solid}}"))
|
||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}}"))
|
||||
end
|
||||
|
||||
if !axis[:showaxis]
|
||||
@@ -359,6 +420,8 @@ function pgf_axis(sp::Subplot, letter)
|
||||
end
|
||||
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
|
||||
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
|
||||
else
|
||||
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_border], 1.0), "}"))
|
||||
end
|
||||
|
||||
# return the style list and KW args
|
||||
@@ -395,7 +458,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
bb = bbox(sp)
|
||||
push!(style, """
|
||||
xshift = $(left(bb).value)mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value,2))mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value, digits=2))mm,
|
||||
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
|
||||
""")
|
||||
kw[:width] = "$(width(bb).value)mm"
|
||||
@@ -403,9 +466,11 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
if sp[:title] != ""
|
||||
kw[:title] = "$(sp[:title])"
|
||||
cstr, α = pgf_color(plot_color(sp[:titlefontcolor]))
|
||||
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
|
||||
end
|
||||
|
||||
if sp[:aspect_ratio] in (1, :equal)
|
||||
if get_aspect_ratio(sp) in (1, :equal)
|
||||
kw[:axisEqual] = "true"
|
||||
end
|
||||
|
||||
@@ -413,8 +478,23 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
if haskey(_pgfplots_legend_pos, legpos)
|
||||
kw[:legendPos] = _pgfplots_legend_pos[legpos]
|
||||
end
|
||||
cstr, bg_alpha = pgf_color(plot_color(sp[:background_color_legend]))
|
||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||
|
||||
if is3d(sp)
|
||||
push!(style, string(
|
||||
"legend style = {",
|
||||
pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], fg_alpha, "solid", ), ",",
|
||||
"fill = $cstr,",
|
||||
"fill opacity = $bg_alpha,",
|
||||
"text opacity = $(alpha(plot_color(sp[:legendfontcolor]))),",
|
||||
"font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)),
|
||||
"}",
|
||||
))
|
||||
|
||||
if any(s[:seriestype] == :contour for s in series_list(sp))
|
||||
kw[:view] = "{0}{90}"
|
||||
kw[:colorbar] = !(sp[:colorbar] in (:none, :off, :hide, false))
|
||||
elseif is3d(sp)
|
||||
azim, elev = sp[:camera]
|
||||
kw[:view] = "{$(azim)}{$(elev)}"
|
||||
end
|
||||
@@ -438,9 +518,9 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
# As it is likely that all series within the same axis use the same
|
||||
# colormap this should not cause any problem.
|
||||
for series in series_list(sp)
|
||||
for col in (:markercolor, :fillcolor)
|
||||
if typeof(series.d[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.d[col]))}")
|
||||
for col in (:markercolor, :fillcolor, :linecolor)
|
||||
if typeof(series.plotattributes[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.plotattributes[col]))}")
|
||||
|
||||
if sp[:colorbar] == :none
|
||||
kw[:colorbar] = "false"
|
||||
@@ -454,22 +534,23 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
end
|
||||
@label colorbar_end
|
||||
|
||||
push!(style, "colorbar style={title=$(sp[:colorbar_title])}")
|
||||
o = axisf(; style = join(style, ","), kw...)
|
||||
|
||||
# add the series object to the PGFPlots.Axis
|
||||
for series in series_list(sp)
|
||||
push!(o, pgf_series(sp, series))
|
||||
push!.(Ref(o), pgf_series(sp, series))
|
||||
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt), pgf_thickness_scaling(series))
|
||||
end
|
||||
end
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o, locate_annotation(sp, ann...)...)
|
||||
pgf_add_annotation!(o, locate_annotation(sp, ann...)..., pgf_thickness_scaling(sp))
|
||||
end
|
||||
|
||||
|
||||
@@ -491,7 +572,7 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
|
||||
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
|
||||
|
||||
# read it into io
|
||||
write(io, readstring(open(fn)))
|
||||
write(io, read(open(fn), String))
|
||||
|
||||
# cleanup
|
||||
PGFPlots.cleanup(plt.o)
|
||||
@@ -499,13 +580,12 @@ end
|
||||
|
||||
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
|
||||
fn = tempname()*".tex"
|
||||
PGFPlots.save(fn, backend_object(plt), include_preamble=false)
|
||||
write(io, readstring(open(fn)))
|
||||
PGFPlots.save(fn, backend_object(plt), include_preamble=plt.attr[:tex_output_standalone])
|
||||
write(io, read(open(fn), String))
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PGFPlotsBackend})
|
||||
# prepare the object
|
||||
PGFPlots.pushPGFPlotsPreamble("\\usepackage{fontspec}")
|
||||
pgfplt = PGFPlots.plot(plt.o)
|
||||
|
||||
# save an svg
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+393
-344
File diff suppressed because it is too large
Load Diff
+38
-111
@@ -1,129 +1,56 @@
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "plotlyjs.jl"))
|
||||
# https://github.com/sglyon/PlotlyJS.jl
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
function plotlyjs_syncplot(plt::Plot{PlotlyJSBackend})
|
||||
plt[:overwrite_figure] && closeall()
|
||||
plt.o = PlotlyJS.plot()
|
||||
traces = PlotlyJS.GenericTrace[]
|
||||
for series_dict in plotly_series(plt)
|
||||
plotly_type = pop!(series_dict, :type)
|
||||
push!(traces, PlotlyJS.GenericTrace(plotly_type; series_dict...))
|
||||
end
|
||||
PlotlyJS.addtraces!(plt.o, traces...)
|
||||
layout = plotly_layout(plt)
|
||||
w, h = plt[:size]
|
||||
PlotlyJS.relayout!(plt.o, layout, width = w, height = h)
|
||||
return plt.o
|
||||
end
|
||||
|
||||
# https://github.com/spencerlyon2/PlotlyJS.jl
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
const _plotlyjs_attr = _plotly_attr
|
||||
const _plotlyjs_seriestype = _plotly_seriestype
|
||||
const _plotlyjs_style = _plotly_style
|
||||
const _plotlyjs_marker = _plotly_marker
|
||||
const _plotlyjs_scale = _plotly_scale
|
||||
const _plotlyjs_mimeformats = Dict(
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"image/svg+xml" => "svg",
|
||||
"image/eps" => "eps",
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function add_backend_string(::PlotlyJSBackend)
|
||||
"""
|
||||
if !Plots.is_installed("PlotlyJS")
|
||||
Pkg.add("PlotlyJS")
|
||||
end
|
||||
if !Plots.is_installed("Rsvg")
|
||||
Pkg.add("Rsvg")
|
||||
end
|
||||
import Blink
|
||||
Blink.AtomShell.install()
|
||||
"""
|
||||
for (mime, fmt) in _plotlyjs_mimeformats
|
||||
@eval _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PlotlyJSBackend}) = PlotlyJS.savefig(io, plotlyjs_syncplot(plt), format = $fmt)
|
||||
end
|
||||
|
||||
# Use the Plotly implementation for json and html:
|
||||
_show(io::IO, mime::MIME"application/vnd.plotly.v1+json", plt::Plot{PlotlyJSBackend}) = plotly_show_js(io, plt)
|
||||
|
||||
function _initialize_backend(::PlotlyJSBackend; kw...)
|
||||
@eval begin
|
||||
import PlotlyJS
|
||||
export PlotlyJS
|
||||
end
|
||||
html_head(plt::Plot{PlotlyJSBackend}) = plotly_html_head(plt)
|
||||
html_body(plt::Plot{PlotlyJSBackend}) = plotly_html_body(plt)
|
||||
|
||||
# # override IJulia inline display
|
||||
# if isijulia()
|
||||
# IJulia.display_dict(plt::AbstractPlot{PlotlyJSBackend}) = IJulia.display_dict(plt.o)
|
||||
# end
|
||||
end
|
||||
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = write(io, standalone_html(plt))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
_display(plt::Plot{PlotlyJSBackend}) = display(plotlyjs_syncplot(plt))
|
||||
|
||||
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
|
||||
else
|
||||
PlotlyJS.plot()
|
||||
@require WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin
|
||||
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
||||
plt_html = sprint(show, MIME("text/html"), plt)
|
||||
return WebIO.render(WebIO.dom"div"(innerHTML=plt_html))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
syncplot = plt.o
|
||||
pdicts = plotly_series(plt, series)
|
||||
for pdict in pdicts
|
||||
typ = pop!(pdict, :type)
|
||||
gt = PlotlyJS.GenericTrace(typ; pdict...)
|
||||
PlotlyJS.addtraces!(syncplot, gt)
|
||||
end
|
||||
end
|
||||
|
||||
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
|
||||
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
||||
z = series[:z]
|
||||
if z != nothing
|
||||
kw[:z] = (isa(z,Surface) ? transpose_z(series, series[:z].surf, false) : z,)
|
||||
end
|
||||
PlotlyJS.restyle!(
|
||||
plt.o,
|
||||
findfirst(plt.series_list, series),
|
||||
kw
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _update_plot_object(plt::Plot{PlotlyJSBackend})
|
||||
pdict = plotly_layout(plt)
|
||||
syncplot = plt.o
|
||||
w,h = plt[:size]
|
||||
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
|
||||
prepare_output(plt)
|
||||
if isijulia() && !_use_remote[]
|
||||
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
|
||||
else
|
||||
show(io, MIME("text/html"), plt.o)
|
||||
end
|
||||
end
|
||||
|
||||
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
|
||||
tmpfn = tempname() * "." * ext
|
||||
PlotlyJS.savefig(plt.o, tmpfn)
|
||||
write(io, read(open(tmpfn)))
|
||||
end
|
||||
_show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "svg")
|
||||
_show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "png")
|
||||
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf")
|
||||
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps")
|
||||
|
||||
function write_temp_html(plt::Plot{PlotlyJSBackend})
|
||||
filename = string(tempname(), ".html")
|
||||
savefig(plt, filename)
|
||||
filename
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PlotlyJSBackend})
|
||||
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
|
||||
display(plt.o)
|
||||
else
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function closeall(::PlotlyJSBackend)
|
||||
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
|
||||
close(current().o)
|
||||
end
|
||||
end
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot{PlotlyJSBackend}) = true
|
||||
|
||||
+470
-420
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,12 @@
|
||||
|
||||
# https://github.com/Evizero/UnicodePlots.jl
|
||||
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "unicodeplots.jl"))
|
||||
end
|
||||
|
||||
const _unicodeplots_attr = merge_with_base_supported([
|
||||
:label,
|
||||
:legend,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linestyle,
|
||||
:markershape,
|
||||
:bins,
|
||||
:title,
|
||||
:guide, :lims,
|
||||
])
|
||||
const _unicodeplots_seriestype = [
|
||||
:path, :scatter, :straightline,
|
||||
# :bar,
|
||||
:shape,
|
||||
:histogram2d,
|
||||
:spy
|
||||
]
|
||||
const _unicodeplots_style = [:auto, :solid]
|
||||
const _unicodeplots_marker = [:none, :auto, :circle]
|
||||
const _unicodeplots_scale = [:identity]
|
||||
|
||||
|
||||
# don't warn on unsupported... there's just too many warnings!!
|
||||
warnOnUnsupported_args(::UnicodePlotsBackend, d::KW) = nothing
|
||||
warnOnUnsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::UnicodePlotsBackend)
|
||||
"""
|
||||
Pkg.add("UnicodePlots")
|
||||
Pkg.build("UnicodePlots")
|
||||
"""
|
||||
end
|
||||
|
||||
function _initialize_backend(::UnicodePlotsBackend; kw...)
|
||||
@eval begin
|
||||
import UnicodePlots
|
||||
export UnicodePlots
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
const _canvas_type = Ref(:auto)
|
||||
|
||||
function _canvas_map()
|
||||
@@ -69,8 +27,8 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
for sp in plt.subplots
|
||||
xaxis = sp[:xaxis]
|
||||
yaxis = sp[:yaxis]
|
||||
xlim = axis_limits(xaxis)
|
||||
ylim = axis_limits(yaxis)
|
||||
xlim = axis_limits(sp, :x)
|
||||
ylim = axis_limits(sp, :y)
|
||||
|
||||
# make vectors
|
||||
xlim = [xlim[1], xlim[2]]
|
||||
@@ -124,7 +82,7 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
|
||||
# now use the ! functions to add to the plot
|
||||
for series in series_list(sp)
|
||||
addUnicodeSeries!(o, series.d, sp[:legend] != :none, xlim, ylim)
|
||||
addUnicodeSeries!(o, series.plotattributes, sp[:legend] != :none, xlim, ylim)
|
||||
end
|
||||
|
||||
# save the object
|
||||
@@ -134,17 +92,17 @@ end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
function addUnicodeSeries!(o, plotattributes, addlegend::Bool, xlim, ylim)
|
||||
# get the function, or special handling for step/bar/hist
|
||||
st = d[:seriestype]
|
||||
st = plotattributes[:seriestype]
|
||||
if st == :histogram2d
|
||||
UnicodePlots.densityplot!(o, d[:x], d[:y])
|
||||
UnicodePlots.densityplot!(o, plotattributes[:x], plotattributes[:y])
|
||||
return
|
||||
end
|
||||
|
||||
if st in (:path, :straightline)
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif st == :scatter || d[:markershape] != :none
|
||||
elseif st == :scatter || plotattributes[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
# elseif st == :bar
|
||||
# func = UnicodePlots.barplot!
|
||||
@@ -156,16 +114,16 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
|
||||
# get the series data and label
|
||||
x, y = if st == :straightline
|
||||
straightline_data(d)
|
||||
straightline_data(plotattributes)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
shape_data(plotattributes)
|
||||
else
|
||||
[collect(float(d[s])) for s in (:x, :y)]
|
||||
[collect(float(plotattributes[s])) for s in (:x, :y)]
|
||||
end
|
||||
label = addlegend ? d[:label] : ""
|
||||
label = addlegend ? plotattributes[:label] : ""
|
||||
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
|
||||
color = plotattributes[:linecolor] in UnicodePlots.color_cycle ? plotattributes[:linecolor] : :auto
|
||||
|
||||
# add the series
|
||||
x, y = Plots.unzip(collect(Base.Iterators.filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
|
||||
@@ -183,7 +141,7 @@ function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
|
||||
gui(plt)
|
||||
|
||||
# @osx_only begin
|
||||
@static if is_apple()
|
||||
@static if Sys.isapple()
|
||||
# BEGIN HACK
|
||||
|
||||
# wait while the plot gets drawn
|
||||
@@ -212,7 +170,7 @@ end
|
||||
|
||||
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
|
||||
unicodeplots_rebuild(plt)
|
||||
map(show, plt.o)
|
||||
foreach(x -> show(io, x), plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
+11
-7
@@ -2,9 +2,6 @@
|
||||
# NOTE: backend should implement `html_body` and `html_head`
|
||||
|
||||
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
|
||||
@require Revise begin
|
||||
Revise.track(Plots, joinpath(Pkg.dir("Plots"), "src", "backends", "web.jl"))
|
||||
end
|
||||
|
||||
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
|
||||
"""
|
||||
@@ -12,6 +9,7 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
|
||||
<html>
|
||||
<head>
|
||||
<title>$title</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
$(html_head(plt))
|
||||
</head>
|
||||
<body>
|
||||
@@ -22,16 +20,16 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
|
||||
end
|
||||
|
||||
function open_browser_window(filename::AbstractString)
|
||||
@static if is_apple()
|
||||
@static if Sys.isapple()
|
||||
return run(`open $(filename)`)
|
||||
end
|
||||
@static if is_linux() || is_bsd() # is_bsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
|
||||
@static if Sys.islinux() || Sys.isbsd() # Sys.isbsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
|
||||
return run(`xdg-open $(filename)`)
|
||||
end
|
||||
@static if is_windows()
|
||||
@static if Sys.iswindows()
|
||||
return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
|
||||
end
|
||||
warn("Unknown OS... cannot open browser window.")
|
||||
@warn("Unknown OS... cannot open browser window.")
|
||||
end
|
||||
|
||||
function write_temp_html(plt::AbstractPlot)
|
||||
@@ -44,8 +42,14 @@ function write_temp_html(plt::AbstractPlot)
|
||||
end
|
||||
|
||||
function standalone_html_window(plt::AbstractPlot)
|
||||
old = use_local_dependencies[] # save state to restore afterwards
|
||||
# if we open a browser ourself, we can host local files, so
|
||||
# when we have a local plotly downloaded this is the way to go!
|
||||
use_local_dependencies[] = isfile(plotly_local_file_path)
|
||||
filename = write_temp_html(plt)
|
||||
open_browser_window(filename)
|
||||
# restore for other backends
|
||||
use_local_dependencies[] = old
|
||||
end
|
||||
|
||||
# uses wkhtmltopdf/wkhtmltoimage: http://wkhtmltopdf.org/downloads.html
|
||||
|
||||
+94
-43
@@ -1,13 +1,13 @@
|
||||
|
||||
|
||||
const P2 = FixedSizeArrays.Vec{2,Float64}
|
||||
const P3 = FixedSizeArrays.Vec{3,Float64}
|
||||
const P2 = GeometryTypes.Point2{Float64}
|
||||
const P3 = GeometryTypes.Point3{Float64}
|
||||
|
||||
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
|
||||
nanpush!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); append!(a, b))
|
||||
compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : angle)
|
||||
compute_angle(v::P2) = (angle = atan(v[2], v[1]); angle < 0 ? 2π - angle : angle)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
@@ -58,7 +58,7 @@ end
|
||||
|
||||
"get an array of tuples of points on a circle with radius `r`"
|
||||
function partialcircle(start_θ, end_θ, n = 20, r=1)
|
||||
Tuple{Float64,Float64}[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
|
||||
Tuple{Float64,Float64}[(r*cos(u),r*sin(u)) for u in range(start_θ, stop=end_θ, length=n)]
|
||||
end
|
||||
|
||||
"interleave 2 vectors into each other (like a zipper's teeth)"
|
||||
@@ -68,7 +68,8 @@ function weave(x,y; ordering = Vector[x,y])
|
||||
while !done
|
||||
for o in ordering
|
||||
try
|
||||
push!(ret, shift!(o))
|
||||
push!(ret, popfirst!(o))
|
||||
catch
|
||||
end
|
||||
end
|
||||
done = isempty(x) && isempty(y)
|
||||
@@ -186,7 +187,7 @@ end
|
||||
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(sx)
|
||||
for i=eachindex(sx)
|
||||
sx[i] = (sx[i] - cx) * x + cx
|
||||
sy[i] = (sy[i] - cy) * y + cy
|
||||
end
|
||||
@@ -201,7 +202,7 @@ end
|
||||
"translate a Shape in space"
|
||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||
sx, sy = coords(shape)
|
||||
for i=1:length(sx)
|
||||
for i=eachindex(sx)
|
||||
sx[i] += x
|
||||
sy[i] += y
|
||||
end
|
||||
@@ -229,7 +230,7 @@ end
|
||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||
x, y = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(x)
|
||||
for i=eachindex(x)
|
||||
xi = rotate_x(x[i], y[i], Θ, cx, cy)
|
||||
yi = rotate_y(x[i], y[i], Θ, cx, cy)
|
||||
x[i], y[i] = xi, yi
|
||||
@@ -255,8 +256,24 @@ mutable struct Font
|
||||
color::Colorant
|
||||
end
|
||||
|
||||
"Create a Font from a list of unordered features"
|
||||
function font(args...)
|
||||
"""
|
||||
font(args...)
|
||||
Create a Font from a list of features. Values may be specified either as
|
||||
arguments (which are distinguished by type/value) or as keyword arguments.
|
||||
# Arguments
|
||||
- `family`: AbstractString. "serif" or "sans-serif" or "monospace"
|
||||
- `pointsize`: Integer. Size of font in points
|
||||
- `halign`: Symbol. Horizontal alignment (:hcenter, :left, or :right)
|
||||
- `valign`: Symbol. Vertical aligment (:vcenter, :top, or :bottom)
|
||||
- `rotation`: Real. Angle of rotation for text in degrees (use a non-integer type)
|
||||
- `color`: Colorant or Symbol
|
||||
# Examples
|
||||
```julia-repl
|
||||
julia> font(8)
|
||||
julia> font(family="serif",halign=:center,rotation=45.0)
|
||||
```
|
||||
"""
|
||||
function font(args...;kw...)
|
||||
|
||||
# defaults
|
||||
family = "sans-serif"
|
||||
@@ -296,7 +313,33 @@ function font(args...)
|
||||
elseif typeof(arg) <: Real
|
||||
rotation = convert(Float64, arg)
|
||||
else
|
||||
warn("Unused font arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused font arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
for symbol in keys(kw)
|
||||
if symbol == :family
|
||||
family = kw[:family]
|
||||
elseif symbol == :pointsize
|
||||
pointsize = kw[:pointsize]
|
||||
elseif symbol == :halign
|
||||
halign = kw[:halign]
|
||||
if halign == :center
|
||||
halign = :hcenter
|
||||
end
|
||||
@assert halign in (:hcenter, :left, :right)
|
||||
elseif symbol == :valign
|
||||
valign = kw[:valign]
|
||||
if valign == :center
|
||||
valign = :vcenter
|
||||
end
|
||||
@assert valign in (:vcenter, :top, :bottom)
|
||||
elseif symbol == :rotation
|
||||
rotation = kw[:rotation]
|
||||
elseif symbol == :color
|
||||
color = parse(Colorant, kw[:color])
|
||||
else
|
||||
@warn("Unused font kwarg: $symbol")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -343,15 +386,16 @@ end
|
||||
PlotText(str) = PlotText(string(str), font())
|
||||
|
||||
"""
|
||||
text(string, args...)
|
||||
text(string, args...; kw...)
|
||||
|
||||
Create a PlotText object wrapping a string with font info, for plot annotations
|
||||
Create a PlotText object wrapping a string with font info, for plot annotations.
|
||||
`args` and `kw` are passed to `font`.
|
||||
"""
|
||||
text(t::PlotText) = t
|
||||
text(t::PlotText, font::Font) = PlotText(t.str, font)
|
||||
text(str::AbstractString, f::Font) = PlotText(str, f)
|
||||
function text(str, args...)
|
||||
PlotText(string(str), font(args...))
|
||||
function text(str, args...;kw...)
|
||||
PlotText(string(str), font(args...;kw...))
|
||||
end
|
||||
|
||||
Base.length(t::PlotText) = length(t.str)
|
||||
@@ -388,13 +432,14 @@ function stroke(args...; alpha = nothing)
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
catch
|
||||
end
|
||||
elseif allAlphas(arg)
|
||||
alpha = arg
|
||||
elseif allReals(arg)
|
||||
width = arg
|
||||
else
|
||||
warn("Unused stroke arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused stroke arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -420,13 +465,14 @@ function brush(args...; alpha = nothing)
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
catch
|
||||
end
|
||||
elseif allAlphas(arg)
|
||||
alpha = arg
|
||||
elseif allReals(arg)
|
||||
size = arg
|
||||
else
|
||||
warn("Unused brush arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused brush arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -438,16 +484,16 @@ end
|
||||
mutable struct SeriesAnnotations
|
||||
strs::AbstractVector # the labels/names
|
||||
font::Font
|
||||
baseshape::Nullable
|
||||
baseshape::Union{Shape, AbstractVector{Shape}, Nothing}
|
||||
scalefactor::Tuple
|
||||
end
|
||||
function series_annotations(strs::AbstractVector, args...)
|
||||
fnt = font()
|
||||
shp = Nullable{Any}()
|
||||
shp = nothing
|
||||
scalefactor = (1,1)
|
||||
for arg in args
|
||||
if isa(arg, Shape) || (isa(arg, AbstractVector) && eltype(arg) == Shape)
|
||||
shp = Nullable(arg)
|
||||
shp = arg
|
||||
elseif isa(arg, Font)
|
||||
fnt = arg
|
||||
elseif isa(arg, Symbol) && haskey(_shapes, arg)
|
||||
@@ -457,7 +503,7 @@ function series_annotations(strs::AbstractVector, args...)
|
||||
elseif is_2tuple(arg)
|
||||
scalefactor = arg
|
||||
else
|
||||
warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
# if scalefactor != 1
|
||||
@@ -468,7 +514,7 @@ function series_annotations(strs::AbstractVector, args...)
|
||||
SeriesAnnotations(strs, fnt, shp, scalefactor)
|
||||
end
|
||||
series_annotations(anns::SeriesAnnotations) = anns
|
||||
series_annotations(::Void) = nothing
|
||||
series_annotations(::Nothing) = nothing
|
||||
|
||||
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
anns = series[:series_annotations]
|
||||
@@ -483,12 +529,13 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
# end
|
||||
|
||||
# @show msw msh
|
||||
if anns != nothing && !isnull(anns.baseshape)
|
||||
if anns !== nothing && anns.baseshape !== nothing
|
||||
# we use baseshape to overwrite the markershape attribute
|
||||
# with a list of custom shapes for each
|
||||
msw,msh = anns.scalefactor
|
||||
msize = Float64[]
|
||||
shapes = Shape[begin
|
||||
shapes = Vector{Shape}(undef, length(anns.strs))
|
||||
for i in eachindex(anns.strs)
|
||||
str = _cycle(anns.strs,i)
|
||||
|
||||
# get the width and height of the string (in mm)
|
||||
@@ -505,9 +552,9 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
# and then re-scale a copy of baseshape to match the w/h ratio
|
||||
maxscale = max(xscale, yscale)
|
||||
push!(msize, maxscale)
|
||||
baseshape = _cycle(get(anns.baseshape),i)
|
||||
shape = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
||||
end for i=1:length(anns.strs)]
|
||||
baseshape = _cycle(anns.baseshape, i)
|
||||
shapes[i] = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
||||
end
|
||||
series[:markershape] = shapes
|
||||
series[:markersize] = msize
|
||||
end
|
||||
@@ -519,9 +566,12 @@ mutable struct EachAnn
|
||||
x
|
||||
y
|
||||
end
|
||||
Base.start(ea::EachAnn) = 1
|
||||
Base.done(ea::EachAnn, i) = ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
function Base.next(ea::EachAnn, i)
|
||||
|
||||
function Base.iterate(ea::EachAnn, i = 1)
|
||||
if ea.anns === nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
return nothing
|
||||
end
|
||||
|
||||
tmp = _cycle(ea.anns.strs,i)
|
||||
str,fnt = if isa(tmp, PlotText)
|
||||
tmp.str, tmp.font
|
||||
@@ -531,7 +581,7 @@ function Base.next(ea::EachAnn, i)
|
||||
((_cycle(ea.x,i), _cycle(ea.y,i), str, fnt), i+1)
|
||||
end
|
||||
|
||||
annotations(::Void) = []
|
||||
annotations(::Nothing) = []
|
||||
annotations(anns::AVec) = anns
|
||||
annotations(anns) = Any[anns]
|
||||
annotations(sa::SeriesAnnotations) = sa
|
||||
@@ -541,13 +591,17 @@ annotations(sa::SeriesAnnotations) = sa
|
||||
function process_annotation(sp::Subplot, xs, ys, labs, font = font())
|
||||
anns = []
|
||||
labs = makevec(labs)
|
||||
for i in 1:max(length(xs), length(ys), length(labs))
|
||||
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
|
||||
xlength = length(methods(length, (typeof(xs),))) == 0 ? 1 : length(xs)
|
||||
ylength = length(methods(length, (typeof(ys),))) == 0 ? 1 : length(ys)
|
||||
for i in 1:max(xlength, ylength, length(labs))
|
||||
x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i)
|
||||
x = typeof(x) <: TimeType ? Dates.value(x) : x
|
||||
y = typeof(y) <: TimeType ? Dates.value(y) : y
|
||||
if lab == :auto
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||
else
|
||||
push!(anns, (x, y, isa(lab, PlotText) ? lab : text(lab, font)))
|
||||
push!(anns, (x, y, isa(lab, PlotText) ? lab : isa(lab, Tuple) ? text(lab...) : text(lab, font)))
|
||||
end
|
||||
end
|
||||
anns
|
||||
@@ -562,7 +616,7 @@ function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol},
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||
else
|
||||
push!(anns, (pos, isa(lab, PlotText) ? lab : text(lab, font)))
|
||||
push!(anns, (pos, isa(lab, PlotText) ? lab : isa(lab, Tuple) ? text(lab...) : text(lab, font)))
|
||||
end
|
||||
end
|
||||
anns
|
||||
@@ -609,8 +663,8 @@ Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
|
||||
|
||||
Base.Array(surf::Surface) = surf.surf
|
||||
|
||||
for f in (:length, :size)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
for f in (:length, :size, :axes)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
end
|
||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
|
||||
@@ -697,7 +751,7 @@ function arrow(args...)
|
||||
elseif T <: Tuple && length(arg) == 2
|
||||
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
|
||||
else
|
||||
warn("Skipped arrow arg $arg")
|
||||
@warn("Skipped arrow arg $arg")
|
||||
end
|
||||
end
|
||||
Arrow(style, side, headlength, headwidth)
|
||||
@@ -729,7 +783,7 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
"create a BezierCurve for plotting"
|
||||
mutable struct BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
mutable struct BezierCurve{T <: GeometryTypes.Point}
|
||||
control_points::Vector{T}
|
||||
end
|
||||
|
||||
@@ -742,12 +796,9 @@ function (bc::BezierCurve)(t::Real)
|
||||
p
|
||||
end
|
||||
|
||||
# mean(x::Real, y::Real) = 0.5*(x+y) #commented out as I cannot see this used anywhere and it overwrites a Base method with different functionality
|
||||
# mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps) # I also could not see this used anywhere, and it's type piracy - implementing a NaNMath version for this would just involve converting to a standard array
|
||||
|
||||
@deprecate curve_points coords
|
||||
|
||||
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, Base.range(range..., length=n))
|
||||
|
||||
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
|
||||
# may create a loop if necessary. Assumes the view is [0,1]
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
|
||||
# https://github.com/bokeh/Bokeh.jl
|
||||
|
||||
|
||||
supported_attrs(::BokehBackend) = merge_with_base_supported([
|
||||
# :annotations,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
:linecolor,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# :fillalpha,
|
||||
# :foreground_color,
|
||||
:group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:seriestype,
|
||||
:linewidth,
|
||||
# :linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
# :markeralpha,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
# :smooth,
|
||||
# :show,
|
||||
:size,
|
||||
:title,
|
||||
# :window_title,
|
||||
:x,
|
||||
# :xguide,
|
||||
# :xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
# :yguide,
|
||||
# :ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :legendfont,
|
||||
# :grid,
|
||||
# :surface,
|
||||
# :levels,
|
||||
])
|
||||
supported_types(::BokehBackend) = [:path, :scatter]
|
||||
supported_styles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supported_markers(::BokehBackend) = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
||||
supported_scales(::BokehBackend) = [:identity, :ln]
|
||||
is_subplot_supported(::BokehBackend) = false
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
function _initialize_backend(::BokehBackend; kw...)
|
||||
@eval begin
|
||||
warn("Bokeh is no longer supported... many features will likely be broken.")
|
||||
import Bokeh
|
||||
export Bokeh
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
const _glyphtypes = KW(
|
||||
:circle => :Circle,
|
||||
:rect => :Square,
|
||||
:diamond => :Diamond,
|
||||
:utriangle => :Triangle,
|
||||
:dtriangle => :InvertedTriangle,
|
||||
# :pentagon =>
|
||||
# :hexagon =>
|
||||
# :heptagon =>
|
||||
# :octagon =>
|
||||
:cross => :Cross,
|
||||
:xcross => :X,
|
||||
:star5 => :Asterisk,
|
||||
)
|
||||
|
||||
|
||||
function bokeh_glyph_type(d::KW)
|
||||
st = d[:seriestype]
|
||||
mt = d[:markershape]
|
||||
if st == :scatter && mt == :none
|
||||
mt = :circle
|
||||
end
|
||||
|
||||
# if we have a marker, use that
|
||||
if st == :scatter || mt != :none
|
||||
return _glyphtypes[mt]
|
||||
end
|
||||
|
||||
# otherwise return a line
|
||||
return :Line
|
||||
end
|
||||
|
||||
function get_stroke_vector(linestyle::Symbol)
|
||||
dash = 12
|
||||
dot = 3
|
||||
gap = 2
|
||||
linestyle == :solid && return Int[]
|
||||
linestyle == :dash && return Int[dash, gap]
|
||||
linestyle == :dot && return Int[dot, gap]
|
||||
linestyle == :dashdot && return Int[dash, gap, dot, gap]
|
||||
linestyle == :dashdotdot && return Int[dash, gap, dot, gap, dot, gap]
|
||||
error("unsupported linestyle: ", linestyle)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# function _create_plot(pkg::BokehBackend, d::KW)
|
||||
function _create_backend_figure(plt::Plot{BokehBackend})
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
|
||||
datacolumns = Bokeh.BokehDataSet[]
|
||||
tools = Bokeh.tools()
|
||||
filename = tempname() * ".html"
|
||||
title = plt.attr[:title]
|
||||
w, h = plt.attr[:size]
|
||||
xaxis_type = plt.attr[:xscale] == :log10 ? :log : :auto
|
||||
yaxis_type = plt.attr[:yscale] == :log10 ? :log : :auto
|
||||
# legend = plt.attr[:legend] ? xxxx : nothing
|
||||
legend = nothing
|
||||
extra_args = KW() # TODO: we'll put extra settings (xlim, etc) here
|
||||
Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
|
||||
|
||||
# Plot(bplt, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
# function _series_added(::BokehBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{BokehBackend}, series::Series)
|
||||
bdata = Dict{Symbol, Vector}(:x => collect(series.d[:x]), :y => collect(series.d[:y]))
|
||||
|
||||
glyph = Bokeh.Bokehjs.Glyph(
|
||||
glyphtype = bokeh_glyph_type(d),
|
||||
linecolor = webcolor(d[:linecolor]), # shape's stroke or line color
|
||||
linewidth = d[:linewidth], # shape's stroke width or line width
|
||||
fillcolor = webcolor(d[:markercolor]),
|
||||
size = ceil(Int, d[:markersize] * 2.5), # magic number 2.5 to keep in same scale as other backends
|
||||
dash = get_stroke_vector(d[:linestyle])
|
||||
)
|
||||
|
||||
legend = nothing # TODO
|
||||
push!(plt.o.datacolumns, Bokeh.BokehDataSet(bdata, glyph, legend))
|
||||
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot_object(plt::Plot{BokehBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
# function getxy(plt::Plot{BokehBackend}, i::Int)
|
||||
# series = plt.o.datacolumns[i].data
|
||||
# series[:x], series[:y]
|
||||
# end
|
||||
#
|
||||
# function setxy!(plt::Plot{BokehBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# series = plt.o.datacolumns[i].data
|
||||
# series[:x], series[:y] = xy
|
||||
# plt
|
||||
# end
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
|
||||
# TODO: write a png to io
|
||||
warn("mime png not implemented")
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
|
||||
Bokeh.showplot(plt.o)
|
||||
end
|
||||
|
||||
# function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
|
||||
# # TODO: display/show the subplot
|
||||
# end
|
||||
@@ -1,744 +0,0 @@
|
||||
|
||||
# https://github.com/dcjones/Gadfly.jl
|
||||
|
||||
|
||||
supported_attrs(::GadflyBackend) = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color, :foreground_color, :color_palette,
|
||||
:group, :label, :seriestype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :n, :nc, :nr, :layout, :smooth,
|
||||
:title, :window_title, :show, :size,
|
||||
:x, :xguide, :xlims, :xticks, :xscale, :xflip,
|
||||
:y, :yguide, :ylims, :yticks, :yscale, :yflip,
|
||||
:z,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:xerror, :yerror,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
])
|
||||
supported_types(::GadflyBackend) = [
|
||||
:path,
|
||||
:scatter, :hexbin,
|
||||
:bar,
|
||||
:contour, :shape
|
||||
]
|
||||
supported_styles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supported_markers(::GadflyBackend) = vcat(_allMarkers, Shape)
|
||||
supported_scales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
is_subplot_supported(::GadflyBackend) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::GadflyBackend; kw...)
|
||||
@eval begin
|
||||
import Gadfly, Compose
|
||||
export Gadfly, Compose
|
||||
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# immutable MissingVec <: AbstractVector{Float64} end
|
||||
# Base.size(v::MissingVec) = (1,)
|
||||
# Base.getindex(v::MissingVec, i::Integer) = 0.0
|
||||
|
||||
function createGadflyPlotObject(d::KW)
|
||||
gplt = Gadfly.Plot()
|
||||
gplt.mapping = Dict()
|
||||
gplt.data_source = Gadfly.DataFrames.DataFrame()
|
||||
# gplt.layers = gplt.layers[1:0]
|
||||
gplt.layers = [Gadfly.layer(Gadfly.Geom.point(tag=:remove), x=zeros(1), y=zeros(1));] # x=MissingVec(), y=MissingVec());]
|
||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xguide]),
|
||||
Gadfly.Guide.ylabel(d[:yguide]),
|
||||
Gadfly.Guide.title(d[:title])]
|
||||
gplt
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getLineGeom(d::KW)
|
||||
st = d[:seriestype]
|
||||
xbins, ybins = maketuple(d[:bins])
|
||||
if st == :hexb
|
||||
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
|
||||
elseif st == :histogram2d
|
||||
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
|
||||
elseif st == :histogram
|
||||
Gadfly.Geom.histogram(bincount = xbins,
|
||||
orientation = isvertical(d) ? :vertical : :horizontal,
|
||||
position = d[:bar_position] == :stack ? :stack : :dodge)
|
||||
elseif st == :path
|
||||
Gadfly.Geom.path
|
||||
elseif st in (:bar, :sticks)
|
||||
Gadfly.Geom.bar
|
||||
elseif st == :steppost
|
||||
Gadfly.Geom.step
|
||||
elseif st == :steppre
|
||||
Gadfly.Geom.step(direction = :vh)
|
||||
elseif st == :hline
|
||||
Gadfly.Geom.hline
|
||||
elseif st == :vline
|
||||
Gadfly.Geom.vline
|
||||
elseif st == :contour
|
||||
Gadfly.Geom.contour(levels = d[:levels])
|
||||
# elseif st == :shape
|
||||
# Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
else
|
||||
nothing
|
||||
end
|
||||
end
|
||||
|
||||
function get_extra_theme_args(d::KW, k::Symbol)
|
||||
# gracefully handles old Gadfly versions
|
||||
extra_theme_args = KW()
|
||||
try
|
||||
extra_theme_args[:line_style] = Gadfly.get_stroke_vector(d[k])
|
||||
catch err
|
||||
if string(err) == "UndefVarError(:get_stroke_vector)"
|
||||
Base.warn_once("Gadfly.get_stroke_vector failed... do you have an old version of Gadfly?")
|
||||
else
|
||||
rethrow()
|
||||
end
|
||||
end
|
||||
extra_theme_args
|
||||
end
|
||||
|
||||
function getGadflyLineTheme(d::KW)
|
||||
st = d[:seriestype]
|
||||
lc = convertColor(getColor(d[:linecolor]), d[:linealpha])
|
||||
fc = convertColor(getColor(d[:fillcolor]), d[:fillalpha])
|
||||
|
||||
Gadfly.Theme(;
|
||||
default_color = (st in (:histogram,:histogram2d,:hexbin,:bar,:sticks) ? fc : lc),
|
||||
line_width = (st == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
|
||||
# line_style = Gadfly.get_stroke_vector(d[:linestyle]),
|
||||
lowlight_color = x->RGB(fc), # fill/ribbon
|
||||
lowlight_opacity = alpha(fc), # fill/ribbon
|
||||
bar_highlight = RGB(lc), # bars
|
||||
get_extra_theme_args(d, :linestyle)...
|
||||
)
|
||||
end
|
||||
|
||||
# add a line as a new layer
|
||||
function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
|
||||
gplt = getGadflyContext(plt)
|
||||
gfargs = vcat(geoms..., getGadflyLineTheme(d))
|
||||
kwargs = KW()
|
||||
st = d[:seriestype]
|
||||
|
||||
# add a fill?
|
||||
if d[:fillrange] != nothing && st != :contour
|
||||
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
|
||||
nmin, nmax = length(fillmin), length(fillmax)
|
||||
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
push!(gfargs, Gadfly.Geom.ribbon)
|
||||
end
|
||||
|
||||
if st in (:hline, :vline)
|
||||
kwargs[st == :hline ? :yintercept : :xintercept] = d[:y]
|
||||
|
||||
else
|
||||
if st == :sticks
|
||||
w = 0.01 * mean(diff(d[:x]))
|
||||
kwargs[:xmin] = d[:x] - w
|
||||
kwargs[:xmax] = d[:x] + w
|
||||
elseif st == :contour
|
||||
kwargs[:z] = d[:z].surf
|
||||
addGadflyContColorScale(plt, d[:linecolor])
|
||||
end
|
||||
|
||||
kwargs[:x] = d[st == :histogram ? :y : :x]
|
||||
kwargs[:y] = d[:y]
|
||||
|
||||
end
|
||||
|
||||
# # add the layer
|
||||
Gadfly.layer(gfargs...; order=numlayers, kwargs...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
get_shape(sym::Symbol) = _shapes[sym]
|
||||
get_shape(shape::Shape) = shape
|
||||
|
||||
# extract the underlying ShapeGeometry object(s)
|
||||
getMarkerGeom(shapes::AVec) = gadflyshape(map(get_shape, shapes))
|
||||
getMarkerGeom(other) = gadflyshape(get_shape(other))
|
||||
|
||||
# getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
||||
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
|
||||
function getMarkerGeom(d::KW)
|
||||
if d[:seriestype] == :shape
|
||||
Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
else
|
||||
getMarkerGeom(d[:markershape])
|
||||
end
|
||||
end
|
||||
|
||||
function getGadflyMarkerTheme(d::KW, attr::KW)
|
||||
c = getColor(d[:markercolor])
|
||||
α = d[:markeralpha]
|
||||
if α != nothing
|
||||
c = RGBA(RGB(c), α)
|
||||
end
|
||||
|
||||
ms = d[:markersize]
|
||||
ms = if typeof(ms) <: AVec
|
||||
warn("Gadfly doesn't support variable marker sizes... using the average: $(mean(ms))")
|
||||
mean(ms) * Gadfly.px
|
||||
else
|
||||
ms * Gadfly.px
|
||||
end
|
||||
|
||||
Gadfly.Theme(;
|
||||
default_color = c,
|
||||
default_point_size = ms,
|
||||
discrete_highlight_color = c -> RGB(getColor(d[:markerstrokecolor])),
|
||||
highlight_width = d[:markerstrokewidth] * Gadfly.px,
|
||||
line_width = d[:markerstrokewidth] * Gadfly.px,
|
||||
# get_extra_theme_args(d, :markerstrokestyle)...
|
||||
)
|
||||
end
|
||||
|
||||
function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
|
||||
plt.attr[:colorbar] == :none && return
|
||||
if !isa(c, ColorGradient)
|
||||
c = default_gradient()
|
||||
end
|
||||
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
|
||||
end
|
||||
|
||||
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, attr::KW, geoms...)
|
||||
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, attr), getMarkerGeom(d))
|
||||
kwargs = KW()
|
||||
|
||||
# handle continuous color scales for the markers
|
||||
zcolor = d[:marker_z]
|
||||
if zcolor != nothing && typeof(zcolor) <: AVec
|
||||
kwargs[:color] = zcolor
|
||||
addGadflyContColorScale(plt, d[:markercolor])
|
||||
end
|
||||
|
||||
Gadfly.layer(gfargs...; x = d[:x], y = d[:y], order=numlayers, kwargs...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function addToGadflyLegend(plt::Plot, d::KW)
|
||||
if plt.attr[:legend] != :none && d[:label] != ""
|
||||
gplt = getGadflyContext(plt)
|
||||
|
||||
# add the legend if needed
|
||||
if all(g -> !isa(g, Gadfly.Guide.ManualColorKey), gplt.guides)
|
||||
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
|
||||
end
|
||||
|
||||
# now add the series to the legend
|
||||
for guide in gplt.guides
|
||||
if isa(guide, Gadfly.Guide.ManualColorKey)
|
||||
# TODO: there's a BUG in gadfly if you pass in the same color more than once,
|
||||
# since gadfly will call unique(colors), but doesn't also merge the rows that match
|
||||
# Should ensure from this side that colors which are the same are merged together
|
||||
|
||||
c = getColor(d[d[:markershape] == :none ? :linecolor : :markercolor])
|
||||
foundit = false
|
||||
|
||||
# extend the label if we found this color
|
||||
for i in 1:length(guide.colors)
|
||||
if RGB(c) == guide.colors[i]
|
||||
guide.labels[i] *= ", " * d[:label]
|
||||
foundit = true
|
||||
end
|
||||
end
|
||||
|
||||
# didn't find the color, so add a new entry into the legend
|
||||
if !foundit
|
||||
push!(guide.labels, d[:label])
|
||||
push!(guide.colors, c)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
getGadflySmoothing(smooth::Bool) = smooth ? [Gadfly.Geom.smooth(method=:lm)] : Any[]
|
||||
getGadflySmoothing(smooth::Real) = [Gadfly.Geom.smooth(method=:loess, smoothing=float(smooth))]
|
||||
|
||||
|
||||
function addGadflySeries!(plt::Plot, d::KW)
|
||||
layers = Gadfly.Layer[]
|
||||
gplt = getGadflyContext(plt)
|
||||
|
||||
# add a regression line?
|
||||
# TODO: make more flexible
|
||||
smooth = getGadflySmoothing(d[:smooth])
|
||||
|
||||
# lines
|
||||
geom = getLineGeom(d)
|
||||
if geom != nothing
|
||||
prepend!(layers, addGadflyLine!(plt, length(gplt.layers), d, geom, smooth...))
|
||||
smooth = Any[] # don't add a regression for markers too
|
||||
end
|
||||
|
||||
# special handling for ohlc and scatter
|
||||
st = d[:seriestype]
|
||||
# if st == :ohlc
|
||||
# error("Haven't re-implemented after refactoring")
|
||||
if st in (:histogram2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
|
||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
|
||||
elseif st == :scatter && d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
end
|
||||
|
||||
# markers
|
||||
if d[:markershape] != :none || st == :shape
|
||||
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.attr, smooth...))
|
||||
end
|
||||
|
||||
st in (:histogram2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
|
||||
|
||||
# now save the layers that apply to this series
|
||||
d[:gadflylayers] = layers
|
||||
prepend!(gplt.layers, layers)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# NOTE: I'm leaving this here and commented out just in case I want to implement again... it was hacky code to create multi-colored line segments
|
||||
|
||||
# # colorgroup
|
||||
# z = d[:z]
|
||||
|
||||
# # handle line segments of different colors
|
||||
# cscheme = d[:linecolor]
|
||||
# if isa(cscheme, ColorVector)
|
||||
# # create a color scale, and set the color group to the index of the color
|
||||
# push!(gplt.scales, Gadfly.Scale.color_discrete_manual(cscheme.v...))
|
||||
|
||||
# # this is super weird, but... oh well... for some reason this creates n separate line segments...
|
||||
# # create a list of vertices that go: [x1,x2,x2,x3,x3, ... ,xi,xi, ... xn,xn] (same for y)
|
||||
# # then the vector passed to the "color" keyword should be a vector: [1,1,2,2,3,3,4,4, ..., i,i, ... , n,n]
|
||||
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(d[:y])]
|
||||
# cs = collect(repmat(csindices', 2, 1))[1:end-1]
|
||||
# grp = collect(repmat((1:length(d[:y]))', 2, 1))[1:end-1]
|
||||
# d[:x], d[:y] = map(createSegments, (d[:x], d[:y]))
|
||||
# colorgroup = [(:linecolor, cs), (:group, grp)]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function addGadflyTicksGuide(gplt, ticks, isx::Bool)
|
||||
ticks == :auto && return
|
||||
|
||||
# remove the ticks?
|
||||
if ticks in (:none, false, nothing)
|
||||
return addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
||||
end
|
||||
|
||||
ttype = ticksType(ticks)
|
||||
|
||||
# just the values... put ticks here, but use standard labels
|
||||
if ttype == :ticks
|
||||
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
|
||||
replaceType(gplt.guides, gtype(ticks = collect(ticks)))
|
||||
|
||||
# set the ticks and the labels
|
||||
# Note: this is pretty convoluted, but I think it works. We set the ticks using Gadfly.Guide,
|
||||
# and then set the label function (wraps a dict lookup) through a continuous Gadfly.Scale.
|
||||
elseif ttype == :ticks_and_labels
|
||||
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
|
||||
replaceType(gplt.guides, gtype(ticks = collect(ticks[1])))
|
||||
|
||||
# # TODO add xtick_label function (given tick, return label??)
|
||||
# # Scale.x_discrete(; labels=nothing, levels=nothing, order=nothing)
|
||||
# filterGadflyScale(gplt, isx)
|
||||
# gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
|
||||
# labelmap = Dict(zip(ticks...))
|
||||
# labelfunc = val -> labelmap[val]
|
||||
# push!(gplt.scales, gfunc(levels = collect(ticks[1]), labels = labelfunc))
|
||||
|
||||
filterGadflyScale(gplt, isx)
|
||||
gfunc = isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous
|
||||
labelmap = Dict(zip(ticks...))
|
||||
labelfunc = val -> labelmap[val]
|
||||
push!(gplt.scales, gfunc(labels = labelfunc))
|
||||
|
||||
else
|
||||
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
|
||||
end
|
||||
end
|
||||
|
||||
continuousAndSameAxis(scale, isx::Bool) = isa(scale, Gadfly.Scale.ContinuousScale) && scale.vars[1] == (isx ? :x : :y)
|
||||
filterGadflyScale(gplt, isx::Bool) = filter!(scale -> !continuousAndSameAxis(scale, isx), gplt.scales)
|
||||
|
||||
|
||||
function getGadflyScaleFunction(d::KW, isx::Bool)
|
||||
scalekey = isx ? :xscale : :yscale
|
||||
hasScaleKey = haskey(d, scalekey)
|
||||
if hasScaleKey
|
||||
scale = d[scalekey]
|
||||
scale == :ln && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey, log
|
||||
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey, log2
|
||||
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey, log10
|
||||
scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey, asinh
|
||||
scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey, sqrt
|
||||
end
|
||||
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey, identity
|
||||
end
|
||||
|
||||
|
||||
function addGadflyLimitsScale(gplt, d::KW, isx::Bool)
|
||||
gfunc, hasScaleKey, func = getGadflyScaleFunction(d, isx)
|
||||
|
||||
# do we want to add min/max limits for the axis?
|
||||
limsym = isx ? :xlims : :ylims
|
||||
limargs = Any[]
|
||||
|
||||
# map :auto to nothing, otherwise add to limargs
|
||||
lims = get(d, limsym, :auto)
|
||||
if lims == :auto
|
||||
lims = nothing
|
||||
else
|
||||
if limsType(lims) == :limits
|
||||
push!(limargs, (:minvalue, min(lims...)))
|
||||
push!(limargs, (:maxvalue, max(lims...)))
|
||||
else
|
||||
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
|
||||
end
|
||||
end
|
||||
|
||||
# replace any current scales with this one
|
||||
if hasScaleKey || !isempty(limargs)
|
||||
filterGadflyScale(gplt, isx)
|
||||
push!(gplt.scales, gfunc(; limargs...))
|
||||
end
|
||||
|
||||
lims, func
|
||||
end
|
||||
|
||||
function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
|
||||
if isa(gplt.coord, Gadfly.Coord.Cartesian)
|
||||
gplt.coord = Gadfly.Coord.cartesian(
|
||||
gplt.coord.xvars,
|
||||
gplt.coord.yvars;
|
||||
xmin = xlims == nothing ? gplt.coord.xmin : xfunc(minimum(xlims)),
|
||||
xmax = xlims == nothing ? gplt.coord.xmax : xfunc(maximum(xlims)),
|
||||
ymin = ylims == nothing ? gplt.coord.ymin : yfunc(minimum(ylims)),
|
||||
ymax = ylims == nothing ? gplt.coord.ymax : yfunc(maximum(ylims)),
|
||||
xflip = get(d, :xflip, gplt.coord.xflip),
|
||||
yflip = get(d, :yflip, gplt.coord.yflip),
|
||||
fixed = gplt.coord.fixed,
|
||||
aspect_ratio = gplt.coord.aspect_ratio,
|
||||
raster = gplt.coord.raster
|
||||
)
|
||||
else
|
||||
gplt.coord = Gadfly.Coord.Cartesian(
|
||||
xflip = get(d, :xflip, false),
|
||||
yflip = get(d, :yflip, false)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function findGuideAndSet(gplt, t::DataType, args...; kw...)
|
||||
for (i,guide) in enumerate(gplt.guides)
|
||||
if isa(guide, t)
|
||||
gplt.guides[i] = t(args...; kw...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function updateGadflyGuides(plt::Plot, d::KW)
|
||||
gplt = getGadflyContext(plt)
|
||||
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
|
||||
haskey(d, :xguide) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xguide]))
|
||||
haskey(d, :yguide) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:yguide]))
|
||||
|
||||
xlims, xfunc = addGadflyLimitsScale(gplt, d, true)
|
||||
ylims, yfunc = addGadflyLimitsScale(gplt, d, false)
|
||||
|
||||
ticks = get(d, :xticks, :auto)
|
||||
if ticks == :none
|
||||
_remove_axis(plt, true)
|
||||
else
|
||||
addGadflyTicksGuide(gplt, ticks, true)
|
||||
end
|
||||
ticks = get(d, :yticks, :auto)
|
||||
if ticks == :none
|
||||
_remove_axis(plt, false)
|
||||
else
|
||||
addGadflyTicksGuide(gplt, ticks, false)
|
||||
end
|
||||
|
||||
updateGadflyAxisFlips(gplt, d, xlims, ylims, xfunc, yfunc)
|
||||
end
|
||||
|
||||
function updateGadflyPlotTheme(plt::Plot, d::KW)
|
||||
kwargs = KW()
|
||||
|
||||
# colors
|
||||
insidecolor, gridcolor, textcolor, guidecolor, legendcolor =
|
||||
map(s -> getColor(d[s]), (
|
||||
:background_color_inside,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_text,
|
||||
:foreground_color_guide,
|
||||
:foreground_color_legend
|
||||
))
|
||||
|
||||
# # hide the legend?
|
||||
leg = d[d[:legend] == :none ? :colorbar : :legend]
|
||||
if leg != :best
|
||||
kwargs[:key_position] = leg == :inside ? :right : leg
|
||||
end
|
||||
|
||||
if !get(d, :grid, true)
|
||||
kwargs[:grid_color] = gridcolor
|
||||
end
|
||||
|
||||
# fonts
|
||||
tfont, gfont, lfont = d[:tickfont], d[:guidefont], d[:legendfont]
|
||||
|
||||
getGadflyContext(plt).theme = Gadfly.Theme(;
|
||||
background_color = insidecolor,
|
||||
minor_label_color = textcolor,
|
||||
minor_label_font = tfont.family,
|
||||
minor_label_font_size = tfont.pointsize * Gadfly.pt,
|
||||
major_label_color = guidecolor,
|
||||
major_label_font = gfont.family,
|
||||
major_label_font_size = gfont.pointsize * Gadfly.pt,
|
||||
key_title_color = guidecolor,
|
||||
key_title_font = gfont.family,
|
||||
key_title_font_size = gfont.pointsize * Gadfly.pt,
|
||||
key_label_color = legendcolor,
|
||||
key_label_font = lfont.family,
|
||||
key_label_font_size = lfont.pointsize * Gadfly.pt,
|
||||
plot_padding = 1 * Gadfly.mm,
|
||||
kwargs...
|
||||
)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
function createGadflyAnnotationObject(x, y, val::AbstractString)
|
||||
Gadfly.Guide.annotation(Compose.compose(
|
||||
Compose.context(),
|
||||
Compose.text(x, y, val)
|
||||
))
|
||||
end
|
||||
|
||||
function createGadflyAnnotationObject(x, y, txt::PlotText)
|
||||
halign = (txt.font.halign == :hcenter ? Compose.hcenter : (txt.font.halign == :left ? Compose.hleft : Compose.hright))
|
||||
valign = (txt.font.valign == :vcenter ? Compose.vcenter : (txt.font.valign == :top ? Compose.vtop : Compose.vbottom))
|
||||
rotations = (txt.font.rotation == 0.0 ? [] : [Compose.Rotation(txt.font.rotation, Compose.Point(Compose.x_measure(x), Compose.y_measure(y)))])
|
||||
Gadfly.Guide.annotation(Compose.compose(
|
||||
Compose.context(),
|
||||
Compose.text(x, y, txt.str, halign, valign, rotations...),
|
||||
Compose.font(string(txt.font.family)),
|
||||
Compose.fontsize(txt.font.pointsize * Gadfly.pt),
|
||||
Compose.stroke(txt.font.color),
|
||||
Compose.fill(txt.font.color)
|
||||
))
|
||||
end
|
||||
|
||||
function _add_annotations(plt::Plot{GadflyBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
# function _create_plot(pkg::GadflyBackend, d::KW)
|
||||
# gplt = createGadflyPlotObject(d)
|
||||
# Plot(gplt, pkg, 0, d, KW[])
|
||||
# end
|
||||
function _create_backend_figure(plt::Plot{GadflyBackend})
|
||||
createGadflyPlotObject(plt.attr)
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
# function _series_added(::GadflyBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{GadflyBackend}, series::Series)
|
||||
# first clear out the temporary layer
|
||||
gplt = getGadflyContext(plt)
|
||||
if gplt.layers[1].geom.tag == :remove
|
||||
gplt.layers = gplt.layers[2:end]
|
||||
end
|
||||
|
||||
addGadflySeries!(plt, series.d)
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{GadflyBackend}, d::KW)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
# TODO: need to save all the layer indices which apply to this series
|
||||
function getGadflyMappings(plt::Plot, i::Integer)
|
||||
@assert i > 0 && i <= plt.n
|
||||
mappings = [l.mapping for l in plt.seriesargs[i][:gadflylayers]]
|
||||
end
|
||||
|
||||
function getxy(plt::Plot{GadflyBackend}, i::Integer)
|
||||
mapping = getGadflyMappings(plt, i)[1]
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function setxy!(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
# # create the underlying object (each backend will do this differently)
|
||||
# function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
|
||||
# isbefore && return false # wait until after plotting to create the subplots
|
||||
# subplt.o = nothing
|
||||
# true
|
||||
# end
|
||||
|
||||
|
||||
function _remove_axis(plt::Plot{GadflyBackend}, isx::Bool)
|
||||
gplt = getGadflyContext(plt)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{GadflyBackend}, isx::Bool)
|
||||
for l in getGadflyContext(plt).layers
|
||||
_expand_limits(lims, l.mapping[isx ? :x : :y])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
getGadflyContext(plt::Plot{GadflyBackend}) = plt.o
|
||||
# getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
|
||||
|
||||
# # create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
|
||||
# function buildGadflySubplotContext(subplt::Subplot)
|
||||
# rows = Any[]
|
||||
# row = Any[]
|
||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
||||
#
|
||||
# # add the Plot object to the row
|
||||
# push!(row, getGadflyContext(subplt.plts[i]))
|
||||
#
|
||||
# # add the row
|
||||
# if c == ncols(subplt.layout, r)
|
||||
# push!(rows, Gadfly.hstack(row...))
|
||||
# row = Any[]
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # stack the rows
|
||||
# Gadfly.vstack(rows...)
|
||||
# end
|
||||
|
||||
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
|
||||
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
|
||||
# setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getattr(subplt, 1)[:size]...)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
function doshow(io::IO, func, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
|
||||
gplt = getGadflyContext(plt)
|
||||
setGadflyDisplaySize(plt)
|
||||
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
|
||||
end
|
||||
|
||||
getGadflyWriteFunc(::MIME"image/png") = Gadfly.PNG
|
||||
getGadflyWriteFunc(::MIME"image/svg+xml") = Gadfly.SVG
|
||||
# getGadflyWriteFunc(::MIME"text/html") = Gadfly.SVGJS
|
||||
getGadflyWriteFunc(::MIME"application/pdf") = Gadfly.PDF
|
||||
getGadflyWriteFunc(::MIME"application/postscript") = Gadfly.PS
|
||||
getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF
|
||||
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
|
||||
|
||||
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex")
|
||||
@eval function Base.show(io::IO, ::$mime, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
|
||||
func = getGadflyWriteFunc($mime())
|
||||
doshow(io, func, plt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{GadflyBackend})
|
||||
setGadflyDisplaySize(plt.attr[:size]...)
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
|
||||
# setGadflyDisplaySize(getattr(subplt,1)[:size]...)
|
||||
# ctx = buildGadflySubplotContext(subplt)
|
||||
#
|
||||
# # taken from Gadfly since I couldn't figure out how to do it directly
|
||||
#
|
||||
# filename = string(Gadfly.tempname(), ".html")
|
||||
# output = open(filename, "w")
|
||||
#
|
||||
# plot_output = IOBuffer()
|
||||
# Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
|
||||
# Compose.default_graphic_height, false), ctx)
|
||||
# plotsvg = takebuf_string(plot_output)
|
||||
#
|
||||
# write(output,
|
||||
# """
|
||||
# <!DOCTYPE html>
|
||||
# <html>
|
||||
# <head>
|
||||
# <title>Gadfly Plot</title>
|
||||
# <meta charset="utf-8">
|
||||
# </head>
|
||||
# <body>
|
||||
# <script charset="utf-8">
|
||||
# $(readall(Compose.snapsvgjs))
|
||||
# </script>
|
||||
# <script charset="utf-8">
|
||||
# $(readall(Gadfly.gadflyjs))
|
||||
# </script>
|
||||
# $(plotsvg)
|
||||
# </body>
|
||||
# </html>
|
||||
# """)
|
||||
# close(output)
|
||||
# Gadfly.open_file(filename)
|
||||
# end
|
||||
@@ -1,93 +0,0 @@
|
||||
|
||||
|
||||
# Geometry which displays arbitrary shapes at given (x, y) positions.
|
||||
# note: vertices is a list of shapes
|
||||
struct ShapeGeometry <: Gadfly.GeometryElement
|
||||
vertices::AbstractVector #{Tuple{Float64,Float64}}
|
||||
tag::Symbol
|
||||
|
||||
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
|
||||
new(shape, tag)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: add for PR
|
||||
# const shape = ShapeGeometry
|
||||
|
||||
|
||||
function Gadfly.element_aesthetics(::ShapeGeometry)
|
||||
[:x, :y, :size, :color]
|
||||
end
|
||||
|
||||
|
||||
# Generate a form for a shape geometry.
|
||||
#
|
||||
# Args:
|
||||
# geom: shape geometry.
|
||||
# theme: the plot's theme.
|
||||
# aes: aesthetics.
|
||||
#
|
||||
# Returns:
|
||||
# A compose Form.
|
||||
#
|
||||
function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics)
|
||||
|
||||
# TODO: add for PR
|
||||
# Gadfly.assert_aesthetics_defined("Geom.shape", aes, :x, :y)
|
||||
# Gadfly.assert_aesthetics_equal_length("Geom.shape", aes,
|
||||
# element_aesthetics(geom)...)
|
||||
|
||||
default_aes = Gadfly.Aesthetics()
|
||||
default_aes.color = Gadfly.DataFrames.PooledDataArray(RGBA{Float32}[theme.default_color])
|
||||
default_aes.size = Compose.Measure[theme.default_point_size]
|
||||
aes = Gadfly.inherit(aes, default_aes)
|
||||
|
||||
lw_hover_scale = 10
|
||||
lw_ratio = theme.line_width / aes.size[1]
|
||||
|
||||
aes_x, aes_y = Gadfly.concretize(aes.x, aes.y)
|
||||
|
||||
ctx = Compose.compose!(
|
||||
Compose.context(),
|
||||
make_polygon(geom, aes.x, aes.y, aes.size),
|
||||
Compose.fill(aes.color),
|
||||
Compose.linewidth(theme.highlight_width))
|
||||
|
||||
if aes.color_key_continuous != nothing && aes.color_key_continuous
|
||||
Compose.compose!(ctx,
|
||||
Compose.stroke(map(theme.continuous_highlight_color, aes.color)))
|
||||
else
|
||||
Compose.compose!(ctx,
|
||||
Compose.stroke(map(theme.discrete_highlight_color, aes.color)),
|
||||
Compose.svgclass([Gadfly.svg_color_class_from_label(Gadfly.escape_id(aes.color_label([c])[1]))
|
||||
for c in aes.color]))
|
||||
end
|
||||
|
||||
return Compose.compose!(Compose.context(order=4), Compose.svgclass("geometry"), ctx)
|
||||
end
|
||||
|
||||
function gadflyshape(sv::Shape)
|
||||
ShapeGeometry(Any[vertices(sv)])
|
||||
end
|
||||
|
||||
function gadflyshape(sv::AVec{Shape})
|
||||
ShapeGeometry(Any[vertices(s) for s in sv])
|
||||
end
|
||||
|
||||
|
||||
# create a Compose context given a ShapeGeometry and the xs/ys/sizes
|
||||
function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
|
||||
n = max(length(xs), length(ys), length(rs))
|
||||
T = Tuple{Compose.Measure, Compose.Measure}
|
||||
polys = Array(Vector{T}, n)
|
||||
for i in 1:n
|
||||
x = Compose.x_measure(xs[mod1(i, length(xs))])
|
||||
y = Compose.y_measure(ys[mod1(i, length(ys))])
|
||||
r = rs[mod1(i, length(rs))]
|
||||
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in _cycle(geom.vertices, i)]
|
||||
end
|
||||
Gadfly.polygon(polys, geom.tag)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
@@ -1,186 +0,0 @@
|
||||
|
||||
# https://github.com/JuliaGraphics/Immerse.jl
|
||||
|
||||
supported_attrs(::ImmerseBackend) = supported_attrs(GadflyBackend())
|
||||
supported_types(::ImmerseBackend) = supported_types(GadflyBackend())
|
||||
supported_styles(::ImmerseBackend) = supported_styles(GadflyBackend())
|
||||
supported_markers(::ImmerseBackend) = supported_markers(GadflyBackend())
|
||||
supported_scales(::ImmerseBackend) = supported_scales(GadflyBackend())
|
||||
is_subplot_supported(::ImmerseBackend) = true
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::ImmerseBackend; kw...)
|
||||
@eval begin
|
||||
import Immerse, Gadfly, Compose, Gtk
|
||||
export Immerse, Gadfly, Compose, Gtk
|
||||
include(joinpath(dirname(@__FILE__), "gadfly_shapes.jl"))
|
||||
end
|
||||
end
|
||||
|
||||
function createImmerseFigure(d::KW)
|
||||
w,h = d[:size]
|
||||
figidx = Immerse.figure(; name = d[:window_title], width = w, height = h)
|
||||
Immerse.Figure(figidx)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
# function _create_plot(pkg::ImmerseBackend, d::KW)
|
||||
# # create the underlying Gadfly.Plot object
|
||||
# gplt = createGadflyPlotObject(d)
|
||||
#
|
||||
# # save both the Immerse.Figure and the Gadfly.Plot
|
||||
# Plot((nothing,gplt), pkg, 0, d, KW[])
|
||||
# end
|
||||
function _create_backend_figure(plt::Plot{ImmerseBackend})
|
||||
(nothing, createGadflyPlotObject(plt.attr))
|
||||
end
|
||||
|
||||
|
||||
# # plot one data series
|
||||
# function _series_added(::ImmerseBackend, plt::Plot, d::KW)
|
||||
# addGadflySeries!(plt, d)
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
# end
|
||||
|
||||
function _series_added(plt::Plot{ImmerseBackend}, series::Series)
|
||||
addGadflySeries!(plt, series.d)
|
||||
end
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{ImmerseBackend}, d::KW)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _add_annotations(plt::Plot{ImmerseBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function getxy(plt::Plot{ImmerseBackend}, i::Integer)
|
||||
mapping = getGadflyMappings(plt, i)[1]
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function setxy!(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
# function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
|
||||
# return false
|
||||
# # isbefore && return false
|
||||
# end
|
||||
#
|
||||
# function showSubplotObject(subplt::Subplot{ImmerseBackend})
|
||||
# # create the Gtk window with vertical box vsep
|
||||
# d = getattr(subplt,1)
|
||||
# w,h = d[:size]
|
||||
# vsep = Gtk.GtkBoxLeaf(:v)
|
||||
# win = Gtk.GtkWindowLeaf(vsep, d[:window_title], w, h)
|
||||
#
|
||||
# figindices = []
|
||||
# row = Gtk.GtkBoxLeaf(:h)
|
||||
# push!(vsep, row)
|
||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
||||
# plt = subplt.plts[i]
|
||||
#
|
||||
# # get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
|
||||
# box, toolbar, canvas = Immerse.createPlotGuiComponents()
|
||||
#
|
||||
# # add the plot's box to the row
|
||||
# push!(row, box)
|
||||
#
|
||||
# # create the figure and store the index returned for destruction later
|
||||
# figidx = Immerse.figure(canvas)
|
||||
# push!(figindices, figidx)
|
||||
#
|
||||
# fig = Immerse.figure(figidx)
|
||||
# plt.o = (fig, plt.o[2])
|
||||
#
|
||||
# # add the row
|
||||
# if c == ncols(subplt.layout, r)
|
||||
# row = Gtk.GtkBoxLeaf(:h)
|
||||
# push!(vsep, row)
|
||||
# end
|
||||
#
|
||||
# end
|
||||
#
|
||||
# # destructor... clean up plots
|
||||
# Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
|
||||
#
|
||||
# subplt.o = win
|
||||
# true
|
||||
# end
|
||||
|
||||
|
||||
function _remove_axis(plt::Plot{ImmerseBackend}, isx::Bool)
|
||||
gplt = getGadflyContext(plt)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
|
||||
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{ImmerseBackend}, isx::Bool)
|
||||
for l in getGadflyContext(plt).layers
|
||||
_expand_limits(lims, l.mapping[isx ? :x : :y])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
|
||||
# getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
|
||||
|
||||
fig, gplt = plt.o
|
||||
if fig == nothing
|
||||
fig = createImmerseFigure(plt.attr)
|
||||
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
|
||||
plt.o = (fig, gplt)
|
||||
end
|
||||
|
||||
Immerse.figure(fig.figno; displayfig = false)
|
||||
display(gplt)
|
||||
end
|
||||
|
||||
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
|
||||
#
|
||||
# # if we haven't created the window yet, do it
|
||||
# if subplt.o == nothing
|
||||
# showSubplotObject(subplt)
|
||||
# end
|
||||
#
|
||||
# # display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
|
||||
# for plt in subplt.plts
|
||||
# fig, gplt = plt.o
|
||||
# Immerse.figure(fig.figno; displayfig = false)
|
||||
# display(gplt)
|
||||
# end
|
||||
#
|
||||
# # o is the window... show it
|
||||
# showall(subplt.o)
|
||||
# end
|
||||
@@ -1,308 +0,0 @@
|
||||
|
||||
# https://github.com/tbreloff/Qwt.jl
|
||||
|
||||
|
||||
supported_attrs(::QwtBackend) = merge_with_base_supported([
|
||||
:annotations,
|
||||
:linecolor,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:label,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:bins,
|
||||
:pos,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale,
|
||||
])
|
||||
supported_types(::QwtBackend) = [:path, :scatter, :hexbin, :bar]
|
||||
supported_markers(::QwtBackend) = [:none, :auto, :rect, :circle, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
|
||||
supported_scales(::QwtBackend) = [:identity, :log10]
|
||||
is_subplot_supported(::QwtBackend) = true
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::QwtBackend; kw...)
|
||||
@eval begin
|
||||
warn("Qwt is no longer supported... many features will likely be broken.")
|
||||
import Qwt
|
||||
export Qwt
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
const _qwtAliases = KW(
|
||||
:bins => :heatmap_n,
|
||||
:fillrange => :fillto,
|
||||
:linewidth => :width,
|
||||
:markershape => :marker,
|
||||
:hexbin => :heatmap,
|
||||
:path => :line,
|
||||
:steppost => :step,
|
||||
:steppre => :stepinverted,
|
||||
:star5 => :star1,
|
||||
:star8 => :star2,
|
||||
)
|
||||
|
||||
function fixcolors(d::KW)
|
||||
for (k,v) in d
|
||||
if typeof(v) <: ColorScheme
|
||||
d[k] = getColor(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function replaceQwtAliases(d, s)
|
||||
if haskey(_qwtAliases, d[s])
|
||||
d[s] = _qwtAliases[d[s]]
|
||||
end
|
||||
end
|
||||
|
||||
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
|
||||
d = KW(kw)
|
||||
st = d[:seriestype]
|
||||
if st == :scatter
|
||||
d[:seriestype] = :none
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
end
|
||||
|
||||
elseif st in (:hline, :vline)
|
||||
addLineMarker(plt, d)
|
||||
d[:seriestype] = :none
|
||||
d[:markershape] = :circle
|
||||
d[:markersize] = 1
|
||||
if st == :vline
|
||||
d[:x], d[:y] = d[:y], d[:x]
|
||||
end
|
||||
|
||||
elseif !iscreating && st == :bar
|
||||
d = barHack(; kw...)
|
||||
elseif !iscreating && st == :histogram
|
||||
d = barHack(; histogramHack(; kw...)...)
|
||||
end
|
||||
|
||||
replaceQwtAliases(d, :seriestype)
|
||||
replaceQwtAliases(d, :markershape)
|
||||
|
||||
for k in keys(d)
|
||||
if haskey(_qwtAliases, k)
|
||||
d[_qwtAliases[k]] = d[k]
|
||||
end
|
||||
end
|
||||
|
||||
d[:x] = collect(d[:x])
|
||||
d[:y] = collect(d[:y])
|
||||
|
||||
d
|
||||
end
|
||||
|
||||
# function _create_plot(pkg::QwtBackend, d::KW)
|
||||
function _create_backend_figure(plt::Plot{QwtBackend})
|
||||
fixcolors(plt.attr)
|
||||
dumpdict(plt.attr,"\n\n!!! plot")
|
||||
o = Qwt.plot(zeros(0,0); plt.attr..., show=false)
|
||||
# plt = Plot(o, pkg, 0, d, KW[])
|
||||
# plt
|
||||
end
|
||||
|
||||
# function _series_added(::QwtBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{QwtBackend}, series::Series)
|
||||
d = adjustQwtKeywords(plt, false; series.d...)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot!")
|
||||
Qwt.oplot(plt.o; d...)
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
|
||||
lims = get(d, isx ? :xlims : :ylims, nothing)
|
||||
ticks = get(d, isx ? :xticks : :yticks, nothing)
|
||||
w = plt.o.widget
|
||||
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
|
||||
|
||||
if typeof(lims) <: Union{Tuple,AVec} && length(lims) == 2
|
||||
if isx
|
||||
plt.o.autoscale_x = false
|
||||
else
|
||||
plt.o.autoscale_y = false
|
||||
end
|
||||
w[:setAxisScale](axisid, lims...)
|
||||
end
|
||||
|
||||
if typeof(ticks) <: Range
|
||||
if isx
|
||||
plt.o.autoscale_x = false
|
||||
else
|
||||
plt.o.autoscale_y = false
|
||||
end
|
||||
w[:setAxisScale](axisid, float(minimum(ticks)), float(maximum(ticks)), float(step(ticks)))
|
||||
elseif !(ticks in (nothing, :none, :auto))
|
||||
warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
|
||||
end
|
||||
|
||||
# change the scale
|
||||
scalesym = isx ? :xscale : :yscale
|
||||
if haskey(d, scalesym)
|
||||
scaletype = d[scalesym]
|
||||
scaletype == :identity && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLinearScaleEngine())
|
||||
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
|
||||
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
|
||||
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
|
||||
scaletype in supported_scales() || warn("Unsupported scale type: ", scaletype)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{QwtBackend}, d::KW)
|
||||
haskey(d, :title) && Qwt.title(plt.o, d[:title])
|
||||
haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide])
|
||||
haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide])
|
||||
updateLimsAndTicks(plt, d, true)
|
||||
updateLimsAndTicks(plt, d, false)
|
||||
end
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{QwtBackend}, d::KW)
|
||||
haskey(d, :size) && Qwt.resizewidget(plt.o, d[:size]...)
|
||||
haskey(d, :pos) && Qwt.movewidget(plt.o, d[:pos]...)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
|
||||
function addLineMarker(plt::Plot{QwtBackend}, d::KW)
|
||||
for yi in d[:y]
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
ishorizontal = (d[:seriestype] == :hline)
|
||||
marker[:setLineStyle](ishorizontal ? 1 : 2)
|
||||
marker[ishorizontal ? :setYValue : :setXValue](yi)
|
||||
qcolor = Qwt.convertRGBToQColor(getColor(d[:linecolor]))
|
||||
linestyle = plt.o.widget[:getLineStyle](string(d[:linestyle]))
|
||||
marker[:setLinePen](Qwt.QT.QPen(qcolor, d[:linewidth], linestyle))
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
# marker[:setValue](x, y)
|
||||
# marker[:setLabel](Qwt.QWT.QwtText(val))
|
||||
# marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
marker[:setValue](x, y)
|
||||
qwttext = Qwt.QWT.QwtText(val.str)
|
||||
qwttext[:setFont](Qwt.QT.QFont(val.font.family, val.font.pointsize))
|
||||
qwttext[:setColor](Qwt.convertRGBToQColor(getColor(val.font.color)))
|
||||
marker[:setLabel](qwttext)
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function createQwtAnnotation(plt::Plot, x, y, val::AbstractString)
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
marker[:setValue](x, y)
|
||||
marker[:setLabel](Qwt.QWT.QwtText(val))
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function _add_annotations(plt::Plot{QwtBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
createQwtAnnotation(plt, ann...)
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function getxy(plt::Plot{QwtBackend}, i::Int)
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y
|
||||
end
|
||||
|
||||
function setxy!(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y = xy
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# # create the underlying object (each backend will do this differently)
|
||||
# function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
|
||||
# isbefore && return false
|
||||
# i = 0
|
||||
# rows = Any[]
|
||||
# row = Any[]
|
||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
||||
# push!(row, subplt.plts[i].o)
|
||||
# if c == ncols(subplt.layout, r)
|
||||
# push!(rows, Qwt.hsplitter(row...))
|
||||
# row = Any[]
|
||||
# end
|
||||
# end
|
||||
# # for rowcnt in subplt.layout.rowcounts
|
||||
# # push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
||||
# # i += rowcnt
|
||||
# # end
|
||||
# subplt.o = Qwt.vsplitter(rows...)
|
||||
# # Qwt.resizewidget(subplt.o, getattr(subplt,1)[:size]...)
|
||||
# # Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
|
||||
# true
|
||||
# end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{QwtBackend}, isx::Bool)
|
||||
for series in plt.o.lines
|
||||
_expand_limits(lims, isx ? series.x : series.y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _remove_axis(plt::Plot{QwtBackend}, isx::Bool)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.show(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
|
||||
Qwt.refresh(plt.o)
|
||||
Qwt.savepng(plt.o, "/tmp/dfskjdhfkh.png")
|
||||
write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
end
|
||||
|
||||
# function Base.show(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
|
||||
# for plt in subplt.plts
|
||||
# Qwt.refresh(plt.o)
|
||||
# end
|
||||
# Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
|
||||
# write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
# end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
|
||||
Qwt.refresh(plt.o)
|
||||
Qwt.showwidget(plt.o)
|
||||
end
|
||||
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
|
||||
# for plt in subplt.plts
|
||||
# Qwt.refresh(plt.o)
|
||||
# end
|
||||
# Qwt.showwidget(subplt.o)
|
||||
# end
|
||||
@@ -1,272 +0,0 @@
|
||||
|
||||
# https://github.com/nolta/Winston.jl
|
||||
|
||||
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
|
||||
|
||||
supported_attrs(::WinstonBackend) = merge_with_base_supported([
|
||||
:annotations,
|
||||
:linecolor,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:label,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:bins,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide, :lims, :scale,
|
||||
])
|
||||
supported_types(::WinstonBackend) = [:path, :scatter, :bar]
|
||||
supported_styles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supported_markers(::WinstonBackend) = [:none, :auto, :rect, :circle, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
||||
supported_scales(::WinstonBackend) = [:identity, :log10]
|
||||
is_subplot_supported(::WinstonBackend) = false
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _initialize_backend(::WinstonBackend; kw...)
|
||||
@eval begin
|
||||
# ENV["WINSTON_OUTPUT"] = "gtk"
|
||||
warn("Winston is no longer supported... many features will likely be broken.")
|
||||
import Winston, Gtk
|
||||
export Winston, Gtk
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
## dictionaries for conversion of Plots.jl names to Winston ones.
|
||||
const winston_linestyle = KW(:solid=>"solid",
|
||||
:dash=>"dash",
|
||||
:dot=>"dotted",
|
||||
:dashdot=>"dotdashed"
|
||||
)
|
||||
|
||||
const winston_marker = KW(:none=>".",
|
||||
:rect => "square",
|
||||
:circle=>"circle",
|
||||
:diamond=>"diamond",
|
||||
:utriangle=>"triangle",
|
||||
:dtriangle=>"down-triangle",
|
||||
:cross => "plus",
|
||||
:xcross => "cross",
|
||||
:star5 => "asterisk"
|
||||
)
|
||||
|
||||
function _before_update(plt::Plot{WinstonBackend})
|
||||
Winston.ghf(plt.o)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_backend_figure(plt::Plot{WinstonBackend})
|
||||
Winston.FramedPlot(
|
||||
title = plt.attr[:title],
|
||||
xlabel = plt.attr[:xguide],
|
||||
ylabel = plt.attr[:yguide]
|
||||
)
|
||||
end
|
||||
|
||||
copy_remove(d::KW, s::Symbol) = delete!(copy(d), s)
|
||||
|
||||
function addRegressionLineWinston(d::KW, wplt)
|
||||
xs, ys = regressionXY(d[:x], d[:y])
|
||||
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
|
||||
end
|
||||
|
||||
function getWinstonItems(plt::Plot)
|
||||
if isa(plt.o, Winston.FramedPlot)
|
||||
wplt = plt.o
|
||||
window, canvas = nothing, nothing
|
||||
else
|
||||
window, canvas, wplt = plt.o
|
||||
end
|
||||
window, canvas, wplt
|
||||
end
|
||||
|
||||
function _series_added(plt::Plot{WinstonBackend}, series::Series)
|
||||
d = series.d
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
# until we call it normally, do the hack
|
||||
if d[:seriestype] == :bar
|
||||
d = barHack(;d...)
|
||||
end
|
||||
|
||||
|
||||
e = KW()
|
||||
e[:color] = getColor(d[:linecolor])
|
||||
e[:linewidth] = d[:linewidth]
|
||||
e[:kind] = winston_linestyle[d[:linestyle]]
|
||||
e[:symbolkind] = winston_marker[d[:markershape]]
|
||||
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
|
||||
e[:symbolsize] = d[:markersize] / 5
|
||||
|
||||
# pos # (Int,Int), move the enclosing window to this position
|
||||
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
|
||||
|
||||
|
||||
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :histogram2d, :hexbin, :histogram, :bar
|
||||
if d[:seriestype] == :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
|
||||
elseif d[:seriestype] == :path
|
||||
x, y = d[:x], d[:y]
|
||||
Winston.add(wplt, Winston.Curve(x, y; e...))
|
||||
|
||||
fillrange = d[:fillrange]
|
||||
if fillrange != nothing
|
||||
if isa(fillrange, AbstractVector)
|
||||
y2 = fillrange
|
||||
else
|
||||
y2 = Float64[fillrange for yi in y]
|
||||
end
|
||||
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
|
||||
end
|
||||
|
||||
elseif d[:seriestype] == :scatter
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
end
|
||||
|
||||
# elseif d[:seriestype] == :step
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:seriestype] == :stepinverted
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:seriestype] == :sticks
|
||||
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
|
||||
|
||||
# elseif d[:seriestype] == :dots
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:seriestype] == :histogram2d
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:seriestype] == :hexbin
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:seriestype] == :histogram
|
||||
hst = hist(d[:y], d[:bins])
|
||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
|
||||
|
||||
# elseif d[:seriestype] == :bar
|
||||
# # fn = Winston.XXX
|
||||
|
||||
else
|
||||
error("seriestype $(d[:seriestype]) not supported by Winston.")
|
||||
|
||||
end
|
||||
|
||||
|
||||
# markershape
|
||||
if d[:markershape] != :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
end
|
||||
|
||||
|
||||
# optionally add a regression line
|
||||
d[:smooth] && d[:seriestype] != :histogram && addRegressionLineWinston(d, wplt)
|
||||
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const _winstonNames = KW(
|
||||
:xlims => :xrange,
|
||||
:ylims => :yrange,
|
||||
:xscale => :xlog,
|
||||
:yscale => :ylog,
|
||||
)
|
||||
|
||||
function _update_plot_object(plt::Plot{WinstonBackend}, d::KW)
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
for k in (:xguide, :yguide, :title, :xlims, :ylims)
|
||||
if haskey(d, k)
|
||||
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
|
||||
end
|
||||
end
|
||||
|
||||
for k in (:xscale, :yscale)
|
||||
if haskey(d, k)
|
||||
islogscale = d[k] == :log10
|
||||
Winston.setattr(wplt, (k == :xscale ? :xlog : :ylog), islogscale)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::AbstractString)
|
||||
Winston.text(x, y, val)
|
||||
end
|
||||
|
||||
function _add_annotations(plt::Plot{WinstonBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
createWinstonAnnotationObject(plt, ann...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
|
||||
# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function addWinstonLegend(plt::Plot, wplt)
|
||||
if plt.attr[:legend] != :none
|
||||
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
|
||||
end
|
||||
end
|
||||
|
||||
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{WinstonBackend})
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
addWinstonLegend(plt, wplt)
|
||||
show(io, "image/png", wplt)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
|
||||
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
if window == nothing
|
||||
if Winston.output_surface != :gtk
|
||||
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
|
||||
end
|
||||
# initialize window
|
||||
w,h = plt.attr[:size]
|
||||
canvas = Gtk.GtkCanvasLeaf()
|
||||
window = Gtk.GtkWindowLeaf(canvas, plt.attr[:window_title], w, h)
|
||||
plt.o = (window, canvas, wplt)
|
||||
end
|
||||
|
||||
addWinstonLegend(plt, wplt)
|
||||
|
||||
Winston.display(canvas, wplt)
|
||||
Gtk.showall(window)
|
||||
end
|
||||
|
||||
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
|
||||
# # TODO: display/show the Subplot object
|
||||
# end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,415 +0,0 @@
|
||||
|
||||
abstract type ColorScheme end
|
||||
|
||||
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
|
||||
|
||||
export
|
||||
cgrad
|
||||
|
||||
cgrad() = default_gradient()
|
||||
|
||||
function cgrad(arg, values = nothing; alpha = nothing, scale = :identity)
|
||||
colors = ColorGradient(arg, alpha=alpha).colors
|
||||
values = if values != nothing
|
||||
values
|
||||
elseif scale in (:log, :log10)
|
||||
log10(linspace(1,10,30))
|
||||
elseif scale == :log2
|
||||
log2(linspace(1,2,30))
|
||||
elseif scale == :ln
|
||||
log(linspace(1,pi,30))
|
||||
elseif scale in (:exp, :exp10)
|
||||
(exp10(linspace(0,1,30)) - 1) / 9
|
||||
else
|
||||
linspace(0, 1, length(colors))
|
||||
end
|
||||
ColorGradient(colors, values)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
getColor(scheme::ColorScheme) = getColor(scheme, 1)
|
||||
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
|
||||
|
||||
colorscheme(scheme::ColorScheme) = scheme
|
||||
colorscheme(s::AbstractString; kw...) = colorscheme(Symbol(s); kw...)
|
||||
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
|
||||
colorscheme(s::Symbol, vals::AVec{T}; kw...) where {T<:Real} = ColorGradient(s, vals; kw...)
|
||||
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
|
||||
colorscheme(cs::AVec{T}; kw...) where {T<:Colorant} = ColorGradient(cs; kw...)
|
||||
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
|
||||
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
|
||||
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
|
||||
colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
convertColor(c::AbstractString) = parse(Colorant, c)
|
||||
convertColor(c::Symbol) = parse(Colorant, string(c))
|
||||
convertColor(c::Colorant) = c
|
||||
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
|
||||
convertColor(c::ColorScheme) = c
|
||||
convertColor(v::Void) = RGBA(0,0,0,0)
|
||||
convertColor(b::Bool) = b ? RGBA(0,0,0,1) : RGBA(0,0,0,0)
|
||||
|
||||
function convertColor(c, α::Real)
|
||||
c = convertColor(c)
|
||||
RGBA(RGB(getColor(c)), α)
|
||||
end
|
||||
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
|
||||
convertColor(c, α::Void) = convertColor(c)
|
||||
|
||||
# backup... try to convert
|
||||
getColor(c) = convertColor(c)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
function darken(c, v=0.1)
|
||||
rgba = convert(RGBA, c)
|
||||
r = max(0, min(rgba.r - v, 1))
|
||||
g = max(0, min(rgba.g - v, 1))
|
||||
b = max(0, min(rgba.b - v, 1))
|
||||
RGBA(r,g,b,rgba.alpha)
|
||||
end
|
||||
function lighten(c, v=0.3)
|
||||
darken(c, -v)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
const _rainbowColors = [colorant"purple", colorant"blue", colorant"green", colorant"orange", colorant"red"]
|
||||
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
|
||||
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
|
||||
|
||||
const _gradients = KW(
|
||||
:blues => [colorant"lightblue", colorant"darkblue"],
|
||||
:reds => [colorant"lightpink", colorant"darkred"],
|
||||
:greens => [colorant"lightgreen", colorant"darkgreen"],
|
||||
:redsblues => [colorant"darkred", RGB(0.8,0.85,0.8), colorant"darkblue"],
|
||||
:bluesreds => [colorant"darkblue", RGB(0.8,0.85,0.8), colorant"darkred"],
|
||||
:heat => [colorant"lightyellow", colorant"orange", colorant"darkred"],
|
||||
:grays => [RGB(.95,.95,.95),RGB(.05,.05,.05)],
|
||||
:rainbow => _rainbowColors,
|
||||
:lightrainbow => map(lighten, _rainbowColors),
|
||||
:darkrainbow => map(darken, _rainbowColors),
|
||||
:darktest => _testColors,
|
||||
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
||||
)
|
||||
|
||||
function register_gradient_colors(name::Symbol, colors::AVec{C}) where C<:Colorant
|
||||
_gradients[name] = colors
|
||||
end
|
||||
|
||||
include("color_gradients.jl")
|
||||
|
||||
default_gradient() = ColorGradient(:inferno)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
|
||||
struct ColorGradient <: ColorScheme
|
||||
colors::Vector
|
||||
values::Vector
|
||||
|
||||
function ColorGradient(cs::AVec, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing) where S<:Real
|
||||
if length(cs) == length(vals)
|
||||
return new(convertColor(cs,alpha), collect(vals))
|
||||
end
|
||||
|
||||
# # otherwise interpolate evenly between the minval and maxval
|
||||
# minval, maxval = minimum(vals), maximum(vals)
|
||||
# vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
|
||||
# new(convertColor(cs,alpha), vs)
|
||||
|
||||
# interpolate the colors for each value
|
||||
vals = merge(linspace(0, 1, length(cs)), vals)
|
||||
grad = ColorGradient(cs)
|
||||
cs = [getColorZ(grad, z) for z in linspace(0, 1, length(vals))]
|
||||
new(convertColor(cs, alpha), vals)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
Base.getindex(cs::ColorGradient, i::Integer) = getColor(cs, i)
|
||||
Base.getindex(cs::ColorGradient, z::Number) = getColorZ(cs, z)
|
||||
|
||||
|
||||
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
|
||||
function ColorGradient(s::Symbol, vals::AVec{T} = 0:0; kw...) where T<:Real
|
||||
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
|
||||
cs = _gradients[s]
|
||||
if vals == 0:0
|
||||
vals = linspace(0, 1, length(cs))
|
||||
end
|
||||
ColorGradient(cs, vals; kw...)
|
||||
end
|
||||
|
||||
# function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = linspace(0, 1, length(cs)); kw...)
|
||||
# ColorGradient(map(convertColor, cs), vals; kw...)
|
||||
# end
|
||||
|
||||
function ColorGradient(grad::ColorGradient; alpha = nothing)
|
||||
ColorGradient(convertColor(grad.colors, alpha), grad.values)
|
||||
end
|
||||
|
||||
# anything else just gets the default gradient
|
||||
function ColorGradient(cw; alpha=nothing)
|
||||
ColorGradient(default_gradient(), alpha=alpha)
|
||||
end
|
||||
|
||||
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]
|
||||
|
||||
function getColorZ(gradient::ColorGradient, z::Real)
|
||||
cs = gradient.colors
|
||||
vs = gradient.values
|
||||
n = length(cs)
|
||||
@assert n > 0 && n == length(vs)
|
||||
|
||||
# can we just return the first color?
|
||||
if z <= vs[1] || n == 1
|
||||
return cs[1]
|
||||
end
|
||||
|
||||
# find the bounding colors and interpolate
|
||||
for i in 2:n
|
||||
if z <= vs[i]
|
||||
return interpolate_rgb(cs[i-1], cs[i], (z - vs[i-1]) / (vs[i] - vs[i-1]))
|
||||
end
|
||||
end
|
||||
|
||||
# if we get here, return the last color
|
||||
cs[end]
|
||||
end
|
||||
|
||||
getColorVector(gradient::ColorGradient) = gradient.colors
|
||||
|
||||
# for 0.3
|
||||
Colors.RGBA(c::Colorant) = RGBA(red(c), green(c), blue(c), alpha(c))
|
||||
Colors.RGB(c::Colorant) = RGB(red(c), green(c), blue(c))
|
||||
|
||||
function interpolate_rgb(c1::Colorant, c2::Colorant, w::Real)
|
||||
rgb1 = RGBA(c1)
|
||||
rgb2 = RGBA(c2)
|
||||
r = interpolate(rgb1.r, rgb2.r, w)
|
||||
g = interpolate(rgb1.g, rgb2.g, w)
|
||||
b = interpolate(rgb1.b, rgb2.b, w)
|
||||
a = interpolate(rgb1.alpha, rgb2.alpha, w)
|
||||
RGBA(r, g, b, a)
|
||||
end
|
||||
|
||||
|
||||
function interpolate(v1::Real, v2::Real, w::Real)
|
||||
(1-w) * v1 + w * v2
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a function, taking an index and returning a Colorant"
|
||||
struct ColorFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a function, taking an z-value and returning a Colorant"
|
||||
struct ColorZFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
getColorZ(scheme::ColorZFunction, z::Real) = scheme.f(z)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
|
||||
struct ColorVector <: ColorScheme
|
||||
v::Vector{Colorant}
|
||||
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
|
||||
end
|
||||
|
||||
getColor(scheme::ColorVector, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))])
|
||||
getColorVector(scheme::ColorVector) = scheme.v
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a single color"
|
||||
struct ColorWrapper <: ColorScheme
|
||||
c::RGBA
|
||||
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
|
||||
end
|
||||
|
||||
ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Colorant, s), alpha))
|
||||
|
||||
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
|
||||
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
|
||||
convertColor(c::ColorWrapper, α::Void) = c.c
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
isbackgrounddark(bgcolor::Color) = Lab(bgcolor).l < 0.5
|
||||
|
||||
# move closer to lighter/darker depending on background value
|
||||
function adjustAway(val, bgval, vmin=0., vmax=100.)
|
||||
if bgval < 0.5 * (vmax+vmin)
|
||||
tmp = max(val, bgval)
|
||||
return 0.5 * (tmp + max(tmp, vmax))
|
||||
else
|
||||
tmp = min(val, bgval)
|
||||
return 0.5 * (tmp + min(tmp, vmin))
|
||||
end
|
||||
end
|
||||
|
||||
# borrowed from http://stackoverflow.com/a/1855903:
|
||||
lightnessLevel(c::Colorant) = 0.299 * red(c) + 0.587 * green(c) + 0.114 * blue(c)
|
||||
|
||||
isdark(c::Colorant) = lightnessLevel(c) < 0.5
|
||||
islight(c::Colorant) = !isdark(c)
|
||||
|
||||
function convertHexToRGB(h::Unsigned)
|
||||
mask = 0x0000FF
|
||||
RGB([(x & mask) / 0xFF for x in (h >> 16, h >> 8, h)]...)
|
||||
end
|
||||
|
||||
# note: I found this list of hex values in a comment by Tatarize here: http://stackoverflow.com/a/12224359
|
||||
const _masterColorList = [
|
||||
0xFFFFFF, 0x000000, 0x0000FF, 0x00FF00, 0xFF0000, 0x01FFFE, 0xFFA6FE, 0xFFDB66, 0x006401, 0x010067,
|
||||
0x95003A, 0x007DB5, 0xFF00F6, 0xFFEEE8, 0x774D00, 0x90FB92, 0x0076FF, 0xD5FF00, 0xFF937E, 0x6A826C,
|
||||
0xFF029D, 0xFE8900, 0x7A4782, 0x7E2DD2, 0x85A900, 0xFF0056, 0xA42400, 0x00AE7E, 0x683D3B, 0xBDC6FF,
|
||||
0x263400, 0xBDD393, 0x00B917, 0x9E008E, 0x001544, 0xC28C9F, 0xFF74A3, 0x01D0FF, 0x004754, 0xE56FFE,
|
||||
0x788231, 0x0E4CA1, 0x91D0CB, 0xBE9970, 0x968AE8, 0xBB8800, 0x43002C, 0xDEFF74, 0x00FFC6, 0xFFE502,
|
||||
0x620E00, 0x008F9C, 0x98FF52, 0x7544B1, 0xB500FF, 0x00FF78, 0xFF6E41, 0x005F39, 0x6B6882, 0x5FAD4E,
|
||||
0xA75740, 0xA5FFD2, 0xFFB167, 0x009BFF, 0xE85EBE
|
||||
]
|
||||
const _allColors = map(convertHexToRGB, _masterColorList)
|
||||
const _darkColors = filter(isdark, _allColors)
|
||||
const _lightColors = filter(islight, _allColors)
|
||||
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
|
||||
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
|
||||
|
||||
const _defaultNumColors = 17
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
# Methods to automatically generate gradients for color selection based on
|
||||
# background color and a short list of seed colors
|
||||
|
||||
# here are some magic constants that could be changed if you really want
|
||||
const _lightness_darkbg = [80.0]
|
||||
const _lightness_lightbg = [60.0]
|
||||
const _lch_c_const = [60]
|
||||
|
||||
function adjust_lch(color, l, c)
|
||||
lch = convert(LCHab, color)
|
||||
convert(RGB, LCHab(l, c, lch.h))
|
||||
end
|
||||
|
||||
function lightness_from_background(bgcolor)
|
||||
bglight = convert(LCHab, bgcolor).l
|
||||
bglight < 50.0 ? _lightness_darkbg[1] : _lightness_lightbg[1]
|
||||
end
|
||||
|
||||
function gradient_from_list(cs)
|
||||
zvalues = Plots.get_zvalues(length(cs))
|
||||
indices = sortperm(zvalues)
|
||||
sorted_colors = map(RGBA, cs[indices])
|
||||
sorted_zvalues = zvalues[indices]
|
||||
ColorGradient(sorted_colors, sorted_zvalues)
|
||||
end
|
||||
|
||||
function generate_colorgradient(bgcolor = colorant"white";
|
||||
color_bases = color_bases=[colorant"steelblue",colorant"orangered"],
|
||||
lightness = lightness_from_background(bgcolor),
|
||||
chroma = _lch_c_const[1],
|
||||
n = _defaultNumColors)
|
||||
seed_colors = vcat(bgcolor, map(c -> adjust_lch(c, lightness, chroma), color_bases))
|
||||
colors = distinguishable_colors(n,
|
||||
seed_colors,
|
||||
lchoices=Float64[lightness],
|
||||
cchoices=Float64[chroma],
|
||||
hchoices=linspace(0, 340, 20)
|
||||
)[2:end]
|
||||
gradient_from_list(colors)
|
||||
end
|
||||
|
||||
function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
|
||||
grad = if palette == :auto
|
||||
generate_colorgradient(bgcolor)
|
||||
else
|
||||
ColorGradient(palette)
|
||||
end
|
||||
zrng = get_zvalues(numcolors)
|
||||
RGBA[getColorZ(grad, z) for z in zrng]
|
||||
end
|
||||
|
||||
function get_color_palette(palette::Vector{C},
|
||||
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer) where C<:Colorant
|
||||
palette
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getpctrange(n::Int)
|
||||
n > 0 || error()
|
||||
n == 1 && return zeros(1)
|
||||
zs = [0.0, 1.0]
|
||||
for i in 3:n
|
||||
sorted = sort(zs)
|
||||
diffs = diff(sorted)
|
||||
widestj = 0
|
||||
widest = 0.0
|
||||
for (j,d) in enumerate(diffs)
|
||||
if d > widest
|
||||
widest = d
|
||||
widestj = j
|
||||
end
|
||||
end
|
||||
push!(zs, sorted[widestj] + 0.5 * diffs[widestj])
|
||||
end
|
||||
zs
|
||||
end
|
||||
|
||||
function get_zvalues(n::Int)
|
||||
offsets = getpctrange(ceil(Int,n/4)+1)/4
|
||||
offsets = vcat(offsets[1], offsets[3:end])
|
||||
zvalues = Float64[]
|
||||
for offset in offsets
|
||||
append!(zvalues, offset + [0.0, 0.5, 0.25, 0.75])
|
||||
end
|
||||
vcat(zvalues[1], 1.0, zvalues[2:n-1])
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
make255(x) = round(Int, 255 * x)
|
||||
|
||||
function webcolor(c::Color)
|
||||
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
|
||||
end
|
||||
function webcolor(c::TransparentColor)
|
||||
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
|
||||
end
|
||||
webcolor(cs::ColorScheme) = webcolor(getColor(cs))
|
||||
webcolor(c) = webcolor(convertColor(c))
|
||||
webcolor(c, α) = webcolor(convertColor(getColor(c), α))
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
function getSeriesRGBColor(c, sp::Subplot, n::Int)
|
||||
|
||||
if c == :auto
|
||||
c = autopick(sp[:color_palette], n)
|
||||
end
|
||||
|
||||
# c should now be a subtype of ColorScheme
|
||||
colorscheme(c)
|
||||
end
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
|
||||
# TODO:
|
||||
"""
|
||||
- load Contours.jl similar to DataFrames
|
||||
- method to build grid from x/y/z vectors
|
||||
- method to wrap contours creation
|
||||
- method to plot contours as custom shapes (TODO: create Stroke and Fill types and add markerstroke/markerfill args)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# # ----------------------------------------------------------
|
||||
# # ----------------------------------------------------------
|
||||
|
||||
|
||||
# immutable Vertex
|
||||
# x::Float64
|
||||
# y::Float64
|
||||
# z::Float64
|
||||
# end
|
||||
|
||||
# immutable Edge
|
||||
# v::Vertex
|
||||
# u::Vertex
|
||||
# end
|
||||
|
||||
# # ----------------------------------------------------------
|
||||
|
||||
# # one rectangle's z-values and the center vertex
|
||||
# # z is ordered: topleft, topright, bottomright, bottomleft
|
||||
# immutable GridRect
|
||||
# z::Vector{Float64}
|
||||
# center::Vertex
|
||||
# data::Vector{Vertex}
|
||||
# end
|
||||
|
||||
|
||||
|
||||
# type Grid
|
||||
# xs::Vector{Float64}
|
||||
# ys::Vector{Float64}
|
||||
# rects::Matrix{GridRect}
|
||||
# end
|
||||
|
||||
# function splitDataEvenly(v::AbstractVector{Float64}, n::Int)
|
||||
# vs = sort(v)
|
||||
|
||||
# end
|
||||
|
||||
# # the goal here is to create the vertical and horizontal partitions
|
||||
# # which define the grid, so that the data is somewhat evenly split
|
||||
# function bucketData(x, y, z)
|
||||
|
||||
# end
|
||||
|
||||
|
||||
# function buildGrid(x, y, z)
|
||||
# # create
|
||||
# end
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
# create a new "build_series_args" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
|
||||
# Special handling for: no args, xmin/xmax, parametric, dataframes
|
||||
# Then once inputs have been converted, build the series args, map functions, etc.
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
const FuncOrFuncs = Union{Function, AVec{Function}}
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::Void, d::KW) = Any[nothing], nothing
|
||||
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
|
||||
# numeric vector
|
||||
convertToAnyVector(v::AVec{T}, d::KW) where {T<:Number} = Any[v], nothing
|
||||
|
||||
# string vector
|
||||
convertToAnyVector(v::AVec{T}, d::KW) where {T<:AbstractString} = Any[v], nothing
|
||||
|
||||
function convertToAnyVector(v::AMat, d::KW)
|
||||
if all3D(d)
|
||||
Any[Surface(v)]
|
||||
else
|
||||
Any[v[:,i] for i in 1:size(v,2)]
|
||||
end, nothing
|
||||
end
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
|
||||
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
|
||||
# dates
|
||||
convertToAnyVector(dts::AVec{D}, d::KW) where {D<:Union{Date,DateTime}} = Any[dts], nothing
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
function convertToAnyVector(v::AVec, d::KW)
|
||||
if all(x -> typeof(x) <: Number, v)
|
||||
# all real numbers wrap the whole vector as one item
|
||||
Any[convert(Vector{Float64}, v)], nothing
|
||||
else
|
||||
# something else... treat each element as an item
|
||||
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
|
||||
# Any[vi for vi in v], nothing
|
||||
end
|
||||
end
|
||||
|
||||
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
|
||||
|
||||
|
||||
function convertToAnyVector(args...)
|
||||
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
# result in that array being shared. push!, etc will add too many items to that array
|
||||
|
||||
compute_x(x::Void, y::Void, z) = 1:size(z,1)
|
||||
compute_x(x::Void, y, z) = 1:size(y,1)
|
||||
compute_x(x::Function, y, z) = map(x, y)
|
||||
compute_x(x, y, z) = copy(x)
|
||||
|
||||
# compute_y(x::Void, y::Function, z) = error()
|
||||
compute_y(x::Void, y::Void, z) = 1:size(z,2)
|
||||
compute_y(x, y::Function, z) = map(y, x)
|
||||
compute_y(x, y, z) = copy(y)
|
||||
|
||||
compute_z(x, y, z::Function) = map(z, x, y)
|
||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Void) = nothing
|
||||
compute_z(x, y, z) = copy(z)
|
||||
|
||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
||||
nobigs(v) = v
|
||||
|
||||
@noinline function compute_xyz(x, y, z)
|
||||
x = compute_x(x,y,z)
|
||||
y = compute_y(x,y,z)
|
||||
z = compute_z(x,y,z)
|
||||
nobigs(x), nobigs(y), nobigs(z)
|
||||
end
|
||||
|
||||
# not allowed
|
||||
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
+1027
-446
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
# ---------------------------------------------------------
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
|
||||
_fileio_load(@nospecialize(filename::AbstractString)) = FileIO.load(filename::AbstractString)
|
||||
_fileio_save(@nospecialize(filename::AbstractString), @nospecialize(x)) = FileIO.save(filename::AbstractString, x)
|
||||
|
||||
function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
|
||||
fn = tempname()
|
||||
|
||||
# first save a pdf file
|
||||
pdf(plt, fn)
|
||||
|
||||
# load that pdf into a FileIO Stream
|
||||
s = _fileio_load(fn * ".pdf")
|
||||
|
||||
# save a png
|
||||
pngfn = fn * ".png"
|
||||
_fileio_save(pngfn, s)
|
||||
|
||||
# now write from the file
|
||||
write(io, read(open(pngfn), String))
|
||||
end
|
||||
|
||||
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||
@@ -0,0 +1,62 @@
|
||||
const use_local_dependencies = Ref(false)
|
||||
const use_local_plotlyjs = Ref(false)
|
||||
|
||||
|
||||
function _init_ijulia_plotting()
|
||||
# IJulia is more stable with local file
|
||||
use_local_plotlyjs[] = isfile(plotly_local_file_path)
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||
|
||||
The default is nothing, except for plotly based backends, where it
|
||||
adds data for `application/vnd.plotly.v1+json` that is used in
|
||||
frontends like jupyterlab and nteract.
|
||||
"""
|
||||
_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out
|
||||
|
||||
function _ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out
|
||||
end
|
||||
|
||||
function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out
|
||||
end
|
||||
|
||||
|
||||
function _ijulia_display_dict(plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
out = Dict()
|
||||
if output_type == :txt
|
||||
mime = "text/plain"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
elseif output_type == :png
|
||||
mime = "image/png"
|
||||
out[mime] = base64encode(show, MIME(mime), plt)
|
||||
elseif output_type == :svg
|
||||
mime = "image/svg+xml"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
elseif output_type == :html
|
||||
mime = "text/html"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
else
|
||||
error("Unsupported output type $output_type")
|
||||
end
|
||||
_ijulia__extra_mime_info!(plt, out)
|
||||
out
|
||||
end
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
using REPL
|
||||
|
||||
|
||||
function _plots_defaults()
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
Dict{Symbol,Any}(Main.PLOTS_DEFAULTS)
|
||||
else
|
||||
Dict{Symbol,Any}()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function __init__()
|
||||
user_defaults = _plots_defaults()
|
||||
if haskey(user_defaults, :theme)
|
||||
theme(user_defaults[:theme])
|
||||
end
|
||||
for (k,v) in user_defaults
|
||||
k == :theme || default(k, v)
|
||||
end
|
||||
|
||||
insert!(Base.Multimedia.displays, findlast(x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||
|
||||
atreplinit(i -> begin
|
||||
while PlotsDisplay() in Base.Multimedia.displays
|
||||
popdisplay(PlotsDisplay())
|
||||
end
|
||||
insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||
end)
|
||||
|
||||
@require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" begin
|
||||
fn = joinpath(@__DIR__, "backends", "hdf5.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require InspectDR = "d0351b0e-4b05-5898-87b3-e2a8edfddd1d" begin
|
||||
fn = joinpath(@__DIR__, "backends", "inspectdr.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require PGFPlots = "3b7a836e-365b-5785-a47d-02c71176b4aa" begin
|
||||
fn = joinpath(@__DIR__, "backends", "pgfplots.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require ORCA = "47be7bcc-f1a6-5447-8b36-7eeeff7534fd" begin
|
||||
fn = joinpath(@__DIR__, "backends", "orca.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925" begin
|
||||
fn = joinpath(@__DIR__, "backends", "pgfplotsx.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" begin
|
||||
fn = joinpath(@__DIR__, "backends", "plotlyjs.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" begin
|
||||
fn = joinpath(@__DIR__, "backends", "pyplot.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" begin
|
||||
fn = joinpath(@__DIR__, "backends", "unicodeplots.jl")
|
||||
include(fn)
|
||||
@require Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise.track(Plots, fn)
|
||||
end
|
||||
|
||||
@require IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" begin
|
||||
if IJulia.inited
|
||||
_init_ijulia_plotting()
|
||||
|
||||
IJulia.display_dict(plt::Plot) = _ijulia_display_dict(plt)
|
||||
end
|
||||
end
|
||||
|
||||
if haskey(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL")
|
||||
use_local_plotlyjs[] = ENV["PLOTS_HOST_DEPENDENCY_LOCAL"] == "true"
|
||||
use_local_dependencies[] = isfile(plotly_local_file_path) && use_local_plotlyjs[]
|
||||
if use_local_plotlyjs[] && !isfile(plotly_local_file_path)
|
||||
@warn("PLOTS_HOST_DEPENDENCY_LOCAL is set to true, but no local plotly file found. run Pkg.build(\"Plots\") and make sure PLOTS_HOST_DEPENDENCY_LOCAL is set to true")
|
||||
end
|
||||
else
|
||||
use_local_dependencies[] = use_local_plotlyjs[]
|
||||
end
|
||||
|
||||
|
||||
@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin
|
||||
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) = _show_pdfbackends(io, mime, plt)
|
||||
end
|
||||
end
|
||||
+36
-19
@@ -5,8 +5,8 @@ to_pixels(m::AbsoluteLength) = m.value / 0.254
|
||||
|
||||
const _cbar_width = 5mm
|
||||
|
||||
Base.broadcast(::typeof(Base.:.*), m::Measure, n::Number) = m * n
|
||||
Base.broadcast(::typeof(Base.:.*), m::Number, n::Measure) = m * n
|
||||
#Base.broadcast(::typeof(Base.:.*), m::Measure, n::Number) = m * n
|
||||
#Base.broadcast(::typeof(Base.:.*), m::Number, n::Measure) = m * n
|
||||
Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
|
||||
Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
|
||||
Base.zero(::Type{typeof(mm)}) = 0mm
|
||||
@@ -107,11 +107,10 @@ function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
|
||||
if mix.len != 0mm
|
||||
f = (letter == :x ? width : height)
|
||||
totlen = f(plotarea(sp))
|
||||
@show totlen
|
||||
pct += mix.len / totlen
|
||||
end
|
||||
if pct != 0
|
||||
amin, amax = axis_limits(sp[Symbol(letter,:axis)])
|
||||
amin, amax = axis_limits(sp, letter)
|
||||
xy += pct * (amax-amin)
|
||||
end
|
||||
xy
|
||||
@@ -147,7 +146,7 @@ function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...)
|
||||
elseif oarg in (:top, :bottom, :vcenter)
|
||||
origver = oarg
|
||||
else
|
||||
warn("Unused origin arg in bbox construction: $oarg")
|
||||
@warn("Unused origin arg in bbox construction: $oarg")
|
||||
end
|
||||
end
|
||||
bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver)
|
||||
@@ -218,6 +217,7 @@ bottompad(layout::AbstractLayout) = 0mm
|
||||
# this is the parent of the top-level layout
|
||||
struct RootLayout <: AbstractLayout end
|
||||
|
||||
Base.show(io::IO, layout::RootLayout) = Base.show_default(io, layout)
|
||||
Base.parent(::RootLayout) = nothing
|
||||
parent_bbox(::RootLayout) = defaultbox
|
||||
bbox(::RootLayout) = defaultbox
|
||||
@@ -268,7 +268,7 @@ function GridLayout(dims...;
|
||||
widths = zeros(dims[2]),
|
||||
heights = zeros(dims[1]),
|
||||
kw...)
|
||||
grid = Matrix{AbstractLayout}(dims...)
|
||||
grid = Matrix{AbstractLayout}(undef, dims...)
|
||||
layout = GridLayout(
|
||||
parent,
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
@@ -357,10 +357,10 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
# get the max horizontal (left and right) padding over columns,
|
||||
# and max vertical (bottom and top) padding over rows
|
||||
# TODO: add extra padding here
|
||||
pad_left = maximum(minpad_left, 1)
|
||||
pad_top = maximum(minpad_top, 2)
|
||||
pad_right = maximum(minpad_right, 1)
|
||||
pad_bottom = maximum(minpad_bottom, 2)
|
||||
pad_left = maximum(minpad_left, dims = 1)
|
||||
pad_top = maximum(minpad_top, dims = 2)
|
||||
pad_right = maximum(minpad_right, dims = 1)
|
||||
pad_bottom = maximum(minpad_bottom, dims = 2)
|
||||
|
||||
# make sure the perimeter match the parent
|
||||
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
|
||||
@@ -464,12 +464,12 @@ end
|
||||
# constructors
|
||||
|
||||
# pass the layout arg through
|
||||
function layout_args(d::KW)
|
||||
layout_args(get(d, :layout, default(:layout)))
|
||||
function layout_args(plotattributes::AKW)
|
||||
layout_args(plotattributes[:layout])
|
||||
end
|
||||
|
||||
function layout_args(d::KW, n_override::Integer)
|
||||
layout, n = layout_args(get(d, :layout, n_override))
|
||||
function layout_args(plotattributes::AKW, n_override::Integer)
|
||||
layout, n = layout_args(n_override, get(plotattributes, :layout, n_override))
|
||||
if n != n_override
|
||||
error("When doing layout, n ($n) != n_override ($(n_override)). You're probably trying to force existing plots into a layout that doesn't fit them.")
|
||||
end
|
||||
@@ -481,12 +481,27 @@ function layout_args(n::Integer)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args(sztup::NTuple{2,I}) where I<:Integer
|
||||
function layout_args(sztup::NTuple{2, Integer})
|
||||
nr, nc = sztup
|
||||
GridLayout(nr, nc), nr*nc
|
||||
end
|
||||
|
||||
function layout_args(sztup::NTuple{3,I}) where I<:Integer
|
||||
layout_args(n_override::Integer, n::Integer) = layout_args(n)
|
||||
layout_args(n, sztup::NTuple{2, Integer}) = layout_args(sztup)
|
||||
|
||||
function layout_args(n, sztup::Tuple{Colon, Integer})
|
||||
nc = sztup[2]
|
||||
nr = ceil(Int, n / nc)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args(n, sztup::Tuple{Integer, Colon})
|
||||
nr = sztup[1]
|
||||
nc = ceil(Int, n / nr)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args(sztup::NTuple{3, Integer})
|
||||
n, nr, nc = sztup
|
||||
nr, nc = compute_gridsize(n, nr, nc)
|
||||
GridLayout(nr, nc), n
|
||||
@@ -499,6 +514,8 @@ function layout_args(layout::GridLayout)
|
||||
layout, n
|
||||
end
|
||||
|
||||
layout_args(n_override::Integer, layout::GridLayout) = layout_args(layout)
|
||||
|
||||
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
|
||||
|
||||
|
||||
@@ -566,7 +583,7 @@ function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
|
||||
for r=1:nr, c=1:nc
|
||||
l = layout[r,c]
|
||||
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
|
||||
plt = shift!(plts) # grab the first plot out of the list
|
||||
plt = popfirst!(plts) # grab the first plot out of the list
|
||||
layout[r,c] = plt.layout
|
||||
append!(subplots, plt.subplots)
|
||||
merge!(spmap, plt.spmap)
|
||||
@@ -599,7 +616,7 @@ end
|
||||
# ----------------------------------------------------------------------
|
||||
# @layout macro
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
function add_layout_pct!(kw::AKW, v::Expr, idx::Integer, nidx::Integer)
|
||||
# dump(v)
|
||||
# something like {0.2w}?
|
||||
if v.head == :call && v.args[1] == :*
|
||||
@@ -620,7 +637,7 @@ function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
error("Couldn't match layout curly (idx=$idx): $v")
|
||||
end
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Number, idx::Integer)
|
||||
function add_layout_pct!(kw::AKW, v::Number, idx::Integer)
|
||||
# kw[idx == 1 ? :w : :h] = v*pct
|
||||
idx == 1 && (kw[:w] = v*pct)
|
||||
(idx == 2 || nidx == 1) && (kw[:h] = v*pct)
|
||||
|
||||
+73
-152
@@ -52,16 +52,29 @@ function tex(plt::Plot, fn::AbstractString)
|
||||
end
|
||||
tex(fn::AbstractString) = tex(current(), fn)
|
||||
|
||||
function json(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "json")
|
||||
io = open(fn, "w")
|
||||
show(io, MIME("application/vnd.plotly.v1+json"), plt)
|
||||
close(io)
|
||||
end
|
||||
json(fn::AbstractString) = json(current(), fn)
|
||||
|
||||
function html(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "html")
|
||||
io = open(fn, "w")
|
||||
_use_remote[] = true
|
||||
show(io, MIME("text/html"), plt)
|
||||
_use_remote[] = false
|
||||
close(io)
|
||||
end
|
||||
html(fn::AbstractString) = html(current(), fn)
|
||||
|
||||
function txt(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "txt")
|
||||
io = open(fn, "w")
|
||||
show(io, MIME("text/plain"), plt)
|
||||
close(io)
|
||||
end
|
||||
txt(fn::AbstractString) = txt(current(), fn)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -73,7 +86,10 @@ const _savemap = Dict(
|
||||
"ps" => ps,
|
||||
"eps" => eps,
|
||||
"tex" => tex,
|
||||
"json" => json,
|
||||
"html" => html,
|
||||
"tikz" => tex,
|
||||
"txt" => txt,
|
||||
)
|
||||
|
||||
function getExtension(fn::AbstractString)
|
||||
@@ -105,7 +121,7 @@ type is inferred from the file extension. All backends support png and pdf
|
||||
file types, some also support svg, ps, eps, html and tex.
|
||||
"""
|
||||
function savefig(plt::Plot, fn::AbstractString)
|
||||
|
||||
fn = abspath(expanduser(fn))
|
||||
# get the extension
|
||||
local ext
|
||||
try
|
||||
@@ -145,10 +161,6 @@ function Base.display(::PlotsDisplay, plt::Plot)
|
||||
_display(plt)
|
||||
end
|
||||
|
||||
# override the REPL display to open a gui window
|
||||
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
|
||||
|
||||
|
||||
_do_plot_show(plt, showval::Bool) = showval && gui(plt)
|
||||
function _do_plot_show(plt, showval::Symbol)
|
||||
showval == :gui && gui(plt)
|
||||
@@ -157,87 +169,65 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _mimeformats = Dict(
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg",
|
||||
"text/plain" => "txt",
|
||||
"application/x-tex" => "tex",
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
:pyplot => :png,
|
||||
:unicodeplots => :txt,
|
||||
:glvisualize => :png,
|
||||
:plotlyjs => :html,
|
||||
:plotly => :html
|
||||
)
|
||||
|
||||
# a backup for html... passes to svg or png depending on the html_output_format arg
|
||||
function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
if output_type == :png
|
||||
# info("writing png to html output")
|
||||
# @info("writing png to html output")
|
||||
print(io, "<img src=\"data:image/png;base64,", base64encode(show, MIME("image/png"), plt), "\" />")
|
||||
elseif output_type == :svg
|
||||
# info("writing svg to html output")
|
||||
# @info("writing svg to html output")
|
||||
show(io, MIME("image/svg+xml"), plt)
|
||||
elseif output_type == :txt
|
||||
show(io, MIME("text/plain"), plt)
|
||||
else
|
||||
error("only png or svg allowed. got: $output_type")
|
||||
error("only png or svg allowed. got: $(repr(output_type))")
|
||||
end
|
||||
end
|
||||
|
||||
function _show(io::IO, m, plt::Plot{B}) where B
|
||||
# Base.show_backtrace(STDOUT, backtrace())
|
||||
warn("_show is not defined for this backend. m=", string(m))
|
||||
# delegate showable to _show instead
|
||||
function Base.showable(m::M, plt::P) where {M<:MIME, P<:Plot}
|
||||
return hasmethod(_show, Tuple{IO, M, P})
|
||||
end
|
||||
|
||||
function _display(plt::Plot)
|
||||
warn("_display is not defined for this backend.")
|
||||
@warn("_display is not defined for this backend.")
|
||||
end
|
||||
|
||||
# for writing to io streams... first prepare, then callback
|
||||
for mime in keys(_mimeformats)
|
||||
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot{B}) where B
|
||||
prepare_output(plt)
|
||||
_show(io, m, plt)
|
||||
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
|
||||
"application/eps", "application/pdf", "application/postscript",
|
||||
"application/x-tex", "application/vnd.plotly.v1+json")
|
||||
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
||||
if haskey(io, :juno_plotsize)
|
||||
showjuno(io, m, plt)
|
||||
else
|
||||
prepare_output(plt)
|
||||
_show(io, m, plt)
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
end
|
||||
|
||||
Base.show(io::IO, m::MIME"application/prs.juno.plotpane+html", plt::Plot) = showjuno(io, MIME("text/html"), plt)
|
||||
|
||||
# default text/plain for all backends
|
||||
_show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
|
||||
|
||||
"Close all open gui windows of the current backend"
|
||||
closeall() = closeall(backend())
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
|
||||
if is_installed("FileIO")
|
||||
@eval import FileIO
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot)
|
||||
fn = tempname()
|
||||
|
||||
# first save a pdf file
|
||||
pdf(plt, fn)
|
||||
|
||||
# load that pdf into a FileIO Stream
|
||||
s = FileIO.load(fn * ".pdf")
|
||||
|
||||
# save a png
|
||||
pngfn = fn * ".png"
|
||||
FileIO.save(pngfn, s)
|
||||
|
||||
# now write from the file
|
||||
write(io, readstring(open(pngfn)))
|
||||
end
|
||||
end
|
||||
|
||||
# function html_output_format(fmt)
|
||||
# if fmt == "png"
|
||||
# @eval function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
@@ -254,110 +244,41 @@ end
|
||||
#
|
||||
# html_output_format("svg")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@require IJulia begin
|
||||
if IJulia.inited
|
||||
|
||||
"""
|
||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||
|
||||
The default is nothing, except for plotly based backends, where it
|
||||
adds data for `application/vnd.plotly.v1+json` that is used in
|
||||
frontends like jupyterlab and nteract.
|
||||
"""
|
||||
_extra_mime_info!(plt::Plot, out::Dict) = out
|
||||
function _extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
|
||||
out
|
||||
end
|
||||
|
||||
function _extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out
|
||||
end
|
||||
|
||||
function IJulia.display_dict(plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
out = Dict()
|
||||
if output_type == :png
|
||||
mime = "image/png"
|
||||
out[mime] = base64encode(show, MIME(mime), plt)
|
||||
elseif output_type == :svg
|
||||
mime = "image/svg+xml"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
elseif output_type == :html
|
||||
mime = "text/html"
|
||||
out[mime] = sprint(show, MIME(mime), plt)
|
||||
else
|
||||
error("Unsupported output type $output_type")
|
||||
end
|
||||
_extra_mime_info!(plt, out)
|
||||
out
|
||||
end
|
||||
|
||||
# default text/plain passes to html... handles Interact issues
|
||||
function Base.show(io::IO, m::MIME"text/plain", plt::Plot)
|
||||
show(io, MIME("text/html"), plt)
|
||||
end
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Atom PlotPane
|
||||
# ---------------------------------------------------------
|
||||
@require Juno begin
|
||||
import Hiccup, Media
|
||||
function showjuno(io::IO, m, plt)
|
||||
sz = collect(plt[:size])
|
||||
dpi = plt[:dpi]
|
||||
thickness_scaling = plt[:thickness_scaling]
|
||||
|
||||
if Juno.isactive()
|
||||
Media.media(Plot, Media.Plot)
|
||||
jsize = get(io, :juno_plotsize, [400, 500])
|
||||
jratio = get(io, :juno_dpi_ratio, 1)
|
||||
|
||||
scale = minimum(jsize[i] / sz[i] for i in 1:2)
|
||||
plt[:size] = [s * scale for s in sz]
|
||||
plt[:dpi] = jratio*Plots.DPI
|
||||
plt[:thickness_scaling] *= scale
|
||||
|
||||
_show(io::IO, m::MIME"text/plain", plt::Plot{B}) where {B} = print(io, "Plot{$B}()")
|
||||
|
||||
function Juno.render(e::Juno.Editor, plt::Plot)
|
||||
Juno.render(e, nothing)
|
||||
end
|
||||
|
||||
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot)
|
||||
# temporarily overwrite size to be Atom.plotsize
|
||||
sz = plt[:size]
|
||||
jsize = Juno.plotsize()
|
||||
jsize[1] == 0 && (jsize[1] = 400)
|
||||
jsize[2] == 0 && (jsize[2] = 500)
|
||||
|
||||
plt[:size] = jsize
|
||||
Juno.render(pane, HTML(stringmime(MIME("text/html"), plt)))
|
||||
plt[:size] = sz
|
||||
end
|
||||
# special handling for PlotlyJS
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
end
|
||||
else
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot)
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
s = "PlotPane turned off. Unset ENV[\"PLOTS_USE_ATOM_PLOTPANE\"] and restart Julia to enable it."
|
||||
Juno.render(pane, HTML(s))
|
||||
end
|
||||
end
|
||||
|
||||
# special handling for plotly... use PlotsDisplay
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot{PlotlyBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
s = "PlotPane turned off. The plotly backend cannot render in the PlotPane due to javascript issues. Plotlyjs is similar to plotly and is compatible with the plot pane."
|
||||
Juno.render(pane, HTML(s))
|
||||
end
|
||||
prepare_output(plt)
|
||||
try
|
||||
_showjuno(io, m, plt)
|
||||
finally
|
||||
plt[:size] = sz
|
||||
plt[:dpi] = dpi
|
||||
plt[:thickness_scaling] = thickness_scaling
|
||||
end
|
||||
end
|
||||
|
||||
function _showjuno(io::IO, m::MIME"image/svg+xml", plt)
|
||||
if Symbol(plt.attr[:html_output_format]) ≠ :svg
|
||||
throw(MethodError(show, (typeof(m), typeof(plt))))
|
||||
else
|
||||
_show(io, m, plt)
|
||||
end
|
||||
end
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot) = false
|
||||
|
||||
_showjuno(io::IO, m, plt) = _show(io, m, plt)
|
||||
|
||||
+118
-87
@@ -1,49 +1,71 @@
|
||||
# Error for aliases used in recipes
|
||||
function warn_on_recipe_aliases!(plotattributes, recipe_type, args...)
|
||||
for k in keys(plotattributes)
|
||||
if !is_default_attribute(k)
|
||||
dk = get(_keyAliases, k, k)
|
||||
if k !== dk
|
||||
@warn "Attribute alias `$k` detected in the $recipe_type recipe defined for the signature $(signature_string(Val{recipe_type}, args...)). To ensure expected behavior it is recommended to use the default attribute `$dk`."
|
||||
end
|
||||
plotattributes[dk] = pop_kw!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
function warn_on_recipe_aliases!(v::AbstractVector, recipe_type, args...)
|
||||
foreach(x -> warn_on_recipe_aliases!(x, recipe_type, args...), v)
|
||||
end
|
||||
function warn_on_recipe_aliases!(rd::RecipeData, recipe_type, args...)
|
||||
warn_on_recipe_aliases!(rd.plotattributes, recipe_type, args...)
|
||||
end
|
||||
|
||||
function signature_string(::Type{Val{:user}}, args...)
|
||||
return string("(::", join(string.(typeof.(args)), ", ::"), ")")
|
||||
end
|
||||
signature_string(::Type{Val{:type}}, T) = "(::Type{$T}, ::$T)"
|
||||
signature_string(::Type{Val{:plot}}, st) = "(::Type{Val{:$st}}, ::AbstractPlot)"
|
||||
signature_string(::Type{Val{:series}}, st) = "(::Type{Val{:$st}}, x, y, z)"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# preprocessing
|
||||
|
||||
function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
function series_idx(kw_list::AVec{KW}, kw::AKW)
|
||||
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
|
||||
end
|
||||
|
||||
function _expand_seriestype_array(d::KW, args)
|
||||
sts = get(d, :seriestype, :path)
|
||||
function _expand_seriestype_array(plotattributes::AKW, args)
|
||||
sts = get(plotattributes, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(d, :seriestype)
|
||||
RecipeData[begin
|
||||
dc = copy(d)
|
||||
reset_kw!(plotattributes, :seriestype)
|
||||
rd = Vector{RecipeData}(undef, size(sts, 1))
|
||||
for r in axes(sts, 1)
|
||||
dc = copy(plotattributes)
|
||||
dc[:seriestype] = sts[r:r,:]
|
||||
RecipeData(dc, args)
|
||||
end for r=1:size(sts,1)]
|
||||
rd[r] = RecipeData(dc, args)
|
||||
end
|
||||
rd
|
||||
else
|
||||
RecipeData[RecipeData(copy(d), args)]
|
||||
RecipeData[RecipeData(copy(plotattributes), args)]
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(d::KW, args, still_to_process::Vector{RecipeData})
|
||||
function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{RecipeData})
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
if haskey(d, :group)
|
||||
args = (extractGroupArgs(d[:group], args...), args...)
|
||||
if haskey(plotattributes, :group)
|
||||
args = (extractGroupArgs(plotattributes[:group], args...), args...)
|
||||
end
|
||||
|
||||
# if we were passed a vector/matrix of seriestypes and there's more than one row,
|
||||
# we want to duplicate the inputs, once for each seriestype row.
|
||||
if !isempty(args)
|
||||
append!(still_to_process, _expand_seriestype_array(d, args))
|
||||
append!(still_to_process, _expand_seriestype_array(plotattributes, args))
|
||||
end
|
||||
|
||||
# remove subplot and axis args from d... they will be passed through in the kw_list
|
||||
# remove subplot and axis args from plotattributes... they will be passed through in the kw_list
|
||||
if !isempty(args)
|
||||
for (k,v) in d
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
delete!(d, k)
|
||||
end
|
||||
for (k,v) in plotattributes
|
||||
if k in _all_subplot_args || k in _all_axis_args
|
||||
reset_kw!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -55,9 +77,9 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(d, args, still_to_process)
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a stack of series that still need to be processed.
|
||||
@@ -70,7 +92,7 @@ function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
# grab the first in line to be processed and either add it to the kw_list or
|
||||
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes)
|
||||
# for further processing.
|
||||
next_series = shift!(still_to_process)
|
||||
next_series = popfirst!(still_to_process)
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(next_series) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
|
||||
@@ -78,20 +100,24 @@ function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
if isempty(next_series.args)
|
||||
_process_userrecipe(plt, kw_list, next_series)
|
||||
else
|
||||
rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
|
||||
rd_list = RecipesBase.apply_recipe(
|
||||
next_series.plotattributes,
|
||||
next_series.args...
|
||||
)
|
||||
warn_on_recipe_aliases!(rd_list, :user, next_series.args...)
|
||||
prepend!(still_to_process,rd_list)
|
||||
end
|
||||
end
|
||||
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
plotattributes[:smooth] = false
|
||||
kw_list
|
||||
end
|
||||
|
||||
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
kw = recipedata.d
|
||||
kw = recipedata.plotattributes
|
||||
preprocessArgs!(kw)
|
||||
_preprocess_userrecipe(kw)
|
||||
warnOnUnsupported_scales(plt.backend, kw)
|
||||
@@ -106,7 +132,7 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD
|
||||
return
|
||||
end
|
||||
|
||||
function _preprocess_userrecipe(kw::KW)
|
||||
function _preprocess_userrecipe(kw::AKW)
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
@@ -115,26 +141,26 @@ function _preprocess_userrecipe(kw::KW)
|
||||
# map marker_z if it's a Function
|
||||
if isa(get(kw, :marker_z, nothing), Function)
|
||||
# TODO: should this take y and/or z as arguments?
|
||||
kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) : map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# map line_z if it's a Function
|
||||
if isa(get(kw, :line_z, nothing), Function)
|
||||
kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) : map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# convert a ribbon into a fillrange
|
||||
if get(kw, :ribbon, nothing) != nothing
|
||||
if get(kw, :ribbon, nothing) !== nothing
|
||||
make_fillrange_from_ribbon(kw)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(kw, esym, nothing) != nothing
|
||||
for esym in (:xerror, :yerror, :zerror)
|
||||
if get(kw, esym, nothing) !== nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
errkw = copy(kw)
|
||||
errkw[:seriestype] = esym
|
||||
@@ -145,13 +171,13 @@ function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
|
||||
# handle smoothing by adding a new series
|
||||
if get(kw, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
sx = [ignorenan_minimum(x), ignorenan_maximum(x)]
|
||||
sy = β * sx + α
|
||||
sy = β .* sx .+ α
|
||||
push!(kw_list, merge(copy(kw), KW(
|
||||
:seriestype => :path,
|
||||
:x => sx,
|
||||
@@ -170,7 +196,7 @@ end
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
if !isa(get(kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, kw)
|
||||
@@ -180,12 +206,13 @@ function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_pr
|
||||
st = kw[:seriestype]
|
||||
st = kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
||||
warn_on_recipe_aliases!(datalist, :plot, st)
|
||||
for data in datalist
|
||||
preprocessArgs!(data.d)
|
||||
if data.d[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.d)")
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
|
||||
end
|
||||
push!(still_to_process, data.d)
|
||||
push!(still_to_process, data.plotattributes)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
@@ -201,14 +228,14 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
function _plot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (d[k] = pop!(kw, k))
|
||||
haskey(_plot_defaults, k) && (plotattributes[k] = pop!(kw, k))
|
||||
end
|
||||
|
||||
# TODO: init subplots here
|
||||
_update_plot_args(plt, d)
|
||||
_update_plot_args(plt, plotattributes)
|
||||
if !plt.init
|
||||
plt.o = Base.invokelatest(_create_backend_figure, plt)
|
||||
|
||||
@@ -225,7 +252,7 @@ function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
|
||||
# handle inset subplots
|
||||
insets = plt[:inset_subplots]
|
||||
if insets != nothing
|
||||
if insets !== nothing
|
||||
if !(typeof(insets) <: AVec)
|
||||
insets = [insets]
|
||||
end
|
||||
@@ -250,7 +277,7 @@ function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
plt[:inset_subplots] = nothing
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
@@ -259,16 +286,16 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
for kw in kw_list
|
||||
# get the Subplot object to which the series belongs.
|
||||
sps = get(kw, :subplot, :auto)
|
||||
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
|
||||
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], series_idx(kw_list,kw)))
|
||||
kw[:subplot] = sp
|
||||
|
||||
# extract subplot/axis attributes from kw and add to sp_attr
|
||||
attr = KW()
|
||||
for (k,v) in collect(kw)
|
||||
if haskey(_subplot_defaults, k) || haskey(_axis_defaults_byletter, k)
|
||||
if is_subplot_attr(k) || is_axis_attr(k)
|
||||
attr[k] = pop!(kw, k)
|
||||
end
|
||||
if haskey(_axis_defaults, k)
|
||||
if is_axis_attr_noletter(k)
|
||||
v = pop!(kw, k)
|
||||
for letter in (:x,:y,:z)
|
||||
attr[Symbol(letter,k)] = v
|
||||
@@ -287,8 +314,8 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
|
||||
# override subplot/axis args. `sp_attrs` take precendence
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
attr = if !haskey(d, :subplot) || d[:subplot] == idx
|
||||
merge(d, get(sp_attrs, sp, KW()))
|
||||
attr = if !haskey(plotattributes, :subplot) || plotattributes[:subplot] == idx
|
||||
merge(plotattributes, get(sp_attrs, sp, KW()))
|
||||
else
|
||||
get(sp_attrs, sp, KW())
|
||||
end
|
||||
@@ -301,13 +328,13 @@ end
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
function _prepare_subplot(plt::Plot{T}, d::KW) where T
|
||||
st::Symbol = d[:seriestype]
|
||||
sp::Subplot{T} = d[:subplot]
|
||||
function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where T
|
||||
st::Symbol = plotattributes[:seriestype]
|
||||
sp::Subplot{T} = plotattributes[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
_update_subplot_args(plt, sp, d, sp_idx, true)
|
||||
_update_subplot_args(plt, sp, plotattributes, sp_idx, true)
|
||||
|
||||
st = _override_seriestype_check(d, st)
|
||||
st = _override_seriestype_check(plotattributes, st)
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if is3d(st)
|
||||
@@ -325,28 +352,28 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# series types
|
||||
|
||||
function _override_seriestype_check(d::KW, st::Symbol)
|
||||
function _override_seriestype_check(plotattributes::AKW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st)
|
||||
z = d[:z]
|
||||
if !isa(z, Void) && (size(d[:x]) == size(d[:y]) == size(z))
|
||||
if !is3d(st) && !(st in (:contour,:contour3d))
|
||||
z = plotattributes[:z]
|
||||
if !isa(z, Nothing) && (size(plotattributes[:x]) == size(plotattributes[:y]) == size(z))
|
||||
st = (st == :scatter ? :scatter3d : :path3d)
|
||||
d[:seriestype] = st
|
||||
plotattributes[:seriestype] = st
|
||||
end
|
||||
end
|
||||
st
|
||||
end
|
||||
|
||||
function _prepare_annotations(sp::Subplot, d::KW)
|
||||
function _prepare_annotations(sp::Subplot, plotattributes::AKW)
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
# series_anns = annotations(pop!(d, :series_annotations, []))
|
||||
# series_anns = annotations(pop!(plotattributes, :series_annotations, []))
|
||||
# if isa(series_anns, SeriesAnnotations)
|
||||
# series_anns.x = d[:x]
|
||||
# series_anns.y = d[:y]
|
||||
# series_anns.x = plotattributes[:x]
|
||||
# series_anns.y = plotattributes[:y]
|
||||
# elseif length(series_anns) > 0
|
||||
# x, y = d[:x], d[:y]
|
||||
# x, y = plotattributes[:x], plotattributes[:y]
|
||||
# nx, ny, na = map(length, (x,y,series_anns))
|
||||
# n = max(nx, ny, na)
|
||||
# series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n]
|
||||
@@ -354,14 +381,14 @@ function _prepare_annotations(sp::Subplot, d::KW)
|
||||
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
|
||||
end
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol)
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
xmin, xmax = ignorenan_extrema(d[:x]); ymin, ymax = ignorenan_extrema(d[:y])
|
||||
xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y])
|
||||
expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
expand_extrema!(sp, plotattributes)
|
||||
end
|
||||
# expand for zerolines (axes through origin)
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
@@ -370,10 +397,10 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_the_series(plt, sp, d)
|
||||
warnOnUnsupported_args(plt.backend, d)
|
||||
warnOnUnsupported(plt.backend, d)
|
||||
series = Series(d)
|
||||
function _add_the_series(plt, sp, plotattributes)
|
||||
warnOnUnsupported_args(plt.backend, plotattributes)
|
||||
warnOnUnsupported(plt.backend, plotattributes)
|
||||
series = Series(plotattributes)
|
||||
push!(plt.series_list, series)
|
||||
push!(sp.series_list, series)
|
||||
_series_added(plt, series)
|
||||
@@ -383,37 +410,41 @@ end
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _process_seriesrecipe(plt::Plot, d::KW)
|
||||
function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
|
||||
#println("process $(typeof(plotattributes))")
|
||||
# replace seriestype aliases
|
||||
st = Symbol(d[:seriestype])
|
||||
st = d[:seriestype] = get(_typeAliases, st, st)
|
||||
st = Symbol(plotattributes[:seriestype])
|
||||
st = plotattributes[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# shapes shouldn't have fillrange set
|
||||
if d[:seriestype] == :shape
|
||||
d[:fillrange] = nothing
|
||||
if plotattributes[:seriestype] == :shape
|
||||
plotattributes[:fillrange] = nothing
|
||||
end
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if is_seriestype_supported(st)
|
||||
sp = _prepare_subplot(plt, d)
|
||||
_prepare_annotations(sp, d)
|
||||
_expand_subplot_extrema(sp, d, st)
|
||||
_add_the_series(plt, sp, d)
|
||||
sp = _prepare_subplot(plt, plotattributes)
|
||||
_prepare_annotations(sp, plotattributes)
|
||||
_expand_subplot_extrema(sp, plotattributes, st)
|
||||
_update_series_attributes!(plotattributes, plt, sp)
|
||||
_add_the_series(plt, sp, plotattributes)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
x, y, z = plotattributes[:x], plotattributes[:y], plotattributes[:z]
|
||||
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, x, y, z)
|
||||
warn_on_recipe_aliases!(datalist, :series, st)
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
preprocessArgs!(data.d)
|
||||
if data.d[:seriestype] == st
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
|
||||
end
|
||||
_process_seriesrecipe(plt, data.d)
|
||||
_process_seriesrecipe(plt, data.plotattributes)
|
||||
else
|
||||
warn("Unhandled recipe: $(data)")
|
||||
@warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
+45
-47
@@ -1,10 +1,10 @@
|
||||
|
||||
mutable struct CurrentPlot
|
||||
nullableplot::Nullable{AbstractPlot}
|
||||
nullableplot::Union{AbstractPlot, Nothing}
|
||||
end
|
||||
const CURRENT_PLOT = CurrentPlot(Nullable{AbstractPlot}())
|
||||
const CURRENT_PLOT = CurrentPlot(nothing)
|
||||
|
||||
isplotnull() = isnull(CURRENT_PLOT.nullableplot)
|
||||
isplotnull() = CURRENT_PLOT.nullableplot === nothing
|
||||
|
||||
"""
|
||||
current()
|
||||
@@ -14,9 +14,9 @@ function current()
|
||||
if isplotnull()
|
||||
error("No current plot/subplot")
|
||||
end
|
||||
get(CURRENT_PLOT.nullableplot)
|
||||
CURRENT_PLOT.nullableplot
|
||||
end
|
||||
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = Nullable(plot))
|
||||
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = plot)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -46,34 +46,33 @@ When you pass in matrices, it splits by columns. To see the list of available at
|
||||
function, where `attr` is the symbol `:Series:`, `:Subplot:`, `:Plot` or `:Axis`. Pass any attribute to `plotattr`
|
||||
as a String to look up its docstring; e.g. `plotattr("seriestype")`.
|
||||
"""
|
||||
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
function plot(args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = d
|
||||
_plot!(plt, d, args)
|
||||
# plt.user_attr = plotattributes
|
||||
_plot!(plt, plotattributes, args)
|
||||
end
|
||||
|
||||
# build a new plot from existing plots
|
||||
# note: we split into plt1 and plts_tail so we can dispatch correctly
|
||||
function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
|
||||
# build our plot vector from the args
|
||||
n = length(plts_tail) + 1
|
||||
plts = Array{Plot}(n)
|
||||
plts = Array{Plot}(undef, n)
|
||||
plts[1] = plt1
|
||||
for (i,plt) in enumerate(plts_tail)
|
||||
plts[i+1] = plt
|
||||
end
|
||||
|
||||
# compute the layout
|
||||
layout = layout_args(d, n)[1]
|
||||
layout = layout_args(plotattributes, n)[1]
|
||||
num_sp = sum([length(p.subplots) for p in plts])
|
||||
|
||||
# create a new plot object, with subplot list/map made of existing subplots.
|
||||
@@ -84,21 +83,21 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
# TODO: build the user_attr dict by creating "Any matrices" for the args of each subplot
|
||||
|
||||
# TODO: replace this with proper processing from a merged user_attr KW
|
||||
# update plot args, first with existing plots, then override with d
|
||||
# update plot args, first with existing plots, then override with plotattributes
|
||||
for p in plts
|
||||
_update_plot_args(plt, copy(p.attr))
|
||||
plt.n += p.n
|
||||
end
|
||||
_update_plot_args(plt, d)
|
||||
_update_plot_args(plt, plotattributes)
|
||||
|
||||
# pass new plot to the backend
|
||||
plt.o = _create_backend_figure(plt)
|
||||
plt.init = true
|
||||
|
||||
series_attr = KW()
|
||||
for (k,v) in d
|
||||
if haskey(_series_defaults, k)
|
||||
series_attr[k] = pop!(d,k)
|
||||
for (k,v) in plotattributes
|
||||
if is_series_attr(k)
|
||||
series_attr[k] = pop!(plotattributes,k)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -119,8 +118,8 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
sp.plt = plt
|
||||
sp.attr[:subplot_index] = idx
|
||||
for series in serieslist
|
||||
merge!(series.d, series_attr)
|
||||
_add_defaults!(series.d, plt, sp, cmdidx)
|
||||
merge!(series.plotattributes, series_attr)
|
||||
_slice_series_args!(series.plotattributes, plt, sp, cmdidx)
|
||||
push!(plt.series_list, series)
|
||||
_series_added(plt, series)
|
||||
cmdidx += 1
|
||||
@@ -129,12 +128,12 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
|
||||
# first apply any args for the subplots
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
_update_subplot_args(plt, sp, d, idx, false)
|
||||
_update_subplot_args(plt, sp, plotattributes, idx, false)
|
||||
end
|
||||
|
||||
# finish up
|
||||
current(plt)
|
||||
_do_plot_show(plt, get(d, :show, default(:show)))
|
||||
_do_plot_show(plt, get(plotattributes, :show, default(:show)))
|
||||
plt
|
||||
end
|
||||
|
||||
@@ -153,10 +152,10 @@ end
|
||||
|
||||
# this adds to a specific plot... most plot commands will flow through here
|
||||
function plot!(plt::Plot, args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
# merge!(plt.user_attr, d)
|
||||
_plot!(plt, d, args)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
# merge!(plt.user_attr, plotattributes)
|
||||
_plot!(plt, plotattributes, args)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
@@ -164,21 +163,21 @@ end
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
d[:plot_object] = plt
|
||||
function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
|
||||
plotattributes[:plot_object] = plt
|
||||
|
||||
if !isempty(args) && !isdefined(Main, :StatPlots) &&
|
||||
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
|
||||
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
||||
warn("You're trying to plot a DataFrame, but this functionality is provided by StatPlots")
|
||||
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
kw_list = _process_userrecipes(plt, d, args)
|
||||
kw_list = _process_userrecipes(plt, plotattributes, args)
|
||||
|
||||
# info(1)
|
||||
# @info(1)
|
||||
# map(DD, kw_list)
|
||||
|
||||
|
||||
@@ -193,18 +192,18 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
next_kw = shift!(still_to_process)
|
||||
next_kw = popfirst!(still_to_process)
|
||||
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
|
||||
end
|
||||
|
||||
# info(2)
|
||||
# @info(2)
|
||||
# map(DD, kw_list)
|
||||
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
_plot_setup(plt, d, kw_list)
|
||||
_subplot_setup(plt, d, kw_list)
|
||||
_plot_setup(plt, plotattributes, kw_list)
|
||||
_subplot_setup(plt, plotattributes, kw_list)
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
@@ -213,26 +212,25 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
# "SERIES RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# info(3)
|
||||
# @info(3)
|
||||
# map(DD, kw_list)
|
||||
|
||||
for kw in kw_list
|
||||
sp::Subplot = kw[:subplot]
|
||||
# idx = get_subplot_index(plt, sp)
|
||||
|
||||
# # we update subplot args in case something like the color palatte is part of the recipe
|
||||
# _update_subplot_args(plt, sp, kw, idx, true)
|
||||
# in series attributes given as vector with one element per series,
|
||||
# select the value for current series
|
||||
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
|
||||
|
||||
# set default values, select from attribute cycles, and generally set the final attributes
|
||||
_add_defaults!(kw, plt, sp, command_idx(kw_list,kw))
|
||||
|
||||
series_attr = Attr(kw, _series_defaults)
|
||||
# now we have a fully specified series, with colors chosen. we must recursively handle
|
||||
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
|
||||
# we check for a recipe that will convert that series type into one made up of lower-level components.
|
||||
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
|
||||
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
|
||||
# be able to support step, bar, and histogram plots (and any recipes that use those components).
|
||||
_process_seriesrecipe(plt, kw)
|
||||
_process_seriesrecipe(plt, series_attr)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
@@ -284,11 +282,11 @@ end
|
||||
|
||||
function plot(sp::Subplot, args...; kw...)
|
||||
plt = sp.plt
|
||||
plot(plt, args...; kw..., subplot = findfirst(plt.subplots, sp))
|
||||
plot(plt, args...; kw..., subplot = findfirst(isequal(sp), plt.subplots))
|
||||
end
|
||||
function plot!(sp::Subplot, args...; kw...)
|
||||
plt = sp.plt
|
||||
plot!(plt, args...; kw..., subplot = findfirst(plt.subplots, sp))
|
||||
plot!(plt, args...; kw..., subplot = findfirst(isequal(sp), plt.subplots))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
+17
-9
@@ -18,7 +18,7 @@ end
|
||||
plotattr([attr])
|
||||
|
||||
Look up the properties of a Plots attribute, or specify an attribute type. Call `plotattr()` for options.
|
||||
The information is the same as that given on https://juliaplots.github.io/attributes/.
|
||||
The information is the same as that given on https://docs.juliaplots.org/latest/attributes/.
|
||||
"""
|
||||
function plotattr()
|
||||
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
|
||||
@@ -40,23 +40,31 @@ function plotattr(attribute::AbstractString)
|
||||
error("There is no attribute named $attribute")
|
||||
end
|
||||
|
||||
printnothing(x) = x
|
||||
printnothing(x::Nothing) = "nothing"
|
||||
|
||||
function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
|
||||
|
||||
attribute = Symbol(lookup_aliases(attrtype, attribute))
|
||||
|
||||
desc = get(_arg_desc, attribute, "")
|
||||
first_period_idx = findfirst(desc, '.')
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
als = keys(filter((_,v)->v==attribute, _keyAliases)) |> collect |> sort
|
||||
first_period_idx = findfirst(isequal('.'), desc)
|
||||
if isnothing(first_period_idx)
|
||||
typedesc = ""
|
||||
desc = strip(desc)
|
||||
else
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
end
|
||||
als = keys(filter(x->x[2]==attribute, _keyAliases)) |> collect |> sort
|
||||
als = join(map(string,als), ", ")
|
||||
def = _attribute_defaults[attrtype][attribute]
|
||||
|
||||
|
||||
# Looks up the different elements and plots them
|
||||
println("$attribute ", typedesc == "" ? "" : "{$typedesc}", "\n",
|
||||
als == "" ? "" : "$als\n",
|
||||
"\n$desc\n",
|
||||
"$(attrtype) attribute, ", def == "" ? "" : " default: $def")
|
||||
println("$(printnothing(attribute)) ", typedesc == "" ? "" : "{$(printnothing(typedesc))}", "\n",
|
||||
als == "" ? "" : "$(printnothing(als))\n",
|
||||
"\n$(printnothing(desc))\n",
|
||||
"$(printnothing(attrtype)) attribute, ", def == "" ? "" : " default: $(printnothing(def))")
|
||||
end
|
||||
|
||||
+1116
File diff suppressed because it is too large
Load Diff
+558
-228
File diff suppressed because it is too large
Load Diff
+297
-287
@@ -7,85 +7,85 @@
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
|
||||
const MaybeNumber = Union{Number, Missing}
|
||||
const MaybeString = Union{AbstractString, Missing}
|
||||
const DataPoint = Union{MaybeNumber, MaybeString}
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting")
|
||||
prepareSeriesData(::Nothing) = nothing
|
||||
prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t
|
||||
prepareSeriesData(f::Function) = f
|
||||
prepareSeriesData(ar::AbstractRange{<:Number}) = ar
|
||||
function prepareSeriesData(a::AbstractArray{<:MaybeNumber})
|
||||
f = isimmutable(a) ? replace : replace!
|
||||
a = f(x -> ismissing(x) || isinf(x) ? NaN : x, map(float, a))
|
||||
end
|
||||
prepareSeriesData(a::AbstractArray{<:Missing}) = fill(NaN, axes(a))
|
||||
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a)
|
||||
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf))
|
||||
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image
|
||||
prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents)
|
||||
|
||||
# unknown
|
||||
convertToAnyVector(x, d::KW) = error("No user recipe defined for $(typeof(x))")
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::Void, d::KW) = Any[nothing], nothing
|
||||
# default: assume x represents a single series
|
||||
series_vector(x, plotattributes) = [prepareSeriesData(x)]
|
||||
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
series_vector(n::Integer, plotattributes) = [zeros(0) for i in 1:n]
|
||||
|
||||
# numeric vector
|
||||
convertToAnyVector(v::AVec{T}, d::KW) where {T<:Number} = Any[v], nothing
|
||||
|
||||
# string vector
|
||||
convertToAnyVector(v::AVec{T}, d::KW) where {T<:AbstractString} = Any[v], nothing
|
||||
|
||||
function convertToAnyVector(v::AMat, d::KW)
|
||||
if all3D(d)
|
||||
Any[Surface(v)]
|
||||
else
|
||||
Any[v[:,i] for i in 1:size(v,2)]
|
||||
end, nothing
|
||||
end
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
|
||||
|
||||
# volume
|
||||
convertToAnyVector(v::Volume, d::KW) = Any[v], nothing
|
||||
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
|
||||
# # dates
|
||||
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||
# vector of data points is a single series
|
||||
series_vector(v::AVec{<:DataPoint}, plotattributes) = [prepareSeriesData(v)]
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
function convertToAnyVector(v::AVec, d::KW)
|
||||
if all(x -> typeof(x) <: Number, v)
|
||||
# all real numbers wrap the whole vector as one item
|
||||
Any[convert(Vector{Float64}, v)], nothing
|
||||
function series_vector(v::AVec, plotattributes)
|
||||
if all(x -> x isa MaybeNumber, v)
|
||||
series_vector(Vector{MaybeNumber}(v), plotattributes)
|
||||
elseif all(x -> x isa MaybeString, v)
|
||||
series_vector(Vector{MaybeString}(v), plotattributes)
|
||||
else
|
||||
# something else... treat each element as an item
|
||||
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
|
||||
# Any[vi for vi in v], nothing
|
||||
vcat((series_vector(vi, plotattributes) for vi in v)...)
|
||||
end
|
||||
end
|
||||
|
||||
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
|
||||
|
||||
|
||||
function convertToAnyVector(args...)
|
||||
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
|
||||
# Matrix is split into columns
|
||||
function series_vector(v::AMat{<:DataPoint}, plotattributes)
|
||||
if all3D(plotattributes)
|
||||
[prepareSeriesData(Surface(v))]
|
||||
else
|
||||
[prepareSeriesData(v[:, i]) for i in axes(v, 2)]
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Fillranges & ribbons
|
||||
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
# result in that array being shared. push!, etc will add too many items to that array
|
||||
|
||||
compute_x(x::Void, y::Void, z) = 1:size(z,1)
|
||||
compute_x(x::Void, y, z) = 1:size(y,1)
|
||||
compute_x(x::Function, y, z) = map(x, y)
|
||||
compute_x(x, y, z) = copy(x)
|
||||
process_fillrange(range::Number, plotattributes) = [range]
|
||||
process_fillrange(range, plotattributes) = series_vector(range, plotattributes)
|
||||
|
||||
# compute_y(x::Void, y::Function, z) = error()
|
||||
compute_y(x::Void, y::Void, z) = 1:size(z,2)
|
||||
compute_y(x, y::Function, z) = map(y, x)
|
||||
compute_y(x, y, z) = copy(y)
|
||||
process_ribbon(ribbon::Number, plotattributes) = [ribbon]
|
||||
process_ribbon(ribbon, plotattributes) = series_vector(ribbon, plotattributes)
|
||||
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
|
||||
process_ribbon(ribbon::Tuple{S, T}, plotattributes) where {S, T} = collect(zip(
|
||||
series_vector(ribbon[1], plotattributes),
|
||||
series_vector(ribbon[2], plotattributes),
|
||||
))
|
||||
|
||||
compute_z(x, y, z::Function) = map(z, x, y)
|
||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Void) = nothing
|
||||
compute_z(x, y, z) = copy(z)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
compute_x(x::Nothing, y::Nothing, z) = axes(z,1)
|
||||
compute_x(x::Nothing, y, z) = axes(y,1)
|
||||
compute_x(x::Function, y, z) = map(x, y)
|
||||
compute_x(x, y, z) = x
|
||||
|
||||
compute_y(x::Nothing, y::Nothing, z) = axes(z,2)
|
||||
compute_y(x, y::Function, z) = map(y, x)
|
||||
compute_y(x, y, z) = y
|
||||
|
||||
compute_z(x, y, z::Function) = map(z, x, y)
|
||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Nothing) = nothing
|
||||
compute_z(x, y, z) = z
|
||||
|
||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
||||
@@ -99,19 +99,25 @@ nobigs(v) = v
|
||||
end
|
||||
|
||||
# not allowed
|
||||
compute_xyz(x::Void, y::FuncOrFuncs{F}, z) where {F<:Function} = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Void, y::Void, z::FuncOrFuncs{F}) where {F<:Function} = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
compute_xyz(x::Nothing, y::FuncOrFuncs{F}, z) where {F<:Function} = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs{F}) where {F<:Function} = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
# we are going to build recipes to do the processing and splitting of the args
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# The catch-all SliceIt recipe
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# ensure we dispatch to the slicer
|
||||
struct SliceIt end
|
||||
|
||||
# the catch-all recipes
|
||||
# The `SliceIt` recipe finishes user and type recipe processing.
|
||||
# It splits processed data into individual series data, stores in copied `plotattributes`
|
||||
# for each series and returns no arguments.
|
||||
@recipe function f(::Type{SliceIt}, x, y, z)
|
||||
|
||||
# handle data with formatting attached
|
||||
@@ -128,28 +134,18 @@ struct SliceIt end
|
||||
z = z.data
|
||||
end
|
||||
|
||||
xs, _ = convertToAnyVector(x, plotattributes)
|
||||
ys, _ = convertToAnyVector(y, plotattributes)
|
||||
zs, _ = convertToAnyVector(z, plotattributes)
|
||||
xs = series_vector(x, plotattributes)
|
||||
ys = series_vector(y, plotattributes)
|
||||
zs = series_vector(z, plotattributes)
|
||||
|
||||
fr = pop!(plotattributes, :fillrange, nothing)
|
||||
fillranges, _ = if typeof(fr) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(fr, plotattributes)
|
||||
end
|
||||
fillranges = process_fillrange(fr, plotattributes)
|
||||
mf = length(fillranges)
|
||||
|
||||
rib = pop!(plotattributes, :ribbon, nothing)
|
||||
ribbons, _ = if typeof(rib) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(rib, plotattributes)
|
||||
end
|
||||
ribbons = process_ribbon(rib, plotattributes)
|
||||
mr = length(ribbons)
|
||||
|
||||
# @show zs
|
||||
|
||||
mx = length(xs)
|
||||
my = length(ys)
|
||||
mz = length(zs)
|
||||
@@ -174,55 +170,119 @@ struct SliceIt end
|
||||
nothing # don't add a series for the main block
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Apply type recipes
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# this is the default "type recipe"... just pass the object through
|
||||
@recipe f(::Type{T}, v::T) where {T<:Any} = v
|
||||
@recipe f(::Type{T}, v::T) where T = v
|
||||
|
||||
# this should catch unhandled "series recipes" and error with a nice message
|
||||
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
|
||||
|
||||
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
|
||||
function _apply_type_recipe(plotattributes, v, letter)
|
||||
_preprocess_axis_args!(plotattributes, letter)
|
||||
rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v)
|
||||
warn_on_recipe_aliases!(plotattributes, :type, typeof(v))
|
||||
_postprocess_axis_args!(plotattributes, letter)
|
||||
return rdvec[1].args[1]
|
||||
end
|
||||
|
||||
# Handle type recipes when the recipe is defined on the elements.
|
||||
# This sort of recipe should return a pair of functions... one to convert to number,
|
||||
# and one to format tick values.
|
||||
function _apply_type_recipe(d, v::AbstractArray)
|
||||
isempty(v) && return Float64[]
|
||||
args = RecipesBase.apply_recipe(d, typeof(v[1]), v[1])[1].args
|
||||
if length(args) == 2 && typeof(args[1]) <: Function && typeof(args[2]) <: Function
|
||||
numfunc, formatter = args
|
||||
Formatted(map(numfunc, v), formatter)
|
||||
function _apply_type_recipe(plotattributes, v::AbstractArray, letter)
|
||||
_preprocess_axis_args!(plotattributes, letter)
|
||||
# First we try to apply an array type recipe.
|
||||
w = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
|
||||
warn_on_recipe_aliases!(plotattributes, :type, typeof(v))
|
||||
# If the type did not change try it element-wise
|
||||
if typeof(v) == typeof(w)
|
||||
isempty(skipmissing(v)) && return Float64[]
|
||||
x = first(skipmissing(v))
|
||||
args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args
|
||||
warn_on_recipe_aliases!(plotattributes, :type, typeof(x))
|
||||
_postprocess_axis_args!(plotattributes, letter)
|
||||
if length(args) == 2 && all(arg -> arg isa Function, args)
|
||||
numfunc, formatter = args
|
||||
return Formatted(map(numfunc, v), formatter)
|
||||
else
|
||||
return v
|
||||
end
|
||||
end
|
||||
_postprocess_axis_args!(plotattributes, letter)
|
||||
return w
|
||||
end
|
||||
|
||||
# special handling for Surface... need to properly unwrap and re-wrap
|
||||
_apply_type_recipe(
|
||||
plotattributes,
|
||||
v::Surface{<:AMat{<:Union{AbstractFloat, Integer, AbstractString, Missing}}},
|
||||
letter,
|
||||
) = v
|
||||
function _apply_type_recipe(plotattributes, v::Surface, letter)
|
||||
ret = _apply_type_recipe(plotattributes, v.surf, letter)
|
||||
if typeof(ret) <: Formatted
|
||||
Formatted(Surface(ret.data), ret.formatter)
|
||||
else
|
||||
v
|
||||
Surface(ret)
|
||||
end
|
||||
end
|
||||
|
||||
# # special handling for Surface... need to properly unwrap and re-wrap
|
||||
# function _apply_type_recipe(d, v::Surface)
|
||||
# T = eltype(v.surf)
|
||||
# @show T
|
||||
# if T <: Integer || T <: AbstractFloat
|
||||
# v
|
||||
# else
|
||||
# ret = _apply_type_recipe(d, v.surf)
|
||||
# if typeof(ret) <: Formatted
|
||||
# Formatted(Surface(ret.data), ret.formatter)
|
||||
# else
|
||||
# v
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# don't do anything vectors of datapoints and for nothing
|
||||
_apply_type_recipe(plotattributes, v::Nothing, letter) = v
|
||||
_apply_type_recipe(
|
||||
plotattributes,
|
||||
v::AbstractArray{<:Union{AbstractFloat, Integer, AbstractString, Missing}},
|
||||
letter,
|
||||
) = v
|
||||
|
||||
# don't do anything for ints or floats
|
||||
_apply_type_recipe(d, v::AbstractArray{T}) where {T<:Union{Integer,AbstractFloat}} = v
|
||||
# axis args before type recipes should still be mapped to all axes
|
||||
function _preprocess_axis_args!(plotattributes)
|
||||
for (k, v) in plotattributes
|
||||
if is_axis_attr_noletter(k)
|
||||
pop!(plotattributes, k)
|
||||
for l in (:x, :y, :z)
|
||||
lk = Symbol(l, k)
|
||||
haskey(plotattributes, lk) || (plotattributes[lk] = v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function _preprocess_axis_args!(plotattributes, letter)
|
||||
plotattributes[:letter] = letter
|
||||
_preprocess_axis_args!(plotattributes)
|
||||
end
|
||||
|
||||
# axis args in type recipes should only be applied to the current axis
|
||||
function _postprocess_axis_args!(plotattributes, letter)
|
||||
pop!(plotattributes, :letter)
|
||||
if letter in (:x, :y, :z)
|
||||
for (k, v) in plotattributes
|
||||
if is_axis_attr_noletter(k)
|
||||
pop!(plotattributes, k)
|
||||
lk = Symbol(letter, k)
|
||||
haskey(plotattributes, lk) || (plotattributes[lk] = v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Fallback user recipes calling type recipes
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# handle "type recipes" by converting inputs, and then either re-calling or slicing
|
||||
@recipe function f(x, y, z)
|
||||
wrap_surfaces!(plotattributes, x, y, z)
|
||||
did_replace = false
|
||||
newx = _apply_type_recipe(plotattributes, x)
|
||||
newx = _apply_type_recipe(plotattributes, x, :x)
|
||||
x === newx || (did_replace = true)
|
||||
newy = _apply_type_recipe(plotattributes, y)
|
||||
newy = _apply_type_recipe(plotattributes, y, :y)
|
||||
y === newy || (did_replace = true)
|
||||
newz = _apply_type_recipe(plotattributes, z)
|
||||
newz = _apply_type_recipe(plotattributes, z, :z)
|
||||
z === newz || (did_replace = true)
|
||||
if did_replace
|
||||
newx, newy, newz
|
||||
@@ -231,10 +291,11 @@ _apply_type_recipe(d, v::AbstractArray{T}) where {T<:Union{Integer,AbstractFloat
|
||||
end
|
||||
end
|
||||
@recipe function f(x, y)
|
||||
wrap_surfaces!(plotattributes, x, y)
|
||||
did_replace = false
|
||||
newx = _apply_type_recipe(plotattributes, x)
|
||||
newx = _apply_type_recipe(plotattributes, x, :x)
|
||||
x === newx || (did_replace = true)
|
||||
newy = _apply_type_recipe(plotattributes, y)
|
||||
newy = _apply_type_recipe(plotattributes, y, :y)
|
||||
y === newy || (did_replace = true)
|
||||
if did_replace
|
||||
newx, newy
|
||||
@@ -243,7 +304,8 @@ end
|
||||
end
|
||||
end
|
||||
@recipe function f(y)
|
||||
newy = _apply_type_recipe(plotattributes, y)
|
||||
wrap_surfaces!(plotattributes, y)
|
||||
newy = _apply_type_recipe(plotattributes, y, :y)
|
||||
if y !== newy
|
||||
newy
|
||||
else
|
||||
@@ -256,7 +318,7 @@ end
|
||||
@recipe function f(v1, v2, v3, v4, vrest...)
|
||||
did_replace = false
|
||||
newargs = map(v -> begin
|
||||
newv = _apply_type_recipe(plotattributes, v)
|
||||
newv = _apply_type_recipe(plotattributes, v, :unknown)
|
||||
if newv !== v
|
||||
did_replace = true
|
||||
end
|
||||
@@ -269,89 +331,114 @@ end
|
||||
end
|
||||
|
||||
|
||||
# # --------------------------------------------------------------------
|
||||
# # 1 argument
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# helper function to ensure relevant attributes are wrapped by Surface
|
||||
function wrap_surfaces(d::KW)
|
||||
if haskey(d, :fill_z)
|
||||
v = d[:fill_z]
|
||||
function wrap_surfaces!(plotattributes, args...) end
|
||||
wrap_surfaces!(plotattributes, x::AMat, y::AMat, z::AMat) = wrap_surfaces!(plotattributes)
|
||||
wrap_surfaces!(plotattributes, x::AVec, y::AVec, z::AMat) = wrap_surfaces!(plotattributes)
|
||||
function wrap_surfaces!(plotattributes, x::AVec, y::AVec, z::Surface)
|
||||
wrap_surfaces!(plotattributes)
|
||||
end
|
||||
function wrap_surfaces!(plotattributes)
|
||||
if haskey(plotattributes, :fill_z)
|
||||
v = plotattributes[:fill_z]
|
||||
if !isa(v, Surface)
|
||||
d[:fill_z] = Surface(v)
|
||||
plotattributes[:fill_z] = Surface(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 1 argument
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||
|
||||
all3D(plotattributes) = trueOrAllTrue(
|
||||
st -> st in (
|
||||
:contour,
|
||||
:contourf,
|
||||
:heatmap,
|
||||
:surface,
|
||||
:wireframe,
|
||||
:contour3d,
|
||||
:image,
|
||||
:plots_heatmap,
|
||||
),
|
||||
get(plotattributes, :seriestype, :none),
|
||||
)
|
||||
|
||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat}
|
||||
@recipe function f(mat::AMat)
|
||||
if all3D(plotattributes)
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
n, m = axes(mat)
|
||||
m, n, Surface(mat)
|
||||
else
|
||||
SliceIt, nothing, mat, nothing
|
||||
nothing, mat, nothing
|
||||
end
|
||||
end
|
||||
|
||||
# if a matrix is wrapped by Formatted, do similar logic, but wrap data with Surface
|
||||
@recipe function f(fmt::Formatted{T}) where T<:AbstractMatrix
|
||||
@recipe function f(fmt::Formatted{<:AMat})
|
||||
if all3D(plotattributes)
|
||||
mat = fmt.data
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
|
||||
n, m = axes(mat)
|
||||
m, n, Formatted(Surface(mat), fmt.formatter)
|
||||
else
|
||||
SliceIt, nothing, fmt, nothing
|
||||
nothing, fmt, nothing
|
||||
end
|
||||
end
|
||||
|
||||
# assume this is a Volume, so construct one
|
||||
@recipe function f(vol::AbstractArray{T,3}, args...) where T<:Number
|
||||
@recipe function f(vol::AbstractArray{<:MaybeNumber, 3}, args...)
|
||||
seriestype := :volume
|
||||
SliceIt, nothing, Volume(vol, args...), nothing
|
||||
end
|
||||
|
||||
|
||||
# # images - grays
|
||||
# images - grays
|
||||
function clamp_greys!(mat::AMat{<:Gray})
|
||||
for i in eachindex(mat)
|
||||
mat[i].val < 0 && (mat[i] = Gray(0))
|
||||
mat[i].val > 1 && (mat[i] = Gray(1))
|
||||
end
|
||||
mat
|
||||
end
|
||||
|
||||
@recipe function f(mat::AMat{T}) where T<:Gray
|
||||
n, m = size(mat)
|
||||
@recipe function f(mat::AMat{<:Gray})
|
||||
n, m = axes(mat)
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
SliceIt, m, n, Surface(clamp_greys!(mat))
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
|
||||
SliceIt, m, n, Surface(clamp!(convert(Matrix{Float64}, mat), 0., 1.))
|
||||
end
|
||||
end
|
||||
|
||||
# # images - colors
|
||||
|
||||
# images - colors
|
||||
@recipe function f(mat::AMat{T}) where T<:Colorant
|
||||
n, m = size(mat)
|
||||
n, m = axes(mat)
|
||||
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
SliceIt, m, n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
aspect_ratio --> :equal
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(z)
|
||||
SliceIt, m, n, Surface(z)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# # plotting arbitrary shapes/polygons
|
||||
# plotting arbitrary shapes/polygons
|
||||
|
||||
@recipe function f(shape::Shape)
|
||||
seriestype --> :shape
|
||||
@@ -365,53 +452,41 @@ end
|
||||
|
||||
@recipe function f(shapes::AMat{Shape})
|
||||
seriestype --> :shape
|
||||
for j in 1:size(shapes,2)
|
||||
for j in axes(shapes,2)
|
||||
@series coords(vec(shapes[:,j]))
|
||||
end
|
||||
end
|
||||
|
||||
# Dicts: each entry is a data point (x,y)=(key,value)
|
||||
|
||||
@recipe f(d::AbstractDict) = collect(keys(d)), collect(values(d))
|
||||
|
||||
# function without range... use the current range of the x-axis
|
||||
|
||||
@recipe function f(f::FuncOrFuncs{F}) where F<:Function
|
||||
plt = plotattributes[:plot_object]
|
||||
xmin, xmax = try
|
||||
axis_limits(plt[1][:xaxis])
|
||||
catch
|
||||
xm = tryrange(f, [-5,-1,0,0.01])
|
||||
xm, tryrange(f, filter(x->x>xm, [5,1,0.99, 0, -0.01]))
|
||||
xmin, xmax = if haskey(plotattributes, :xlims)
|
||||
plotattributes[:xlims]
|
||||
else
|
||||
try
|
||||
axis_limits(plt[1], :x)
|
||||
catch
|
||||
xinv = invscalefunc(get(plotattributes, :xscale, :identity))
|
||||
xm = PlotUtils.tryrange(f, xinv.([-5,-1,0,0.01]))
|
||||
xm, PlotUtils.tryrange(f, filter(x->x>xm, xinv.([5,1,0.99, 0, -0.01])))
|
||||
end
|
||||
end
|
||||
|
||||
f, xmin, xmax
|
||||
end
|
||||
|
||||
# try some intervals over which the function may be defined
|
||||
function tryrange(F::AbstractArray, vec)
|
||||
rets = [tryrange(f, vec) for f in F] # get the preferred for each
|
||||
maxind = maximum(indexin(rets, vec)) # get the last attempt that succeeded (most likely to fit all)
|
||||
rets .= [tryrange(f, vec[maxind:maxind]) for f in F] # ensure that all functions compute there
|
||||
rets[1]
|
||||
end
|
||||
|
||||
function tryrange(F, vec)
|
||||
for v in vec
|
||||
try
|
||||
tmp = F(v)
|
||||
return v
|
||||
catch
|
||||
end
|
||||
end
|
||||
error("$F is not a Function, or is not defined at any of the values $vec")
|
||||
end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 2 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# # if functions come first, just swap the order (not to be confused with parametric functions...
|
||||
# # as there would be more than one function passed in)
|
||||
# --------------------------------------------------------------------
|
||||
# 2 arguments
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# if functions come first, just swap the order (not to be confused with parametric
|
||||
# functions... as there would be more than one function passed in)
|
||||
|
||||
@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function
|
||||
F2 = typeof(x)
|
||||
@@ -419,58 +494,25 @@ end
|
||||
x, f
|
||||
end
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 3 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# # 3d line or scatter
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
||||
# st = get(d, :seriestype, :none)
|
||||
# if st == :scatter
|
||||
# d[:seriestype] = :scatter3d
|
||||
# elseif !is3d(st)
|
||||
# d[:seriestype] = :path3d
|
||||
# end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
||||
# st = get(d, :seriestype, :none)
|
||||
# if size(x) == size(y) == size(z)
|
||||
# if !is3d(st)
|
||||
# seriestype := :path3d
|
||||
# end
|
||||
# end
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
#
|
||||
# # surface-like... function
|
||||
# --------------------------------------------------------------------
|
||||
# 3 arguments
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# surface-like... function
|
||||
@recipe function f(x::AVec, y::AVec, zf::Function)
|
||||
# x = X <: Number ? sort(x) : x
|
||||
# y = Y <: Number ? sort(y) : y
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
end
|
||||
|
||||
#
|
||||
# # surface-like... matrix grid
|
||||
|
||||
# surface-like... matrix grid
|
||||
@recipe function f(x::AVec, y::AVec, z::AMat)
|
||||
if !like_surface(get(plotattributes, :seriestype, :none))
|
||||
plotattributes[:seriestype] = :contour
|
||||
end
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, x, y, Surface(z)
|
||||
x, y, Surface(z)
|
||||
end
|
||||
|
||||
# # images - grays
|
||||
|
||||
# images - grays
|
||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
@@ -485,8 +527,7 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
# # images - colors
|
||||
|
||||
# images - colors
|
||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
@@ -501,88 +542,57 @@ end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Parametric functions
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# # special handling... xmin/xmax with parametric function(s)
|
||||
# --------------------------------------------------------------------
|
||||
# Parametric functions
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# special handling... xmin/xmax with parametric function(s)
|
||||
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
||||
xs = adapted_grid(f, (xmin, xmax))
|
||||
xs, f
|
||||
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||
_scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||
end
|
||||
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
||||
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
|
||||
xs, fs
|
||||
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||
unzip(_scaled_adapted_grid.(fs, xscale, yscale, xmin, xmax))
|
||||
end
|
||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, linspace(umin, umax, n)
|
||||
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n)
|
||||
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||
(xf, xinv), (yf, yinv) = ((scalefunc(s),invscalefunc(s)) for s in (xscale,yscale))
|
||||
xs, ys = adapted_grid(yf∘f∘xinv, xf.((xmin, xmax)))
|
||||
xinv.(xs), yinv.(ys)
|
||||
end
|
||||
|
||||
# special handling... 3D parametric function(s)
|
||||
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
|
||||
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
|
||||
end
|
||||
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200) where {F<:Function,G<:Function,H<:Function}
|
||||
fx, fy, fz, linspace(umin, umax, numPoints)
|
||||
fx, fy, fz, range(umin, stop = umax, length = numPoints)
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Lists of tuples and FixedSizeArrays
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # if we get an unhandled tuple, just splat it in
|
||||
@recipe f(tup::Tuple) = tup
|
||||
|
||||
#
|
||||
# # (x,y) tuples
|
||||
@recipe f(xy::AVec{Tuple{R1,R2}}) where {R1<:Number,R2<:Number} = unzip(xy)
|
||||
@recipe f(xy::Tuple{R1,R2}) where {R1<:Number,R2<:Number} = [xy[1]], [xy[2]]
|
||||
# --------------------------------------------------------------------
|
||||
# Lists of tuples and GeometryTypes.Points
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# # (x,y,z) tuples
|
||||
@recipe f(xyz::AVec{Tuple{R1,R2,R3}}) where {R1<:Number,R2<:Number,R3<:Number} = unzip(xyz)
|
||||
@recipe f(xyz::Tuple{R1,R2,R3}) where {R1<:Number,R2<:Number,R3<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
@recipe f(v::AVec{<:Tuple}) = unzip(v)
|
||||
@recipe f(v::AVec{<:GeometryTypes.Point}) = unzip(v)
|
||||
@recipe f(tup::Tuple) = [tup]
|
||||
@recipe f(p::GeometryTypes.Point) = [p]
|
||||
|
||||
# these might be points+velocity, or OHLC or something else
|
||||
@recipe f(xyuv::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
||||
@recipe f(xyuv::Tuple{R1,R2,R3,R4}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
|
||||
# Special case for 4-tuples in :ohlc series
|
||||
@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
||||
|
||||
|
||||
#
|
||||
# # 2D FixedSizeArrays
|
||||
@recipe f(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T<:Number} = unzip(xy)
|
||||
@recipe f(xy::FixedSizeArrays.Vec{2,T}) where {T<:Number} = [xy[1]], [xy[2]]
|
||||
|
||||
#
|
||||
# # 3D FixedSizeArrays
|
||||
@recipe f(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T<:Number} = unzip(xyz)
|
||||
@recipe f(xyz::FixedSizeArrays.Vec{3,T}) where {T<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # handle grouping
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# @recipe function f(groupby::GroupBy, args...)
|
||||
# for (i,glab) in enumerate(groupby.groupLabels)
|
||||
# # create a new series, with the label of the group, and an idxfilter (to be applied in slice_and_dice)
|
||||
# # TODO: use @series instead
|
||||
# @show i, glab, groupby.groupIds[i]
|
||||
# di = copy(d)
|
||||
# get!(di, :label, string(glab))
|
||||
# get!(di, :idxfilter, groupby.groupIds[i])
|
||||
# push!(series_list, RecipeData(di, args))
|
||||
# end
|
||||
# nothing
|
||||
# end
|
||||
# --------------------------------------------------------------------
|
||||
# handle grouping
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
splittable_kw(key, val, lengthGroup) = false
|
||||
splittable_kw(key, val::AbstractArray, lengthGroup) = (key != :group) && size(val,1) == lengthGroup
|
||||
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && length(axes(val,1)) == lengthGroup
|
||||
splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup))
|
||||
splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup)
|
||||
|
||||
@@ -594,12 +604,12 @@ function split_kw(key, val::SeriesAnnotations, indices)
|
||||
end
|
||||
|
||||
function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
|
||||
y_mat = Array{promote_type(eltype(y), typeof(def_val))}(length(keys(x_ind)), length(groupby.groupLabels))
|
||||
y_mat = Array{promote_type(eltype(y), typeof(def_val))}(undef, length(keys(x_ind)), length(groupby.groupLabels))
|
||||
fill!(y_mat, def_val)
|
||||
for i in 1:length(groupby.groupLabels)
|
||||
for i in eachindex(groupby.groupLabels)
|
||||
xi = x[groupby.groupIds[i]]
|
||||
yi = y[groupby.groupIds[i]]
|
||||
y_mat[getindex.(x_ind, xi), i] = yi
|
||||
y_mat[getindex.(Ref(x_ind), xi), i] = yi
|
||||
end
|
||||
return y_mat
|
||||
end
|
||||
@@ -627,17 +637,17 @@ group_as_matrix(t) = false
|
||||
else
|
||||
g = args[1]
|
||||
if length(g.args) == 1
|
||||
x = zeros(Int64, lengthGroup)
|
||||
x = zeros(Int, lengthGroup)
|
||||
for indexes in groupby.groupIds
|
||||
x[indexes] = 1:length(indexes)
|
||||
x[indexes] = eachindex(indexes)
|
||||
end
|
||||
last_args = g.args
|
||||
else
|
||||
x = g.args[1]
|
||||
last_args = g.args[2:end]
|
||||
end
|
||||
x_u = unique(x)
|
||||
x_ind = Dict(zip(x_u, 1:length(x_u)))
|
||||
x_u = unique(sort(x))
|
||||
x_ind = Dict(zip(x_u, eachindex(x_u)))
|
||||
for (key,val) in plotattributes
|
||||
if splittable_kw(key, val, lengthGroup)
|
||||
:($key) := groupedvec2mat(x_ind, x, val, groupby)
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
"""
|
||||
scatter(x,y)
|
||||
scatter!(x,y)
|
||||
|
||||
Make a scatter plot of y vs x.
|
||||
|
||||
# Examples
|
||||
```julia-repl
|
||||
julia> scatter([1,2,3],[4,5,6],markersize=[3,4,5],markercolor=[:red,:green,:blue])
|
||||
julia> scatter([(1,4),(2,5),(3,6)])
|
||||
```
|
||||
"""
|
||||
@shorthands scatter
|
||||
|
||||
"""
|
||||
bar(x,y)
|
||||
bar!(x,y)
|
||||
|
||||
Make a bar plot of y vs x.
|
||||
|
||||
# Arguments
|
||||
|
||||
- $(_document_argument("bar_position"))
|
||||
- $(_document_argument("bar_width"))
|
||||
- $(_document_argument("bar_edges"))
|
||||
- $(_document_argument("orientation"))
|
||||
|
||||
# Examples
|
||||
```julia-repl
|
||||
julia> bar([1,2,3],[4,5,6],fillcolor=[:red,:green,:blue],fillalpha=[0.2,0.4,0.6])
|
||||
julia> bar([(1,4),(2,5),(3,6)])
|
||||
```
|
||||
"""
|
||||
@shorthands bar
|
||||
|
||||
@shorthands barh
|
||||
|
||||
"""
|
||||
histogram(x)
|
||||
histogram!(x)
|
||||
|
||||
Plot a histogram.
|
||||
|
||||
# Arguments
|
||||
|
||||
- `x`: AbstractVector of values to be binned
|
||||
- $(_document_argument("bins"))
|
||||
- `weights`: Vector of weights for the values in `x`, for weighted bin counts
|
||||
- $(_document_argument("normalize"))
|
||||
- $(_document_argument("bar_position"))
|
||||
- $(_document_argument("bar_width"))
|
||||
- $(_document_argument("bar_edges"))
|
||||
- $(_document_argument("orientation"))
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> histogram([1,2,1,1,4,3,8],bins=0:8)
|
||||
```
|
||||
"""
|
||||
@shorthands histogram
|
||||
|
||||
"""
|
||||
barhist(x)
|
||||
barhist!(x)
|
||||
|
||||
Make a histogram bar plot. See `histogram`.
|
||||
"""
|
||||
@shorthands barhist
|
||||
|
||||
"""
|
||||
stephist(x)
|
||||
stephist(x)
|
||||
|
||||
Make a histogram step plot (bin counts are represented using horizontal lines
|
||||
instead of bars). See `histogram`.
|
||||
"""
|
||||
@shorthands stephist
|
||||
|
||||
"""
|
||||
scatterhist(x)
|
||||
scatterhist!(x)
|
||||
|
||||
Make a histogram scatter plot (bin counts are represented using points
|
||||
instead of bars). See `histogram`.
|
||||
"""
|
||||
@shorthands scatterhist
|
||||
|
||||
"""
|
||||
histogram2d(x,y)
|
||||
histogram2d!(x,y)
|
||||
|
||||
Plot a two-dimensional histogram.
|
||||
|
||||
# Arguments
|
||||
|
||||
- `bins`: Number of bins (if an `Integer`) or bin edges (if an `AbtractVector`)
|
||||
- `weights`: Vector of weights for the values in `x`. Each entry of x contributes
|
||||
its weight to the height of its bin.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> histogram2d(randn(10_000),randn(10_000))
|
||||
```
|
||||
"""
|
||||
@shorthands histogram2d
|
||||
|
||||
"""
|
||||
density(x)
|
||||
density!(x)
|
||||
|
||||
Make a line plot of a kernel density estimate of x.
|
||||
|
||||
# Arguments
|
||||
|
||||
- `x`: AbstractVector of samples for probability density estimation
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> using StatsPlots
|
||||
julia> density(randn(100_000))
|
||||
```
|
||||
"""
|
||||
@shorthands density
|
||||
|
||||
"""
|
||||
heatmap(x,y,z)
|
||||
heatmap!(x,y,z)
|
||||
|
||||
Plot a heatmap of the rectangular array `z`.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> heatmap(randn(10,10))
|
||||
```
|
||||
"""
|
||||
@shorthands heatmap
|
||||
@shorthands plots_heatmap
|
||||
|
||||
"""
|
||||
hexbin(x,y)
|
||||
hexbin!(x,y)
|
||||
|
||||
Make a hexagonal binning plot (a histogram of the observations `(x[i],y[i])`
|
||||
with hexagonal bins)
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> hexbin(randn(10_000), randn(10_000))
|
||||
```
|
||||
"""
|
||||
@shorthands hexbin
|
||||
|
||||
"""
|
||||
sticks(x,y)
|
||||
sticks!(x,y)
|
||||
|
||||
Draw a stick plot of y vs x.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> sticks(1:10)
|
||||
```
|
||||
"""
|
||||
@shorthands sticks
|
||||
|
||||
"""
|
||||
hline(y)
|
||||
hline!(y)
|
||||
|
||||
Draw horizontal lines at positions specified by the values in
|
||||
the AbstractVector `y`
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> hline([-1,0,2])
|
||||
```
|
||||
"""
|
||||
@shorthands hline
|
||||
|
||||
"""
|
||||
vline(x)
|
||||
vline!(x)
|
||||
|
||||
Draw vertical lines at positions specified by the values in
|
||||
the AbstractVector `x`
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> vline([-1,0,2])
|
||||
```
|
||||
"""
|
||||
@shorthands vline
|
||||
|
||||
"""
|
||||
hspan(y)
|
||||
|
||||
Draw a rectangle between the horizontal line at position `y[1]`
|
||||
and the horizontal line at position `y[2]`. If `length(y) ≥ 4`,
|
||||
then further rectangles are drawn between `y[3]` and `y[4]`,
|
||||
`y[5]` and `y[6]`, and so on. If `length(y)` is odd, then the
|
||||
last entry of `y` is ignored.
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> hspan(1:6)
|
||||
```
|
||||
"""
|
||||
@shorthands hspan
|
||||
|
||||
"""
|
||||
vspan(x)
|
||||
|
||||
Draw a rectangle between the vertical line at position `x[1]`
|
||||
and the vertical line at position `x[2]`. If `length(x) ≥ 4`,
|
||||
then further rectangles are drawn between `x[3]` and `x[4]`,
|
||||
`x[5]` and `x[6]`, and so on. If `length(x)` is odd, then the
|
||||
last entry of `x` is ignored.
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> vspan(1:6)
|
||||
```
|
||||
"""
|
||||
@shorthands vspan
|
||||
|
||||
"""
|
||||
ohlc(x,y::Vector{OHLC})
|
||||
ohlc!(x,y::Vector{OHLC})
|
||||
|
||||
Make open-high-low-close plot. Each entry of y is represented by a vertical
|
||||
segment extending from the low value to the high value, with short horizontal
|
||||
segments on the left and right indicating the open and close values, respectively.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> meanprices = cumsum(randn(100))
|
||||
julia> y = OHLC[(p+rand(),p+1,p-1,p+rand()) for p in meanprices]
|
||||
julia> ohlc(y)
|
||||
```
|
||||
"""
|
||||
@shorthands ohlc
|
||||
|
||||
|
||||
"""
|
||||
contour(x,y,z)
|
||||
contour!(x,y,z)
|
||||
|
||||
Draw contour lines of the `Surface` z.
|
||||
|
||||
# Arguments
|
||||
- `levels`: Contour levels (if `AbstractVector`) or number of levels (if `Integer`)
|
||||
- `fill`: Bool. Fill area between contours or draw contours only (false by default)
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> x = y = range(-20, 20, length = 100)
|
||||
julia> contour(x, y, (x, y) -> x^2 + y^2)
|
||||
```
|
||||
"""
|
||||
@shorthands contour
|
||||
|
||||
"An alias for `contour` with fill = true."
|
||||
@shorthands contourf
|
||||
|
||||
|
||||
@shorthands contour3d
|
||||
|
||||
"""
|
||||
surface(x,y,z)
|
||||
surface!(x,y,z)
|
||||
|
||||
Draw a 3D surface plot.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> using LinearAlgebra
|
||||
julia> x = y = range(-3, 3, length = 100)
|
||||
julia> surface(x, y, (x, y) -> sinc(norm([x, y])))
|
||||
```
|
||||
"""
|
||||
@shorthands surface
|
||||
|
||||
"""
|
||||
wireframe(x,y,z)
|
||||
wireframe!(x,y,z)
|
||||
|
||||
Draw a 3D wireframe plot.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> wireframe(1:10,1:10,randn(10,10))
|
||||
```
|
||||
"""
|
||||
@shorthands wireframe
|
||||
|
||||
"""
|
||||
path3d(x,y,z)
|
||||
path3d!(x,y,z)
|
||||
|
||||
Plot a 3D path from `(x[1],y[1],z[1])` to `(x[2],y[2],z[2])`,
|
||||
..., to `(x[end],y[end],z[end])`.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> path3d([0,1,2,3],[0,1,4,9],[0,1,8,27])
|
||||
```
|
||||
"""
|
||||
@shorthands path3d
|
||||
|
||||
"""
|
||||
scatter3d(x,y,z)
|
||||
scatter3d!(x,y,z)
|
||||
|
||||
Make a 3D scatter plot.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> scatter3d([0,1,2,3],[0,1,4,9],[0,1,8,27])
|
||||
```
|
||||
"""
|
||||
@shorthands scatter3d
|
||||
|
||||
"""
|
||||
boxplot(x, y)
|
||||
boxplot!(x, y)
|
||||
|
||||
Make a box and whisker plot.
|
||||
|
||||
# Keyword arguments
|
||||
- `notch`: Bool. Notch the box plot? (false)
|
||||
- `range`: Real. Values more than range*IQR below the first quartile
|
||||
or above the third quartile are shown as outliers (1.5)
|
||||
- `outliers`: Bool. Show outliers? (true)
|
||||
- `whisker_width`: Real or Symbol. Length of whiskers (:match)
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> using StatsPlots
|
||||
julia> boxplot(repeat([1,2,3],outer=100),randn(300))
|
||||
```
|
||||
"""
|
||||
@shorthands boxplot
|
||||
|
||||
"""
|
||||
violin(x,y,z)
|
||||
violin!(x,y,z)
|
||||
|
||||
Make a violin plot.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> violin(repeat([1,2,3],outer=100),randn(300))
|
||||
```
|
||||
"""
|
||||
@shorthands violin
|
||||
|
||||
"""
|
||||
quiver(x,y,quiver=(u,v))
|
||||
quiver!(x,y,quiver=(u,v))
|
||||
|
||||
Make a quiver (vector field) plot. The `i`th vector extends
|
||||
from `(x[i],y[i])` to `(x[i] + u[i], y[i] + v[i])`.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> quiver([1,2,3],[3,2,1],quiver=([1,1,1],[1,2,3]))
|
||||
```
|
||||
"""
|
||||
@shorthands quiver
|
||||
|
||||
"""
|
||||
curves(x,y)
|
||||
curves!(x,y)
|
||||
|
||||
Draw a Bezier curve from `(x[1],y[1])` to `(x[end],y[end])`
|
||||
with control points `(x[2],y[2]), ..., (x[end-1],y[end]-1)`
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> curves([1,2,3,4],[1,1,2,4])
|
||||
```
|
||||
"""
|
||||
@shorthands curves
|
||||
|
||||
"Plot a pie diagram"
|
||||
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
|
||||
"Plot with seriestype :path3d"
|
||||
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
|
||||
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
|
||||
|
||||
"Add title to an existing plot"
|
||||
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
||||
|
||||
"Add xlabel to an existing plot"
|
||||
xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
|
||||
|
||||
"Add ylabel to an existing plot"
|
||||
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
|
||||
|
||||
"Set xlims for an existing plot"
|
||||
xlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; xlims = lims, kw...)
|
||||
|
||||
"Set ylims for an existing plot"
|
||||
ylims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; ylims = lims, kw...)
|
||||
|
||||
"Set zlims for an existing plot"
|
||||
zlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; zlims = lims, kw...)
|
||||
|
||||
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
|
||||
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
|
||||
zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmin,zmax), kw...)
|
||||
|
||||
|
||||
"Set xticks for an existing plot"
|
||||
xticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; xticks = v, kw...)
|
||||
|
||||
"Set yticks for an existing plot"
|
||||
yticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; yticks = v, kw...)
|
||||
|
||||
xticks!(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; xticks = (ticks,labels), kw...)
|
||||
yticks!(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(; yticks = (ticks,labels), kw...)
|
||||
|
||||
"""
|
||||
annotate!(anns...)
|
||||
|
||||
Add annotations to an existing plot.
|
||||
|
||||
# Arguments
|
||||
|
||||
- `anns`: An `AbstractVector` of tuples of the form `(x,y,text)`. The `text` object
|
||||
can be a `String` or `PlotText`.
|
||||
|
||||
# Example
|
||||
```julia-repl
|
||||
julia> plot(1:10)
|
||||
julia> annotate!([(7,3,"(7,3)"),(3,7,text("hey", 14, :left, :top, :green))])
|
||||
```
|
||||
"""
|
||||
annotate!(anns...; kw...) = plot!(; annotation = collect(anns), kw...)
|
||||
annotate!(anns::AVec{T}; kw...) where {T<:Tuple} = plot!(; annotation = anns, kw...)
|
||||
|
||||
"Flip the current plots' x axis"
|
||||
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
|
||||
|
||||
"Flip the current plots' y axis"
|
||||
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
|
||||
|
||||
"Specify x axis attributes for an existing plot"
|
||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||
|
||||
"Specify y axis attributes for an existing plot"
|
||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||
xgrid!(args...; kw...) = plot!(; xgrid = args, kw...)
|
||||
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
|
||||
+5
-5
@@ -7,7 +7,7 @@ function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
defaultbox,
|
||||
defaultbox,
|
||||
KW(),
|
||||
Attr(KW(), _subplot_defaults),
|
||||
nothing,
|
||||
nothing
|
||||
)
|
||||
@@ -34,16 +34,16 @@ bottompad(sp::Subplot) = sp.minpad[4]
|
||||
get_subplot(plt::Plot, sp::Subplot) = sp
|
||||
get_subplot(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
get_subplot(plt::Plot, k) = plt.spmap[k]
|
||||
get_subplot(series::Series) = series.d[:subplot]
|
||||
get_subplot(series::Series) = series.plotattributes[:subplot]
|
||||
|
||||
get_subplot_index(plt::Plot, idx::Integer) = Int(idx)
|
||||
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x === sp, plt.subplots)
|
||||
|
||||
series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
|
||||
series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattributes[:subplot] === sp, sp.plt.series_list)
|
||||
|
||||
function should_add_to_legend(series::Series)
|
||||
series.d[:primary] && series.d[:label] != "" &&
|
||||
!(series.d[:seriestype] in (
|
||||
series.plotattributes[:primary] && series.plotattributes[:label] != "" &&
|
||||
!(series.plotattributes[:seriestype] in (
|
||||
:hexbin,:bins2d,:histogram2d,:hline,:vline,
|
||||
:contour,:contourf,:contour3d,:surface,:wireframe,
|
||||
:heatmap, :pie, :image
|
||||
|
||||
+22
-39
@@ -4,39 +4,19 @@
|
||||
Specify the colour theme for plots.
|
||||
"""
|
||||
function theme(s::Symbol; kw...)
|
||||
defaults = _get_defaults(s)
|
||||
defaults = copy(PlotThemes._themes[s].defaults)
|
||||
_theme(s, defaults; kw...)
|
||||
end
|
||||
|
||||
function _get_defaults(s::Symbol)
|
||||
thm = PlotThemes._themes[s]
|
||||
if :defaults in fieldnames(thm)
|
||||
return thm.defaults
|
||||
else # old PlotTheme type
|
||||
defaults = KW(
|
||||
:bg => thm.bg_secondary,
|
||||
:bginside => thm.bg_primary,
|
||||
:fg => thm.lines,
|
||||
:fgtext => thm.text,
|
||||
:fgguide => thm.text,
|
||||
:fglegend => thm.text,
|
||||
:palette => thm.palette,
|
||||
)
|
||||
if thm.gradient != nothing
|
||||
push!(defaults, :gradient => thm.gradient)
|
||||
end
|
||||
return defaults
|
||||
end
|
||||
end
|
||||
|
||||
function _theme(s::Symbol, defaults::KW; kw...)
|
||||
function _theme(s::Symbol, defaults::AKW; kw...)
|
||||
# Reset to defaults to overwrite active theme
|
||||
reset_defaults()
|
||||
|
||||
# Set the theme's gradient as default
|
||||
if haskey(defaults, :gradient)
|
||||
if haskey(defaults, :colorgradient)
|
||||
PlotUtils.clibrary(:misc)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
|
||||
pop!(defaults, :colorgradient)
|
||||
else
|
||||
PlotUtils.clibrary(:Plots)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
|
||||
@@ -44,8 +24,8 @@ function _theme(s::Symbol, defaults::KW; kw...)
|
||||
|
||||
# maybe overwrite the theme's gradient
|
||||
kw = KW(kw)
|
||||
if haskey(kw, :gradient)
|
||||
kwgrad = pop!(kw, :gradient)
|
||||
if haskey(kw, :colorgradient)
|
||||
kwgrad = pop!(kw, :colorgradient)
|
||||
for clib in clibraries()
|
||||
if kwgrad in cgradients(clib)
|
||||
PlotUtils.clibrary(clib)
|
||||
@@ -74,11 +54,11 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
|
||||
@recipe function showtheme(st::ShowTheme)
|
||||
thm, cfunc = _get_showtheme_args(st.args...)
|
||||
defaults = _get_defaults(thm)
|
||||
defaults = PlotThemes._themes[thm].defaults
|
||||
|
||||
# get the gradient
|
||||
gradient_colors = get(defaults, :gradient, cgrad(:inferno).colors)
|
||||
gradient = cgrad(cfunc.(RGB.(gradient_colors)))
|
||||
gradient_colors = get(defaults, :colorgradient, cgrad(:inferno).colors)
|
||||
colorgradient = cgrad(cfunc.(RGB.(gradient_colors)))
|
||||
|
||||
# get the palette
|
||||
palette = get(defaults, :palette, get_color_palette(:auto, plot_color(:white), 17))
|
||||
@@ -86,21 +66,21 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
|
||||
# apply the theme
|
||||
for k in keys(defaults)
|
||||
k in (:gradient, :palette) && continue
|
||||
k in (:colorgradient, :palette) && continue
|
||||
def = defaults[k]
|
||||
arg = get(_keyAliases, k, k)
|
||||
plotattributes[arg] = if typeof(def) <: Colorant
|
||||
cfunc(RGB(def))
|
||||
elseif eltype(def) <: Colorant
|
||||
cfunc.(RGB.(def))
|
||||
elseif contains(string(arg), "color")
|
||||
elseif occursin("color", string(arg))
|
||||
cfunc.(RGB.(plot_color.(def)))
|
||||
else
|
||||
def
|
||||
end
|
||||
end
|
||||
|
||||
srand(1)
|
||||
Random.seed!(1)
|
||||
|
||||
label := ""
|
||||
colorbar := false
|
||||
@@ -132,34 +112,37 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
|
||||
f(r) = sin(r) / r
|
||||
_norm(x, y) = norm([x, y])
|
||||
x = y = linspace(-3π, 3π, 30)
|
||||
x = y = range(-3π, stop = 3π, length = 30)
|
||||
z = f.(_norm.(x, y'))
|
||||
wi = 2:3:30
|
||||
|
||||
@series begin
|
||||
subplot := 4
|
||||
seriestype := :heatmap
|
||||
seriescolor := gradient
|
||||
ticks := -5:5:5
|
||||
seriescolor := colorgradient
|
||||
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
x, y, z
|
||||
end
|
||||
|
||||
@series begin
|
||||
subplot := 5
|
||||
seriestype := :surface
|
||||
seriescolor := gradient
|
||||
seriescolor := colorgradient
|
||||
xticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
yticks := (-2π:2π:2π, string.(-2:2:2, "π"))
|
||||
x, y, z
|
||||
end
|
||||
|
||||
n = 100
|
||||
ts = linspace(0, 10π, n)
|
||||
x = ts .* cos.(ts)
|
||||
ts = range(0, stop = 10π, length = n)
|
||||
x = (0.1ts) .* cos.(ts)
|
||||
y = (0.1ts) .* sin.(ts)
|
||||
z = 1:n
|
||||
|
||||
@series begin
|
||||
subplot := 6
|
||||
seriescolor := gradient
|
||||
seriescolor := colorgradient
|
||||
linewidth := 3
|
||||
line_z := z
|
||||
x, y, z
|
||||
|
||||
+67
-12
@@ -5,8 +5,10 @@
|
||||
const AVec = AbstractVector
|
||||
const AMat = AbstractMatrix
|
||||
const KW = Dict{Symbol,Any}
|
||||
const AKW = AbstractDict{Symbol,Any}
|
||||
const TicksArgs = Union{AVec{T}, Tuple{AVec{T}, AVec{S}}, Symbol} where {T<:Real, S<:AbstractString}
|
||||
|
||||
struct PlotsDisplay <: Display end
|
||||
struct PlotsDisplay <: AbstractDisplay end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
@@ -19,12 +21,66 @@ Base.isempty(wrapper::InputWrapper) = false
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
mutable struct Series
|
||||
d::KW
|
||||
struct Attr <: AbstractDict{Symbol,Any}
|
||||
explicit::KW
|
||||
defaults::KW
|
||||
end
|
||||
|
||||
attr(series::Series, k::Symbol) = series.d[k]
|
||||
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
|
||||
function Base.getindex(attr::Attr, k)
|
||||
return haskey(attr.explicit, k) ? attr.explicit[k] : attr.defaults[k]
|
||||
end
|
||||
Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(attr.defaults,k)
|
||||
Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default
|
||||
function Base.get!(attr::Attr, k, default)
|
||||
v = if haskey(attr, k)
|
||||
attr[k]
|
||||
else
|
||||
attr.defaults[k] = default
|
||||
end
|
||||
return v
|
||||
end
|
||||
function Base.delete!(attr::Attr, k)
|
||||
haskey(attr.explicit, k) && delete!(attr.explicit, k)
|
||||
haskey(attr.defaults, k) && delete!(attr.defaults, k)
|
||||
end
|
||||
Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults)))
|
||||
function Base.iterate(attr::Attr)
|
||||
exp_keys = keys(attr.explicit)
|
||||
def_keys = setdiff(keys(attr.defaults), exp_keys)
|
||||
key_list = collect(Iterators.flatten((exp_keys, def_keys)))
|
||||
iterate(attr, (key_list, 1))
|
||||
end
|
||||
function Base.iterate(attr::Attr, (key_list, i))
|
||||
i > length(key_list) && return nothing
|
||||
k = key_list[i]
|
||||
(k=>attr[k], (key_list, i+1))
|
||||
end
|
||||
|
||||
Base.copy(attr::Attr) = Attr(copy(attr.explicit), attr.defaults)
|
||||
|
||||
RecipesBase.is_explicit(attr::Attr, k) = haskey(attr.explicit,k)
|
||||
isdefault(attr::Attr, k) = !is_explicit(attr,k) && haskey(attr.defaults,k)
|
||||
|
||||
Base.setindex!(attr::Attr, v, k) = attr.explicit[k] = v
|
||||
|
||||
# Reset to default value and return dict
|
||||
reset_kw!(attr::Attr, k) = is_explicit(attr, k) ? delete!(attr.explicit, k) : attr
|
||||
# Reset to default value and return old value
|
||||
pop_kw!(attr::Attr, k) = is_explicit(attr, k) ? pop!(attr.explicit, k) : attr.defaults[k]
|
||||
pop_kw!(attr::Attr, k, default) = is_explicit(attr, k) ? pop!(attr.explicit, k) : get(attr.defaults, k, default)
|
||||
# Fallbacks for dicts without defaults
|
||||
reset_kw!(d::AKW, k) = delete!(d, k)
|
||||
pop_kw!(d::AKW, k) = pop!(d, k)
|
||||
pop_kw!(d::AKW, k, default) = pop!(d, k, default)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
mutable struct Series
|
||||
plotattributes::Attr
|
||||
end
|
||||
|
||||
attr(series::Series, k::Symbol) = series.plotattributes[k]
|
||||
attr!(series::Series, v, k::Symbol) = (series.plotattributes[k] = v)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
@@ -35,7 +91,7 @@ mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox # the canvas area which is available to this subplot
|
||||
plotarea::BoundingBox # the part where the data goes
|
||||
attr::KW # args specific to this subplot
|
||||
attr::Attr # args specific to this subplot
|
||||
o # can store backend-specific data... like a pyplot ax
|
||||
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
|
||||
end
|
||||
@@ -47,7 +103,7 @@ Base.show(io::IO, sp::Subplot) = print(io, "Subplot{$(sp[:subplot_index])}")
|
||||
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
|
||||
mutable struct Axis
|
||||
sps::Vector{Subplot}
|
||||
d::KW
|
||||
plotattributes::Attr
|
||||
end
|
||||
|
||||
mutable struct Extrema
|
||||
@@ -66,8 +122,7 @@ const SubplotMap = Dict{Any, Subplot}
|
||||
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
backend::T # the backend type
|
||||
n::Int # number of series
|
||||
attr::KW # arguments for the whole plot
|
||||
user_attr::KW # raw arg inputs (after aliases). these are used as the input dict in `_plot!`
|
||||
attr::Attr # arguments for the whole plot
|
||||
series_list::Vector{Series} # arguments for each series
|
||||
o # the backend's plot object
|
||||
subplots::Vector{Subplot}
|
||||
@@ -78,7 +133,7 @@ mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
end
|
||||
|
||||
function Plot()
|
||||
Plot(backend(), 0, KW(), KW(), Series[], nothing,
|
||||
Plot(backend(), 0, Attr(KW(), _plot_defaults), Series[], nothing,
|
||||
Subplot[], SubplotMap(), EmptyLayout(),
|
||||
Subplot[], false)
|
||||
end
|
||||
@@ -87,7 +142,7 @@ end
|
||||
|
||||
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
Base.length(plt::Plot) = length(plt.subplots)
|
||||
Base.endof(plt::Plot) = length(plt)
|
||||
Base.lastindex(plt::Plot) = length(plt)
|
||||
|
||||
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
|
||||
Base.size(plt::Plot) = size(plt.layout)
|
||||
@@ -98,6 +153,6 @@ Base.ndims(plt::Plot) = 2
|
||||
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
|
||||
|
||||
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
|
||||
Base.endof(sp::Subplot) = length(series_list(sp))
|
||||
Base.lastindex(sp::Subplot) = length(series_list(sp))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
+490
-510
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,9 @@
|
||||
|
||||
# [ADD BACKEND WEBSITE]
|
||||
|
||||
function _initialize_backend(::[PkgName]Backend; kw...)
|
||||
@eval begin
|
||||
import [PkgName]
|
||||
export [PkgName]
|
||||
# todo: other initialization that needs to be eval-ed
|
||||
end
|
||||
# todo: other initialization
|
||||
end
|
||||
import [PkgName]
|
||||
export [PkgName]
|
||||
push!(_initialized_backends, [pgkname]::Symbol)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -54,7 +49,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
reference_images
|
||||
@@ -1,8 +0,0 @@
|
||||
StatPlots
|
||||
Images
|
||||
ImageMagick
|
||||
@osx QuartzImageIO
|
||||
GR
|
||||
RDatasets
|
||||
VisualRegressionTests
|
||||
UnicodePlots
|
||||
+15
-73
@@ -1,89 +1,31 @@
|
||||
import Plots._current_plots_version
|
||||
|
||||
using VisualRegressionTests
|
||||
# using ExamplePlots
|
||||
|
||||
import DataFrames, RDatasets
|
||||
|
||||
# don't let pyplot use a gui... it'll crash
|
||||
# note: Agg will set gui -> :none in PyPlot
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
try
|
||||
@eval import PyPlot
|
||||
info("Matplotlib version: $(PyPlot.matplotlib[:__version__])")
|
||||
end
|
||||
|
||||
|
||||
using Plots
|
||||
using StatPlots
|
||||
using Base.Test
|
||||
|
||||
default(size=(500,300))
|
||||
|
||||
|
||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
||||
|
||||
const _current_plots_version = v"0.16.0"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = !is_ci(), sigma = [1,1], tol = 1e-2)
|
||||
Plots._debugMode.on = debug
|
||||
example = Plots._examples[idx]
|
||||
info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
Plots.theme(:default)
|
||||
@info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
backend(pkg)
|
||||
backend()
|
||||
|
||||
default(size=(500,300))
|
||||
# ensure consistent results
|
||||
srand(1234)
|
||||
Random.seed!(1234)
|
||||
|
||||
# reference image directory setup
|
||||
# refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
|
||||
refdir = Pkg.dir("PlotReferenceImages", "Plots", string(pkg))
|
||||
fn = "ref$idx.png"
|
||||
|
||||
# firgure out version info
|
||||
vns = filter(x->x[1] != '.', readdir(refdir))
|
||||
versions = sort(VersionNumber.(vns), rev = true)
|
||||
versions = filter(v -> v <= _current_plots_version, versions)
|
||||
# @show refdir fn versions
|
||||
|
||||
newdir = joinpath(refdir, string(_current_plots_version))
|
||||
newfn = joinpath(newdir, fn)
|
||||
|
||||
# figure out which reference file we should compare to, by finding the highest versioned file
|
||||
reffn = nothing
|
||||
for v in versions
|
||||
tmpfn = joinpath(refdir, string(v), fn)
|
||||
if isfile(tmpfn)
|
||||
reffn = tmpfn
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# now we have the fn (if any)... do the comparison
|
||||
# @show reffn
|
||||
if reffn == nothing
|
||||
reffn = newfn
|
||||
end
|
||||
# @show reffn
|
||||
# return
|
||||
reffn = reference_file(pkg, idx, _current_plots_version)
|
||||
newfn = joinpath(reference_path(pkg, _current_plots_version), fn)
|
||||
|
||||
# test function
|
||||
func = (fn, idx) -> begin
|
||||
map(eval, example.exprs)
|
||||
expr = Expr(:block)
|
||||
append!(expr.args, example.exprs)
|
||||
eval(expr)
|
||||
png(fn)
|
||||
end
|
||||
|
||||
# try
|
||||
# run(`mkdir -p $newdir`)
|
||||
# catch err
|
||||
# display(err)
|
||||
# end
|
||||
# # reffn = joinpath(refdir, "ref$idx.png")
|
||||
|
||||
# the test
|
||||
vtest = VisualTest(func, reffn, idx)
|
||||
test_images(vtest, popup=popup, sigma=sigma, eps=eps, newfn = newfn)
|
||||
test_images(vtest, popup=popup, sigma=sigma, tol=tol, newfn = newfn)
|
||||
end
|
||||
|
||||
function image_comparison_facts(pkg::Symbol;
|
||||
@@ -91,11 +33,11 @@ function image_comparison_facts(pkg::Symbol;
|
||||
only = nothing, # limit to these examples (int index)
|
||||
debug = false, # print debug information?
|
||||
sigma = [1,1], # number of pixels to "blur"
|
||||
eps = 1e-2) # acceptable error (percent)
|
||||
tol = 1e-2) # acceptable error (percent)
|
||||
for i in 1:length(Plots._examples)
|
||||
i in skip && continue
|
||||
if only == nothing || i in only
|
||||
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success == true
|
||||
if only === nothing || i in only
|
||||
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, tol=tol) |> success == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+141
-137
@@ -1,120 +1,118 @@
|
||||
module PlotsTests
|
||||
import ImageMagick
|
||||
using VisualRegressionTests
|
||||
using Plots
|
||||
using Random
|
||||
using Test
|
||||
using FileIO
|
||||
using Gtk
|
||||
using LibGit2
|
||||
using GeometryTypes
|
||||
using Dates
|
||||
|
||||
include("test_hdf5plots.jl")
|
||||
include("test_pgfplotsx.jl")
|
||||
|
||||
reference_dir(args...) = joinpath(homedir(), ".julia", "dev", "PlotReferenceImages", args...)
|
||||
|
||||
function reference_file(backend, i, version)
|
||||
refdir = reference_dir("Plots", string(backend))
|
||||
fn = "ref$i.png"
|
||||
versions = sort(VersionNumber.(readdir(refdir)), rev = true)
|
||||
|
||||
reffn = joinpath(refdir, string(version), fn)
|
||||
for v in versions
|
||||
tmpfn = joinpath(refdir, string(v), fn)
|
||||
if isfile(tmpfn)
|
||||
reffn = tmpfn
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return reffn
|
||||
end
|
||||
|
||||
reference_path(backend, version) = reference_dir("Plots", string(backend), string(version))
|
||||
|
||||
if !isdir(reference_dir())
|
||||
mkpath(reference_dir())
|
||||
LibGit2.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git", reference_dir())
|
||||
end
|
||||
|
||||
include("imgcomp.jl")
|
||||
|
||||
# don't actually show the plots
|
||||
srand(1234)
|
||||
Random.seed!(1234)
|
||||
default(show=false, reuse=true)
|
||||
img_eps = isinteractive() ? 1e-2 : 10e-2
|
||||
is_ci() = get(ENV, "CI", "false") == "true"
|
||||
img_tol = is_ci() ? 1e-2 : Sys.islinux() ? 1e-3 : 0.1
|
||||
|
||||
## Uncomment the following lines to update reference images for different backends
|
||||
|
||||
#=
|
||||
@testset "GR" begin
|
||||
ENV["PLOTS_TEST"] = "true"
|
||||
ENV["GKSwstype"] = "100"
|
||||
@test gr() == Plots.GRBackend()
|
||||
@test backend() == Plots.GRBackend()
|
||||
|
||||
image_comparison_facts(:gr, eps=img_eps)
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
|
||||
|
||||
#@testset "PyPlot" begin
|
||||
# @test pyplot() == Plots.PyPlotBackend()
|
||||
# @test backend() == Plots.PyPlotBackend()
|
||||
#
|
||||
# image_comparison_facts(:pyplot, eps=img_eps)
|
||||
#end
|
||||
|
||||
@testset "UnicodePlots" begin
|
||||
@test unicodeplots() == Plots.UnicodePlotsBackend()
|
||||
@test backend() == Plots.UnicodePlotsBackend()
|
||||
|
||||
# lets just make sure it runs without error
|
||||
@test isa(plot(rand(10)), Plots.Plot) == true
|
||||
plotly()
|
||||
@testset "Plotly" begin
|
||||
image_comparison_facts(:plotly, tol=img_tol, skip = Plots._backend_skips[:plotlyjs])
|
||||
end
|
||||
|
||||
# The plotlyjs testimages return a connection error on travis:
|
||||
# connect: connection refused (ECONNREFUSED)
|
||||
pyplot()
|
||||
@testset "PyPlot" begin
|
||||
image_comparison_facts(:pyplot, tol=img_tol, skip = Plots._backend_skips[:pyplot])
|
||||
end
|
||||
|
||||
# @testset "PlotlyJS" begin
|
||||
# @test plotlyjs() == Plots.PlotlyJSBackend()
|
||||
# @test backend() == Plots.PlotlyJSBackend()
|
||||
#
|
||||
# if is_linux() && isinteractive()
|
||||
# image_comparison_facts(:plotlyjs,
|
||||
# skip=[
|
||||
# 2, # animation (skipped for speed)
|
||||
# 27, # (polar plots) takes very long / not working
|
||||
# 31, # animation (skipped for speed)
|
||||
# ],
|
||||
# eps=img_eps)
|
||||
# end
|
||||
# end
|
||||
pgfplots()
|
||||
@testset "PGFPlots" begin
|
||||
image_comparison_facts(:pgfplots, tol=img_tol, skip = Plots._backend_skips[:pgfplots])
|
||||
end
|
||||
=#
|
||||
##
|
||||
|
||||
@testset "Backends" begin
|
||||
|
||||
# InspectDR returns that error on travis:
|
||||
# ERROR: LoadError: InitError: Cannot open display:
|
||||
# in Gtk.GLib.GError(::Gtk.##229#230) at /home/travis/.julia/v0.5/Gtk/src/GLib/gerror.jl:17
|
||||
@testset "GR" begin
|
||||
ENV["PLOTS_TEST"] = "true"
|
||||
ENV["GKSwstype"] = "100"
|
||||
@test gr() == Plots.GRBackend()
|
||||
@test backend() == Plots.GRBackend()
|
||||
|
||||
# @testset "InspectDR" begin
|
||||
# @test inspectdr() == Plots.InspectDRBackend()
|
||||
# @test backend() == Plots.InspectDRBackend()
|
||||
#
|
||||
# image_comparison_facts(:inspectdr,
|
||||
# skip=[
|
||||
# 2, # animation
|
||||
# 6, # heatmap not defined
|
||||
# 10, # heatmap not defined
|
||||
# 22, # contour not defined
|
||||
# 23, # pie not defined
|
||||
# 27, # polar plot not working
|
||||
# 28, # heatmap not defined
|
||||
# 31, # animation
|
||||
# ],
|
||||
# eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# @testset "Plotly" begin
|
||||
# @test plotly() == Plots.PlotlyBackend()
|
||||
# @test backend() == Plots.PlotlyBackend()
|
||||
#
|
||||
# # # until png generation is reliable on OSX, just test on linux
|
||||
# # @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# @testset "Immerse" begin
|
||||
# @test immerse() == Plots.ImmerseBackend()
|
||||
# @test backend() == Plots.ImmerseBackend()
|
||||
#
|
||||
# # as long as we can plot anything without error, it should be the same as Gadfly
|
||||
# image_comparison_facts(:immerse, only=[1], eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# @testset "PlotlyJS" begin
|
||||
# @test plotlyjs() == Plots.PlotlyJSBackend()
|
||||
# @test backend() == Plots.PlotlyJSBackend()
|
||||
#
|
||||
# # as long as we can plot anything without error, it should be the same as Plotly
|
||||
# image_comparison_facts(:plotlyjs, only=[1], eps=img_eps)
|
||||
# end
|
||||
|
||||
|
||||
# @testset "Gadfly" begin
|
||||
# @test gadfly() == Plots.GadflyBackend()
|
||||
# @test backend() == Plots.GadflyBackend()
|
||||
#
|
||||
# @test typeof(plot(1:10)) == Plots.Plot{Plots.GadflyBackend}
|
||||
# @test plot(Int[1,2,3], rand(3)) == not(nothing)
|
||||
# @test plot(sort(rand(10)), rand(Int, 10, 3)) == not(nothing)
|
||||
# @test plot!(rand(10,3), rand(10,3)) == not(nothing)
|
||||
#
|
||||
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
|
||||
# end
|
||||
@static if haskey(ENV, "APPVEYOR")
|
||||
@info "Skipping GR image comparison tests on AppVeyor"
|
||||
else
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
end
|
||||
|
||||
@testset "UnicodePlots" begin
|
||||
@test unicodeplots() == Plots.UnicodePlotsBackend()
|
||||
@test backend() == Plots.UnicodePlotsBackend()
|
||||
|
||||
# lets just make sure it runs without error
|
||||
p = plot(rand(10))
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = bar(randn(10))
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = plot([1, 2], [3, 4])
|
||||
annotate!(p, [(1.5, 3.2, Plots.text("Test", :red, :center))])
|
||||
hline!(p, [3.1])
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4])
|
||||
hline!(p, [3.1])
|
||||
annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, Plots.text("Test", :red, :center))])
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4])
|
||||
annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, Plots.text("Test", :red, :center))])
|
||||
hline!(p, [3.1])
|
||||
@test isa(p, Plots.Plot) == true
|
||||
@test isa(display(p), Nothing) == true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@testset "Axes" begin
|
||||
p = plot()
|
||||
@@ -131,46 +129,52 @@ end
|
||||
end
|
||||
|
||||
@testset "NoFail" begin
|
||||
histogram([1, 0, 0, 0, 0, 0])
|
||||
plots = [histogram([1, 0, 0, 0, 0, 0]),
|
||||
plot([missing]),
|
||||
plot([missing; 1:4]),
|
||||
plot([fill(missing,10); 1:4]),
|
||||
plot([1 1; 1 missing]),
|
||||
plot(["a" "b"; missing "d"], [1 2; 3 4])]
|
||||
for plt in plots
|
||||
display(plt)
|
||||
end
|
||||
@test_nowarn plot(x->x^2,0,2)
|
||||
end
|
||||
|
||||
# tests for preprocessing recipes
|
||||
@testset "EmptyAnim" begin
|
||||
anim = @animate for i in []
|
||||
end
|
||||
|
||||
# @testset "recipes" begin
|
||||
@test_throws ArgumentError gif(anim)
|
||||
end
|
||||
|
||||
# user recipe
|
||||
@testset "Segments" begin
|
||||
function segments(args...)
|
||||
segs = UnitRange{Int}[]
|
||||
for seg in iter_segments(args...)
|
||||
push!(segs,seg)
|
||||
end
|
||||
segs
|
||||
end
|
||||
|
||||
# type T end
|
||||
# @recipe function f(::T)
|
||||
# line := (3,0.3,:red)
|
||||
# marker := (20,0.5,:blue,:o)
|
||||
# bg := :yellow
|
||||
# rand(10)
|
||||
# end
|
||||
# plot(T())
|
||||
nan10 = fill(NaN,10)
|
||||
@test segments(11:20) == [1:10]
|
||||
@test segments([NaN]) == []
|
||||
@test segments(nan10) == []
|
||||
@test segments([nan10; 1:5]) == [11:15]
|
||||
@test segments([1:5;nan10]) == [1:5]
|
||||
@test segments([nan10; 1:5; nan10; 1:5; nan10]) == [11:15, 26:30]
|
||||
@test segments([NaN; 1], 1:10) == [2:2, 4:4, 6:6, 8:8, 10:10]
|
||||
@test segments([nan10; 1:15], [1:15; nan10]) == [11:15]
|
||||
end
|
||||
|
||||
# plot recipe
|
||||
|
||||
# @recipe function f(::Type{Val{:hiplt}},plt::Plot)
|
||||
# line := (3,0.3,:red)
|
||||
# marker := (20,0.5,:blue,:o)
|
||||
# t := :path
|
||||
# bg:=:green
|
||||
# ()
|
||||
# end
|
||||
# plot(rand(10),t=:hiplt)
|
||||
|
||||
# series recipe
|
||||
|
||||
# @recipe function f(::Type{Val{:hi}},x,y,z)
|
||||
# line := (3,0.3,:red)
|
||||
# marker := (20,0.5,:blue,:o)
|
||||
# t := :path
|
||||
# ()
|
||||
# end
|
||||
# plot(rand(10),t=:hiplt)
|
||||
|
||||
# end
|
||||
|
||||
|
||||
end # module
|
||||
@testset "Utils" begin
|
||||
zipped = ([(1,2)], [("a","b")], [(1,"a"),(2,"b")],
|
||||
[(1,2),(3,4)], [(1,2,3),(3,4,5)], [(1,2,3,4),(3,4,5,6)],
|
||||
[(1,2.0),(missing,missing)], [(1,missing),(missing,"a")],
|
||||
[(missing,missing)], [(missing,missing,missing),("a","b","c")])
|
||||
for z in zipped
|
||||
@test isequal(collect(zip(Plots.unzip(z)...)), z)
|
||||
@test isequal(collect(zip(Plots.unzip(GeometryTypes.Point.(z))...)), z)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import SnoopCompile
|
||||
|
||||
### Log the compiles
|
||||
# This only needs to be run once (to generate "/tmp/plots_compiles.csv")
|
||||
|
||||
# SnoopCompile.@snoop "/tmp/plots_compiles.csv" begin
|
||||
# include(joinpath(dirname(@__FILE__), "runtests.jl"))
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------
|
||||
|
||||
### Parse the compiles and generate precompilation scripts
|
||||
# This can be run repeatedly to tweak the scripts
|
||||
|
||||
# IMPORTANT: we must have the module(s) defined for the parcelation
|
||||
# step, otherwise we will get no precompiles for the Plots module
|
||||
using Plots
|
||||
|
||||
data = SnoopCompile.read("/tmp/plots_compiles.csv")
|
||||
|
||||
# The Plots tests are run inside a module PlotsTest, so all
|
||||
# the precompiles get credited to PlotsTest. Credit them to Plots instead.
|
||||
subst = Dict("PlotsTests"=>"Plots")
|
||||
|
||||
# Blacklist helps fix problems:
|
||||
# - MIME uses type-parameters with symbols like :image/png, which is
|
||||
# not parseable
|
||||
blacklist = ["MIME"]
|
||||
|
||||
# Use these two lines if you want to create precompile functions for
|
||||
# individual packages
|
||||
pc, discards = SnoopCompile.parcel(data[end:-1:1,2], subst=subst, blacklist=blacklist)
|
||||
SnoopCompile.write("/tmp/precompile", pc)
|
||||
|
||||
pdir = joinpath(dirname(@__FILE__), "..")
|
||||
run(`cp /tmp/precompile/precompile_Plots.jl $pdir/src/precompile.jl`)
|
||||
@@ -0,0 +1,21 @@
|
||||
using Plots, HDF5
|
||||
|
||||
|
||||
@testset "HDF5_Plots" begin
|
||||
fname = "tmpplotsave.hdf5"
|
||||
hdf5()
|
||||
|
||||
x = 1:10
|
||||
psrc=plot(x, x.*x); #Create some plot
|
||||
Plots.hdf5plot_write(psrc, fname)
|
||||
|
||||
#Read back file:
|
||||
gr() #Choose some fast backend likely to work in test environment.
|
||||
pread = Plots.hdf5plot_read(fname)
|
||||
|
||||
#Make sure data made it through:
|
||||
@test psrc.subplots[1].series_list[1][:x] == pread.subplots[1].series_list[1][:x]
|
||||
@test psrc.subplots[1].series_list[1][:y] == pread.subplots[1].series_list[1][:y]
|
||||
|
||||
#display(pread) #Don't display. Regression env might not support
|
||||
end #testset
|
||||
@@ -0,0 +1,328 @@
|
||||
using Plots, Test
|
||||
pgfplotsx()
|
||||
|
||||
function create_plot(args...; kwargs...)
|
||||
pgfx_plot = plot(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
end
|
||||
|
||||
function create_plot!(args...; kwargs...)
|
||||
pgfx_plot = plot!(args...; kwargs...)
|
||||
return pgfx_plot, repr("application/x-tex", pgfx_plot)
|
||||
end
|
||||
|
||||
@testset "PGFPlotsX" begin
|
||||
pgfx_plot = plot(1:5)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
|
||||
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
|
||||
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
|
||||
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
|
||||
@test !haskey(axis.contents[1].options.dict, "fill")
|
||||
|
||||
@testset "Legends" begin
|
||||
legends_plot = plot( rand(5,2), lab = ["1" ""] )
|
||||
scatter!(legends_plot, rand(5) )
|
||||
Plots._update_plot_object(legends_plot)
|
||||
axis_contents = Plots.pgfx_axes(legends_plot.o)[1].contents
|
||||
leg_entries = filter( x -> x isa PGFPlotsX.LegendEntry, axis_contents )
|
||||
series = filter( x -> x isa PGFPlotsX.Plot, axis_contents )
|
||||
@test length(leg_entries) == 2
|
||||
@test !haskey(series[1].options.dict, "forget plot")
|
||||
@test haskey(series[2].options.dict, "forget plot")
|
||||
@test !haskey(series[3].options.dict, "forget plot")
|
||||
end # testset
|
||||
|
||||
@testset "3D docs example" begin
|
||||
n = 100
|
||||
ts = range(0, stop = 8π, length = n)
|
||||
x = ts .* map(cos, ts)
|
||||
y = (0.1ts) .* map(sin, ts)
|
||||
z = 1:n
|
||||
pl = plot(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
zcolor = reverse(z),
|
||||
m = (10, 0.8, :blues, Plots.stroke(0)),
|
||||
leg = false,
|
||||
cbar = true,
|
||||
w = 5,
|
||||
)
|
||||
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(
|
||||
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||
)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
end
|
||||
end # testset
|
||||
@testset "Color docs example" begin
|
||||
y = rand(100)
|
||||
plot(
|
||||
0:10:100,
|
||||
rand(11, 4),
|
||||
lab = "lines",
|
||||
w = 3,
|
||||
palette = :grays,
|
||||
fill = 0,
|
||||
α = 0.6,
|
||||
)
|
||||
pl = scatter!(
|
||||
y,
|
||||
zcolor = abs.(y .- 0.5),
|
||||
m = (:heat, 0.8, Plots.stroke(1, :green)),
|
||||
ms = 10 * abs.(y .- 0.5) .+ 4,
|
||||
lab = ["grad", "", "ient"],
|
||||
)
|
||||
Plots._update_plot_object(pl)
|
||||
axis = Plots.pgfx_axes(pl.o)[1]
|
||||
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
|
||||
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
|
||||
marker = axis.contents[15]
|
||||
@test marker isa PGFPlotsX.Plot
|
||||
@test marker.options["mark"] == "*"
|
||||
@test marker.options["mark options"]["color"] ==
|
||||
RGBA{Float64}(colorant"green", 0.8)
|
||||
@test marker.options["mark options"]["line width"] == 1
|
||||
end # testset
|
||||
@testset "Plot in pieces" begin
|
||||
pic = plot(rand(100) / 3, reg = true, fill = (0, :green))
|
||||
scatter!(pic, rand(100), markersize = 6, c = :orange)
|
||||
Plots._update_plot_object(pic)
|
||||
axis_contents = Plots.pgfx_axes(pic.o)[1].contents
|
||||
leg_entries = filter( x -> x isa PGFPlotsX.LegendEntry, axis_contents )
|
||||
series = filter( x -> x isa PGFPlotsX.Plot, axis_contents )
|
||||
@test length(leg_entries) == 2
|
||||
@test length(series) == 4
|
||||
@test haskey(series[1].options.dict, "forget plot")
|
||||
@test !haskey(series[2].options.dict, "forget plot")
|
||||
@test haskey(series[3].options.dict, "forget plot")
|
||||
@test !haskey(series[4].options.dict, "forget plot")
|
||||
end # testset
|
||||
@testset "Marker types" begin
|
||||
markers = filter((m -> begin
|
||||
m in Plots.supported_markers()
|
||||
end), Plots._shape_keys)
|
||||
markers = reshape(markers, 1, length(markers))
|
||||
n = length(markers)
|
||||
x = (range(0, stop = 10, length = n + 2))[2:(end - 1)]
|
||||
y = repeat(reshape(reverse(x), 1, :), n, 1)
|
||||
scatter(
|
||||
x,
|
||||
y,
|
||||
m = (8, :auto),
|
||||
lab = map(string, markers),
|
||||
bg = :linen,
|
||||
xlim = (0, 10),
|
||||
ylim = (0, 10),
|
||||
)
|
||||
end # testset
|
||||
@testset "Layout" begin
|
||||
plot(
|
||||
Plots.fakedata(100, 10),
|
||||
layout = 4,
|
||||
palette = [:grays :blues :heat :lightrainbow],
|
||||
bg_inside = [:orange :pink :darkblue :black],
|
||||
)
|
||||
end # testset
|
||||
@testset "Polar plots" begin
|
||||
Θ = range(0, stop = 1.5π, length = 100)
|
||||
r = abs.(0.1 * randn(100) + sin.(3Θ))
|
||||
plot(Θ, r, proj = :polar, m = 2)
|
||||
end # testset
|
||||
@testset "Drawing shapes" begin
|
||||
verts = [
|
||||
(-1.0, 1.0),
|
||||
(-1.28, 0.6),
|
||||
(-0.2, -1.4),
|
||||
(0.2, -1.4),
|
||||
(1.28, 0.6),
|
||||
(1.0, 1.0),
|
||||
(-1.0, 1.0),
|
||||
(-0.2, -0.6),
|
||||
(0.0, -0.2),
|
||||
(-0.4, 0.6),
|
||||
(1.28, 0.6),
|
||||
(0.2, -1.4),
|
||||
(-0.2, -1.4),
|
||||
(0.6, 0.2),
|
||||
(-0.2, 0.2),
|
||||
(0.0, -0.2),
|
||||
(0.2, 0.2),
|
||||
(-0.2, -0.6),
|
||||
]
|
||||
x = 0.1:0.2:0.9
|
||||
y = 0.7 * rand(5) .+ 0.15
|
||||
plot(
|
||||
x,
|
||||
y,
|
||||
line = (3, :dash, :lightblue),
|
||||
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
|
||||
bg = :pink,
|
||||
fg = :darkblue,
|
||||
xlim = (0, 1),
|
||||
ylim = (0, 1),
|
||||
leg = false,
|
||||
)
|
||||
end # testset
|
||||
@testset "Histogram 2D" begin
|
||||
histogram2d(randn(10000), randn(10000), nbins = 20)
|
||||
end # testset
|
||||
@testset "Heatmap-like" begin
|
||||
xs = [string("x", i) for i = 1:10]
|
||||
ys = [string("y", i) for i = 1:4]
|
||||
z = float((1:4) * reshape(1:10, 1, :))
|
||||
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
if @test_nowarn(
|
||||
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
|
||||
)
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
|
||||
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
|
||||
end
|
||||
|
||||
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
|
||||
# TODO: clims are wrong
|
||||
end # testset
|
||||
@testset "Contours" begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x, y) = begin
|
||||
(3x + y^2) * abs(sin(x) + cos(y))
|
||||
end
|
||||
X = repeat(reshape(x, 1, :), length(y), 1)
|
||||
Y = repeat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p2 = contour(x, y, Z)
|
||||
p1 = contour(x, y, f, fill = true)
|
||||
plot(p1, p2)
|
||||
# TODO: colorbar for filled contours
|
||||
end # testset
|
||||
@testset "Varying colors" begin
|
||||
t = range(0, stop = 1, length = 100)
|
||||
θ = (6π) .* t
|
||||
x = t .* cos.(θ)
|
||||
y = t .* sin.(θ)
|
||||
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
|
||||
p2 = scatter(
|
||||
x,
|
||||
y,
|
||||
marker_z = ((x, y) -> begin
|
||||
x + y
|
||||
end),
|
||||
color = :bluesreds,
|
||||
legend = false,
|
||||
)
|
||||
plot(p1, p2)
|
||||
end # testset
|
||||
@testset "Framestyles" begin
|
||||
scatter(
|
||||
fill(randn(10), 6),
|
||||
fill(randn(10), 6),
|
||||
framestyle = [:box :semi :origin :zerolines :grid :none],
|
||||
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
|
||||
color = permutedims(1:6),
|
||||
layout = 6,
|
||||
label = "",
|
||||
markerstrokewidth = 0,
|
||||
ticks = -2:2,
|
||||
)
|
||||
# TODO: support :semi
|
||||
end # testset
|
||||
@testset "Quiver" begin
|
||||
x = (-2pi):0.2:(2 * pi)
|
||||
y = sin.(x)
|
||||
|
||||
u = ones(length(x))
|
||||
v = cos.(x)
|
||||
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
|
||||
# TODO: could adjust limits to fit arrows if too long, but how?
|
||||
# TODO: get latex available on CI
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
|
||||
# end
|
||||
end # testset
|
||||
@testset "Annotations" begin
|
||||
y = rand(10)
|
||||
pgfx_plot = plot(
|
||||
y,
|
||||
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
|
||||
leg = false,
|
||||
)
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
|
||||
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
|
||||
@test length(nodes) == 1
|
||||
mktempdir() do path
|
||||
file_path =joinpath(path,"annotations.tex")
|
||||
@test_nowarn savefig(pgfx_plot, file_path)
|
||||
open(file_path) do io
|
||||
lines = readlines(io)
|
||||
@test count(s -> occursin("node", s), lines) == 1
|
||||
end
|
||||
end
|
||||
annotate!([
|
||||
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
|
||||
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
|
||||
])
|
||||
Plots._update_plot_object(pgfx_plot)
|
||||
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
|
||||
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
|
||||
@test length(nodes) == 3
|
||||
mktempdir() do path
|
||||
file_path =joinpath(path,"annotations.tex")
|
||||
@test_nowarn savefig(pgfx_plot, file_path)
|
||||
open(file_path) do io
|
||||
lines = readlines(io)
|
||||
@test count(s -> occursin("node", s), lines) == 3
|
||||
end
|
||||
end
|
||||
annotation_plot = scatter!(
|
||||
range(2, stop = 8, length = 6),
|
||||
rand(6),
|
||||
marker = (50, 0.2, :orange),
|
||||
series_annotations = [
|
||||
"series",
|
||||
"annotations",
|
||||
"map",
|
||||
"to",
|
||||
"series",
|
||||
Plots.text("data", :green),
|
||||
],
|
||||
)
|
||||
Plots._update_plot_object(annotation_plot)
|
||||
axis_content = Plots.pgfx_axes(annotation_plot.o)[1].contents
|
||||
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
|
||||
@test length(nodes) == 9
|
||||
mktempdir() do path
|
||||
file_path =joinpath(path,"annotations.tex")
|
||||
@test_nowarn savefig(annotation_plot, file_path)
|
||||
open(file_path) do io
|
||||
lines = readlines(io)
|
||||
@test count(s -> occursin("node", s), lines) == 9
|
||||
end
|
||||
end
|
||||
end # testset
|
||||
@testset "Ribbon" begin
|
||||
aa = rand(10)
|
||||
bb = rand(10)
|
||||
cc = rand(10)
|
||||
conf = [aa - cc bb - cc]
|
||||
ribbon_plot =
|
||||
plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
|
||||
Plots._update_plot_object(ribbon_plot)
|
||||
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
|
||||
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
|
||||
@test length(plots) == 4
|
||||
@test !haskey(plots[1].options.dict, "fill")
|
||||
@test !haskey(plots[2].options.dict, "fill")
|
||||
@test !haskey(plots[3].options.dict, "fill")
|
||||
@test haskey(plots[4].options.dict, "fill")
|
||||
@test ribbon_plot.o !== nothing
|
||||
@test ribbon_plot.o.the_plot !== nothing
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
|
||||
# end
|
||||
end # testset
|
||||
end # testset
|
||||
@@ -1,31 +0,0 @@
|
||||
Pkg.add("ImageMagick")
|
||||
Pkg.build("ImageMagick")
|
||||
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
|
||||
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
|
||||
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
|
||||
|
||||
Pkg.clone("StatPlots")
|
||||
Pkg.checkout("PlotUtils")
|
||||
|
||||
# Pkg.clone("Blink")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.add("Rsvg")
|
||||
# Pkg.add("PlotlyJS")
|
||||
|
||||
# Pkg.checkout("RecipesBase")
|
||||
# Pkg.clone("VisualRegressionTests")
|
||||
|
||||
# need this to use Conda
|
||||
ENV["PYTHON"] = ""
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
|
||||
# Pkg.add("InspectDR")
|
||||
|
||||
Pkg.test("Plots"; coverage=false)
|
||||
Reference in New Issue
Block a user