Compare commits
933 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 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 | |||
| 268c2861c9 | |||
| 15739bc0ee | |||
| c2e8b5a1fa | |||
| 1a67ca897c | |||
| 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 }}
|
||||
@@ -7,3 +7,6 @@ examples/meetup/.ipynb_checkpoints/*
|
||||
deps/plotly-latest.min.js
|
||||
deps/build.log
|
||||
deps/deps.jl
|
||||
Manifest.toml
|
||||
dev/
|
||||
test/tmpplotsave.hdf5
|
||||
|
||||
+27
-43
@@ -4,57 +4,41 @@ os:
|
||||
- linux
|
||||
# - osx
|
||||
julia:
|
||||
# - 1.0
|
||||
- 1.0
|
||||
- 1
|
||||
- nightly
|
||||
# matrix:
|
||||
# allow_failures:
|
||||
# - julia: nightly
|
||||
|
||||
# # before install:
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install wkhtmltopdf; fi
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
|
||||
# ref: http://askubuntu.com/a/556672 for the wkhtmltopdf apt repository info
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- at-spi2-core
|
||||
- libgtk-3-dev
|
||||
- xauth
|
||||
- xvfb
|
||||
|
||||
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
|
||||
# script:
|
||||
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
|
||||
# - julia -e 'import Pkg; Pkg.add(Pkg.PackageSpec(path=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())'
|
||||
|
||||
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
|
||||
- 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,118 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- Minor version 0.17 is the last one to support Julia 0.6!!
|
||||
- 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
|
||||
@@ -179,7 +283,7 @@ Many updates, min julia 1.0
|
||||
- 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
|
||||
@@ -331,7 +435,7 @@ Many updates, min julia 1.0
|
||||
- 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)
|
||||
@@ -453,8 +557,8 @@ Many updates, min julia 1.0
|
||||
#### 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.0"
|
||||
|
||||
[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"
|
||||
julia = "1"
|
||||
|
||||
[extras]
|
||||
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
|
||||
GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
|
||||
Gtk = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44"
|
||||
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"
|
||||
HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
|
||||
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,17 +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)
|
||||
<a href="http://docs.juliaplots.org/latest/" target="_blank"><img src="https://img.shields.io/badge/docs-latest-blue.svg" alt="Latest documentation"></a>
|
||||
<!-- [](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:
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
julia 1.0
|
||||
|
||||
RecipesBase 0.6.0
|
||||
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
|
||||
GR 0.35.0
|
||||
+5
-2
@@ -1,6 +1,6 @@
|
||||
environment:
|
||||
matrix:
|
||||
# - julia_version: 0.7
|
||||
- julia_version: 1.0
|
||||
- julia_version: 1
|
||||
- julia_version: nightly
|
||||
|
||||
@@ -19,6 +19,9 @@ branches:
|
||||
- master
|
||||
- /release-.*/
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.julia\artifacts'
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
on_build_success: false
|
||||
@@ -29,7 +32,7 @@ install:
|
||||
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
|
||||
|
||||
build_script:
|
||||
- echo "%JL_BUILD_SCRIPT%"
|
||||
- echo "%JL_TEST_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
|
||||
|
||||
test_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
+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
|
||||
+12
-96
@@ -1,15 +1,17 @@
|
||||
module Plots
|
||||
|
||||
_current_plots_version = v"0.20.6"
|
||||
const _current_plots_version = VersionNumber(split(first(filter(line -> occursin("version", line), readlines(normpath(@__DIR__, "..", "Project.toml")))), "\"")[2])
|
||||
|
||||
using Reexport
|
||||
|
||||
import StaticArrays
|
||||
using Dates, Printf, Statistics, Base64, LinearAlgebra
|
||||
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
|
||||
@@ -84,7 +86,6 @@ export
|
||||
backends,
|
||||
backend_name,
|
||||
backend_object,
|
||||
add_backend,
|
||||
aliases,
|
||||
|
||||
Shape,
|
||||
@@ -180,103 +181,15 @@ 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 plots_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::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...)
|
||||
|
||||
"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}
|
||||
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
@@ -309,4 +222,7 @@ end
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
include("precompile.jl")
|
||||
_precompile_()
|
||||
|
||||
end # module
|
||||
|
||||
+44
-18
@@ -60,31 +60,43 @@ 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)
|
||||
@@ -93,16 +105,30 @@ 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,16 +167,16 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
error("Unsupported animate filter: $args")
|
||||
end
|
||||
|
||||
push!(block.args, :(if $filterexpr; frame($animsym); end))
|
||||
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
|
||||
$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
|
||||
|
||||
+36
-25
@@ -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,7 +21,7 @@ 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. `range(min(x), stop = extrema(x), length = 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",
|
||||
@@ -29,26 +29,28 @@ const _arg_desc = KW(
|
||||
: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 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}. Number of levels (or x-levels/y-levels) for a contour type.",
|
||||
: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,11 +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",
|
||||
: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.",
|
||||
: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
|
||||
@@ -86,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.",
|
||||
@@ -110,14 +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).",
|
||||
: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.",
|
||||
: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",
|
||||
@@ -149,4 +159,5 @@ const _arg_desc = KW(
|
||||
: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`",
|
||||
: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.",
|
||||
)
|
||||
|
||||
+227
-177
@@ -90,7 +90,7 @@ like_surface(seriestype::Symbol) = seriestype in _surface_like
|
||||
|
||||
is3d(seriestype::Symbol) = seriestype in _3dTypes
|
||||
is3d(series::Series) = is3d(series.plotattributes)
|
||||
is3d(plotattributes::KW) = trueOrAllTrue(is3d, Symbol(plotattributes[:seriestype]))
|
||||
is3d(plotattributes::AKW) = trueOrAllTrue(is3d, Symbol(plotattributes[:seriestype]))
|
||||
|
||||
is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d"
|
||||
ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar"
|
||||
@@ -232,6 +232,7 @@ const _bar_width = 0.8
|
||||
|
||||
const _series_defaults = KW(
|
||||
:label => "AUTO",
|
||||
:colorbar_entry => true,
|
||||
:seriescolor => :auto,
|
||||
:seriesalpha => nothing,
|
||||
:seriestype => :path,
|
||||
@@ -271,6 +272,7 @@ const _series_defaults = KW(
|
||||
:arrow => nothing, # allows for adding arrows to line/path... call `arrow(args...)`
|
||||
:normalize => false, # do we want a normalized histogram?
|
||||
:weights => nothing, # optional weights for histograms (1D and 2D)
|
||||
:show_empty_bins => false, # should empty bins in 2D histogram be colored as zero (otherwise they are transparent)
|
||||
:contours => false, # add contours to 3d surface and wireframe plots
|
||||
:contour_labels => false,
|
||||
:match_dimensions => false, # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196
|
||||
@@ -298,6 +300,7 @@ const _plot_defaults = KW(
|
||||
:link => :none,
|
||||
:overwrite_figure => true,
|
||||
:html_output_format => :auto,
|
||||
:tex_output_standalone => false,
|
||||
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
|
||||
# the parent layout and the relative bounding box of inset subplots
|
||||
:dpi => DPI, # dots per inch for images, etc
|
||||
@@ -334,9 +337,15 @@ const _subplot_defaults = KW(
|
||||
:legendfontvalign => :vcenter,
|
||||
:legendfontrotation => 0.0,
|
||||
:legendfontcolor => :match,
|
||||
:legendtitlefontfamily => :match,
|
||||
:legendtitlefontsize => 11,
|
||||
:legendtitlefonthalign => :hcenter,
|
||||
:legendtitlefontvalign => :vcenter,
|
||||
:legendtitlefontrotation => 0.0,
|
||||
:legendtitlefontcolor => :match,
|
||||
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
||||
:projection => :none, # can also be :polar or :3d
|
||||
:aspect_ratio => :none, # choose from :none or :equal
|
||||
:aspect_ratio => :auto, # choose from :none or :equal
|
||||
:margin => 1mm,
|
||||
:left_margin => :match,
|
||||
:top_margin => :match,
|
||||
@@ -390,6 +399,7 @@ const _axis_defaults = KW(
|
||||
:minorgrid => false,
|
||||
:showaxis => true,
|
||||
:widen => true,
|
||||
:draw_arrow => false,
|
||||
)
|
||||
|
||||
const _suppress_warnings = Set{Symbol}([
|
||||
@@ -408,37 +418,26 @@ const _suppress_warnings = Set{Symbol}([
|
||||
|
||||
# add defaults for the letter versions
|
||||
const _axis_defaults_byletter = KW()
|
||||
for letter in (:x,:y,:z)
|
||||
for k in keys(_axis_defaults)
|
||||
# for k in (
|
||||
# :guide,
|
||||
# :lims,
|
||||
# :ticks,
|
||||
# :scale,
|
||||
# :rotation,
|
||||
# :flip,
|
||||
# :link,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :foreground_color_axis,
|
||||
# :foreground_color_border,
|
||||
# :foreground_color_text,
|
||||
# :foreground_color_guide,
|
||||
# :discrete_values,
|
||||
# :formatter,
|
||||
# )
|
||||
_axis_defaults_byletter[Symbol(letter,k)] = :match
|
||||
|
||||
# allow the underscore version too: xguide or x_guide
|
||||
add_aliases(Symbol(letter, k), Symbol(letter, "_", k))
|
||||
function reset_axis_defaults_byletter!()
|
||||
for letter in (:x,:y,:z)
|
||||
_axis_defaults_byletter[letter] = KW()
|
||||
for (k,v) in _axis_defaults
|
||||
_axis_defaults_byletter[letter][k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
reset_axis_defaults_byletter!()
|
||||
|
||||
for letter in (:x,:y,:z), k in keys(_axis_defaults)
|
||||
# allow the underscore version too: xguide or x_guide
|
||||
add_aliases(Symbol(letter, k), Symbol(letter, "_", k))
|
||||
end
|
||||
|
||||
const _all_defaults = KW[
|
||||
_series_defaults,
|
||||
_plot_defaults,
|
||||
_subplot_defaults,
|
||||
_axis_defaults_byletter
|
||||
_subplot_defaults
|
||||
]
|
||||
|
||||
const _initial_defaults = deepcopy(_all_defaults)
|
||||
@@ -447,12 +446,37 @@ const _initial_axis_defaults = deepcopy(_axis_defaults)
|
||||
# to be able to reset font sizes to initial values
|
||||
const _initial_fontsizes = Dict(:titlefontsize => _subplot_defaults[:titlefontsize],
|
||||
:legendfontsize => _subplot_defaults[:legendfontsize],
|
||||
:legendtitlefontsize => _subplot_defaults[:legendtitlefontsize],
|
||||
:tickfontsize => _axis_defaults[:tickfontsize],
|
||||
:guidefontsize => _axis_defaults[:guidefontsize])
|
||||
|
||||
const _all_args = sort(collect(union(map(keys, _all_defaults)...)))
|
||||
const _internal_args =
|
||||
[:plot_object, :series_plotindex, :markershape_to_add, :letter, :idxfilter]
|
||||
|
||||
const _axis_args = sort(union(collect(keys(_axis_defaults))))
|
||||
const _series_args = sort(union(collect(keys(_series_defaults))))
|
||||
const _subplot_args = sort(union(collect(keys(_subplot_defaults))))
|
||||
const _plot_args = sort(union(collect(keys(_plot_defaults))))
|
||||
|
||||
const _magic_axis_args = [:axis, :tickfont, :guidefont, :grid, :minorgrid]
|
||||
const _magic_subplot_args = [:titlefont, :legendfont, :legendtitlefont, ]
|
||||
const _magic_series_args = [:line, :marker, :fill]
|
||||
|
||||
const _all_axis_args = sort(union([_axis_args; _magic_axis_args]))
|
||||
const _all_subplot_args = sort(union([_subplot_args; _magic_subplot_args]))
|
||||
const _all_series_args = sort(union([_series_args; _magic_series_args]))
|
||||
const _all_plot_args = _plot_args
|
||||
|
||||
const _all_args =
|
||||
sort([_all_axis_args; _all_subplot_args; _all_series_args; _all_plot_args])
|
||||
|
||||
is_subplot_attr(k) = k in _all_subplot_args
|
||||
is_series_attr(k) = k in _all_series_args
|
||||
is_axis_attr(k) = Symbol(chop(string(k); head=1, tail=0)) in _all_axis_args
|
||||
is_axis_attr_noletter(k) = k in _all_axis_args
|
||||
|
||||
RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
|
||||
is_default_attribute(k) = k in _internal_args || k in _all_args || is_axis_attr_noletter(k)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -464,7 +488,7 @@ autopick(notarr, idx::Integer) = notarr
|
||||
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
|
||||
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
|
||||
|
||||
function aliasesAndAutopick(plotattributes::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
function aliasesAndAutopick(plotattributes::AKW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
if plotattributes[sym] == :auto
|
||||
plotattributes[sym] = autopick_ignore_none_auto(options, plotIndex)
|
||||
elseif haskey(aliases, plotattributes[sym])
|
||||
@@ -571,6 +595,7 @@ add_aliases(:xerror, :xerr, :xerrorbar)
|
||||
add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
|
||||
add_aliases(:quiver, :velocity, :quiver2d, :gradient, :vectorfield)
|
||||
add_aliases(:normalize, :norm, :normed, :normalized)
|
||||
add_aliases(:show_empty_bins, :showemptybins, :showempty, :show_empty)
|
||||
add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio)
|
||||
add_aliases(:match_dimensions, :transpose, :transpose_z)
|
||||
add_aliases(:subplot, :sp, :subplt, :splt)
|
||||
@@ -599,6 +624,16 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function parse_axis_kw(s::Symbol)
|
||||
s = string(s)
|
||||
for letter in ('x', 'y', 'z')
|
||||
if startswith(s, letter)
|
||||
return (Symbol(letter), Symbol(chop(s, head=1, tail=0)))
|
||||
end
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
# update the defaults globally
|
||||
|
||||
"""
|
||||
@@ -617,6 +652,11 @@ function default(k::Symbol)
|
||||
if haskey(_axis_defaults, k)
|
||||
return _axis_defaults[k]
|
||||
end
|
||||
if (axis_k = parse_axis_kw(k)) !== nothing
|
||||
letter, key = axis_k
|
||||
return _axis_defaults_byletter[letter][key]
|
||||
end
|
||||
k == :letter && return k # for type recipe processing
|
||||
k in _suppress_warnings || error("Unknown key: ", k)
|
||||
end
|
||||
|
||||
@@ -632,6 +672,11 @@ function default(k::Symbol, v)
|
||||
_axis_defaults[k] = v
|
||||
return v
|
||||
end
|
||||
if (axis_k = parse_axis_kw(k)) !== nothing
|
||||
letter, key = axis_k
|
||||
_axis_defaults_byletter[letter][key] = v
|
||||
return v
|
||||
end
|
||||
k in _suppress_warnings || error("Unknown key: ", k)
|
||||
end
|
||||
|
||||
@@ -643,19 +688,20 @@ function default(; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
function default(plotattributes::KW, k::Symbol)
|
||||
function default(plotattributes::AKW, k::Symbol)
|
||||
get(plotattributes, k, default(k))
|
||||
end
|
||||
|
||||
function reset_defaults()
|
||||
foreach(merge!, _all_defaults, _initial_defaults)
|
||||
merge!(_axis_defaults, _initial_axis_defaults)
|
||||
reset_axis_defaults_byletter!()
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# if arg is a valid color value, then set plotattributes[csym] and return true
|
||||
function handleColors!(plotattributes::KW, arg, csym::Symbol)
|
||||
function handleColors!(plotattributes::AKW, arg, csym::Symbol)
|
||||
try
|
||||
if arg == :auto
|
||||
plotattributes[csym] = :auto
|
||||
@@ -672,7 +718,7 @@ end
|
||||
|
||||
|
||||
|
||||
function processLineArg(plotattributes::KW, arg)
|
||||
function processLineArg(plotattributes::AKW, arg)
|
||||
# seriestype
|
||||
if allLineTypes(arg)
|
||||
plotattributes[:seriestype] = arg
|
||||
@@ -682,15 +728,15 @@ function processLineArg(plotattributes::KW, arg)
|
||||
plotattributes[:linestyle] = arg
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[:linewidth] = arg.width)
|
||||
arg.color == nothing || (plotattributes[:linecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:linealpha] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[:linestyle] = arg.style)
|
||||
arg.width === nothing || (plotattributes[:linewidth] = arg.width)
|
||||
arg.color === nothing || (plotattributes[:linecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:linealpha] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[:linestyle] = arg.style)
|
||||
|
||||
elseif typeof(arg) <: Brush
|
||||
arg.size == nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color == nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
arg.size === nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color === nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
|
||||
elseif typeof(arg) <: Arrow || arg in (:arrow, :arrows)
|
||||
plotattributes[:arrow] = arg
|
||||
@@ -711,7 +757,7 @@ function processLineArg(plotattributes::KW, arg)
|
||||
end
|
||||
|
||||
|
||||
function processMarkerArg(plotattributes::KW, arg)
|
||||
function processMarkerArg(plotattributes::AKW, arg)
|
||||
# markershape
|
||||
if allShapes(arg)
|
||||
plotattributes[:markershape] = arg
|
||||
@@ -721,20 +767,24 @@ function processMarkerArg(plotattributes::KW, arg)
|
||||
plotattributes[:markerstrokestyle] = arg
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[:markerstrokewidth] = arg.width)
|
||||
arg.color == nothing || (plotattributes[:markerstrokecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:markerstrokealpha] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[:markerstrokestyle] = arg.style)
|
||||
arg.width === nothing || (plotattributes[:markerstrokewidth] = arg.width)
|
||||
arg.color === nothing || (plotattributes[:markerstrokecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:markerstrokealpha] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[:markerstrokestyle] = arg.style)
|
||||
|
||||
elseif typeof(arg) <: Brush
|
||||
arg.size == nothing || (plotattributes[:markersize] = arg.size)
|
||||
arg.color == nothing || (plotattributes[:markercolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:markeralpha] = arg.alpha)
|
||||
arg.size === nothing || (plotattributes[:markersize] = arg.size)
|
||||
arg.color === nothing || (plotattributes[:markercolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:markeralpha] = arg.alpha)
|
||||
|
||||
# linealpha
|
||||
elseif allAlphas(arg)
|
||||
plotattributes[:markeralpha] = arg
|
||||
|
||||
# bool
|
||||
elseif typeof(arg) <: Bool
|
||||
plotattributes[:markershape] = arg ? :circle : :none
|
||||
|
||||
# markersize
|
||||
elseif allReals(arg)
|
||||
plotattributes[:markersize] = arg
|
||||
@@ -747,12 +797,12 @@ function processMarkerArg(plotattributes::KW, arg)
|
||||
end
|
||||
|
||||
|
||||
function processFillArg(plotattributes::KW, arg)
|
||||
function processFillArg(plotattributes::AKW, arg)
|
||||
# fr = get(plotattributes, :fillrange, 0)
|
||||
if typeof(arg) <: Brush
|
||||
arg.size == nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color == nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
arg.size === nothing || (plotattributes[:fillrange] = arg.size)
|
||||
arg.color === nothing || (plotattributes[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[:fillalpha] = arg.alpha)
|
||||
|
||||
elseif typeof(arg) <: Bool
|
||||
plotattributes[:fillrange] = arg ? 0 : nothing
|
||||
@@ -770,7 +820,6 @@ function processFillArg(plotattributes::KW, arg)
|
||||
plotattributes[:fillrange] = arg
|
||||
|
||||
elseif !handleColors!(plotattributes, arg, :fillcolor)
|
||||
|
||||
plotattributes[:fillrange] = arg
|
||||
end
|
||||
# plotattributes[:fillrange] = fr
|
||||
@@ -778,7 +827,7 @@ function processFillArg(plotattributes::KW, arg)
|
||||
end
|
||||
|
||||
|
||||
function processGridArg!(plotattributes::KW, arg, letter)
|
||||
function processGridArg!(plotattributes::AKW, arg, letter)
|
||||
if arg in _allGridArgs || isa(arg, Bool)
|
||||
plotattributes[Symbol(letter, :grid)] = hasgrid(arg, letter)
|
||||
|
||||
@@ -786,10 +835,10 @@ function processGridArg!(plotattributes::KW, arg, letter)
|
||||
plotattributes[Symbol(letter, :gridstyle)] = arg
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[Symbol(letter, :gridlinewidth)] = arg.width)
|
||||
arg.color == nothing || (plotattributes[Symbol(letter, :foreground_color_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[Symbol(letter, :gridalpha)] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[Symbol(letter, :gridstyle)] = arg.style)
|
||||
arg.width === nothing || (plotattributes[Symbol(letter, :gridlinewidth)] = arg.width)
|
||||
arg.color === nothing || (plotattributes[Symbol(letter, :foreground_color_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[Symbol(letter, :gridalpha)] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[Symbol(letter, :gridstyle)] = arg.style)
|
||||
|
||||
# linealpha
|
||||
elseif allAlphas(arg)
|
||||
@@ -806,7 +855,7 @@ function processGridArg!(plotattributes::KW, arg, letter)
|
||||
end
|
||||
end
|
||||
|
||||
function processMinorGridArg!(plotattributes::KW, arg, letter)
|
||||
function processMinorGridArg!(plotattributes::AKW, arg, letter)
|
||||
if arg in _allGridArgs || isa(arg, Bool)
|
||||
plotattributes[Symbol(letter, :minorgrid)] = hasgrid(arg, letter)
|
||||
|
||||
@@ -815,10 +864,10 @@ function processMinorGridArg!(plotattributes::KW, arg, letter)
|
||||
plotattributes[Symbol(letter, :minorgrid)] = true
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (plotattributes[Symbol(letter, :minorgridlinewidth)] = arg.width)
|
||||
arg.color == nothing || (plotattributes[Symbol(letter, :foreground_color_minor_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha == nothing || (plotattributes[Symbol(letter, :minorgridalpha)] = arg.alpha)
|
||||
arg.style == nothing || (plotattributes[Symbol(letter, :minorgridstyle)] = arg.style)
|
||||
arg.width === nothing || (plotattributes[Symbol(letter, :minorgridlinewidth)] = arg.width)
|
||||
arg.color === nothing || (plotattributes[Symbol(letter, :foreground_color_minor_grid)] = arg.color in (:auto, :match) ? :match : plot_color(arg.color))
|
||||
arg.alpha === nothing || (plotattributes[Symbol(letter, :minorgridalpha)] = arg.alpha)
|
||||
arg.style === nothing || (plotattributes[Symbol(letter, :minorgridstyle)] = arg.style)
|
||||
plotattributes[Symbol(letter, :minorgrid)] = true
|
||||
|
||||
# linealpha
|
||||
@@ -839,7 +888,7 @@ function processMinorGridArg!(plotattributes::KW, arg, letter)
|
||||
end
|
||||
end
|
||||
|
||||
function processFontArg!(plotattributes::KW, fontname::Symbol, arg)
|
||||
function processFontArg!(plotattributes::AKW, fontname::Symbol, arg)
|
||||
T = typeof(arg)
|
||||
if T <: Font
|
||||
plotattributes[Symbol(fontname, :family)] = arg.family
|
||||
@@ -876,7 +925,7 @@ _replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
|
||||
_replace_markershape(shapes::AVec) = map(_replace_markershape, shapes)
|
||||
_replace_markershape(shape) = shape
|
||||
|
||||
function _add_markershape(plotattributes::KW)
|
||||
function _add_markershape(plotattributes::AKW)
|
||||
# add the markershape if it needs to be added... hack to allow "m=10" to add a shape,
|
||||
# and still allow overriding in _apply_recipe
|
||||
ms = pop!(plotattributes, :markershape_to_add, :none)
|
||||
@@ -886,27 +935,11 @@ function _add_markershape(plotattributes::KW)
|
||||
end
|
||||
|
||||
"Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
|
||||
function preprocessArgs!(plotattributes::KW)
|
||||
function preprocessArgs!(plotattributes::AKW)
|
||||
replaceAliases!(plotattributes, _keyAliases)
|
||||
|
||||
# clear all axis stuff
|
||||
# if haskey(plotattributes, :axis) && plotattributes[:axis] in (:none, nothing, false)
|
||||
# plotattributes[:ticks] = nothing
|
||||
# plotattributes[:foreground_color_border] = RGBA(0,0,0,0)
|
||||
# plotattributes[:foreground_color_axis] = RGBA(0,0,0,0)
|
||||
# plotattributes[:grid] = false
|
||||
# delete!(plotattributes, :axis)
|
||||
# end
|
||||
# for letter in (:x, :y, :z)
|
||||
# asym = Symbol(letter, :axis)
|
||||
# if haskey(plotattributes, asym) || plotattributes[asym] in (:none, nothing, false)
|
||||
# plotattributes[Symbol(letter, :ticks)] = nothing
|
||||
# plotattributes[Symbol(letter, :foreground_color_border)] = RGBA(0,0,0,0)
|
||||
# end
|
||||
# end
|
||||
|
||||
# handle axis args common to all axis
|
||||
args = pop!(plotattributes, :axis, ())
|
||||
args = pop_kw!(plotattributes, :axis, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
process_axis_arg!(plotattributes, arg, letter)
|
||||
@@ -915,7 +948,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle axis args
|
||||
for letter in (:x, :y, :z)
|
||||
asym = Symbol(letter, :axis)
|
||||
args = pop!(plotattributes, asym, ())
|
||||
args = pop_kw!(plotattributes, asym, ())
|
||||
if !(typeof(args) <: Axis)
|
||||
for arg in wraptuple(args)
|
||||
process_axis_arg!(plotattributes, arg, letter)
|
||||
@@ -923,8 +956,17 @@ function preprocessArgs!(plotattributes::KW)
|
||||
end
|
||||
end
|
||||
|
||||
# vline accesses the y argument but actually maps it to the x axis.
|
||||
# Hence, we have to swap formatters
|
||||
if get(plotattributes, :seriestype, :path) == :vline
|
||||
xformatter = get(plotattributes, :xformatter, :auto)
|
||||
yformatter = get(plotattributes, :yformatter, :auto)
|
||||
plotattributes[:xformatter] = yformatter
|
||||
plotattributes[:yformatter] = xformatter
|
||||
end
|
||||
|
||||
# handle grid args common to all axes
|
||||
args = pop!(plotattributes, :grid, ())
|
||||
args = pop_kw!(plotattributes, :grid, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
processGridArg!(plotattributes, arg, letter)
|
||||
@@ -933,13 +975,13 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle individual axes grid args
|
||||
for letter in (:x, :y, :z)
|
||||
gridsym = Symbol(letter, :grid)
|
||||
args = pop!(plotattributes, gridsym, ())
|
||||
args = pop_kw!(plotattributes, gridsym, ())
|
||||
for arg in wraptuple(args)
|
||||
processGridArg!(plotattributes, arg, letter)
|
||||
end
|
||||
end
|
||||
# handle minor grid args common to all axes
|
||||
args = pop!(plotattributes, :minorgrid, ())
|
||||
args = pop_kw!(plotattributes, :minorgrid, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
processMinorGridArg!(plotattributes, arg, letter)
|
||||
@@ -948,21 +990,14 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle individual axes grid args
|
||||
for letter in (:x, :y, :z)
|
||||
gridsym = Symbol(letter, :minorgrid)
|
||||
args = pop!(plotattributes, gridsym, ())
|
||||
args = pop_kw!(plotattributes, gridsym, ())
|
||||
for arg in wraptuple(args)
|
||||
processMinorGridArg!(plotattributes, arg, letter)
|
||||
end
|
||||
end
|
||||
# fonts
|
||||
for fontname in (:titlefont, :legendfont)
|
||||
args = pop!(plotattributes, fontname, ())
|
||||
for arg in wraptuple(args)
|
||||
processFontArg!(plotattributes, fontname, arg)
|
||||
end
|
||||
end
|
||||
# handle font args common to all axes
|
||||
for fontname in (:tickfont, :guidefont)
|
||||
args = pop!(plotattributes, fontname, ())
|
||||
args = pop_kw!(plotattributes, fontname, ())
|
||||
for arg in wraptuple(args)
|
||||
for letter in (:x, :y, :z)
|
||||
processFontArg!(plotattributes, Symbol(letter, fontname), arg)
|
||||
@@ -972,15 +1007,35 @@ function preprocessArgs!(plotattributes::KW)
|
||||
# handle individual axes font args
|
||||
for letter in (:x, :y, :z)
|
||||
for fontname in (:tickfont, :guidefont)
|
||||
args = pop!(plotattributes, Symbol(letter, fontname), ())
|
||||
args = pop_kw!(plotattributes, Symbol(letter, fontname), ())
|
||||
for arg in wraptuple(args)
|
||||
processFontArg!(plotattributes, Symbol(letter, fontname), arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
# handle axes args
|
||||
for k in _axis_args
|
||||
if haskey(plotattributes, k)
|
||||
v = plotattributes[k]
|
||||
for letter in (:x, :y, :z)
|
||||
lk = Symbol(letter, k)
|
||||
if !is_explicit(plotattributes, lk)
|
||||
plotattributes[lk] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# fonts
|
||||
for fontname in (:titlefont, :legendfont, :legendtitlefont)
|
||||
args = pop_kw!(plotattributes, fontname, ())
|
||||
for arg in wraptuple(args)
|
||||
processFontArg!(plotattributes, fontname, arg)
|
||||
end
|
||||
end
|
||||
|
||||
# handle line args
|
||||
for arg in wraptuple(pop!(plotattributes, :line, ()))
|
||||
for arg in wraptuple(pop_kw!(plotattributes, :line, ()))
|
||||
processLineArg(plotattributes, arg)
|
||||
end
|
||||
|
||||
@@ -994,7 +1049,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
processMarkerArg(plotattributes, arg)
|
||||
anymarker = true
|
||||
end
|
||||
delete!(plotattributes, :marker)
|
||||
reset_kw!(plotattributes, :marker)
|
||||
if haskey(plotattributes, :markershape)
|
||||
plotattributes[:markershape] = _replace_markershape(plotattributes[:markershape])
|
||||
if plotattributes[:markershape] == :none && plotattributes[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d) #the default should be :auto, not :none, so that :none can be set explicitly and would be respected
|
||||
@@ -1008,7 +1063,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
for arg in wraptuple(get(plotattributes, :fill, ()))
|
||||
processFillArg(plotattributes, arg)
|
||||
end
|
||||
delete!(plotattributes, :fill)
|
||||
reset_kw!(plotattributes, :fill)
|
||||
|
||||
# handle series annotations
|
||||
if haskey(plotattributes, :series_annotations)
|
||||
@@ -1023,7 +1078,7 @@ function preprocessArgs!(plotattributes::KW)
|
||||
arrow()
|
||||
elseif a in (false, nothing, :none)
|
||||
nothing
|
||||
elseif !(typeof(a) <: Arrow)
|
||||
elseif !(typeof(a) <: Arrow || typeof(a) <: AbstractArray{Arrow})
|
||||
arrow(wraptuple(a)...)
|
||||
else
|
||||
a
|
||||
@@ -1050,8 +1105,8 @@ function preprocessArgs!(plotattributes::KW)
|
||||
|
||||
# warnings for moved recipes
|
||||
st = get(plotattributes, :seriestype, :path)
|
||||
if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatPlots)
|
||||
@warn("seriestype $st has been moved to StatPlots. To use: \`Pkg.add(\"StatPlots\"); using StatPlots\`")
|
||||
if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatsPlots)
|
||||
@warn("seriestype $st has been moved to StatsPlots. To use: \`Pkg.add(\"StatsPlots\"); using StatsPlots\`")
|
||||
end
|
||||
|
||||
return
|
||||
@@ -1073,7 +1128,7 @@ function extractGroupArgs(v::AVec, args...; legendEntry = string)
|
||||
if n > 100
|
||||
@warn("You created n=$n groups... Is that intended?")
|
||||
end
|
||||
groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels]
|
||||
groupIds = Vector{Int}[filter(i -> v[i] == glab, eachindex(v)) for glab in groupLabels]
|
||||
GroupBy(map(legendEntry, groupLabels), groupIds)
|
||||
end
|
||||
|
||||
@@ -1081,7 +1136,7 @@ legendEntryFromTuple(ns::Tuple) = join(ns, ' ')
|
||||
|
||||
# this is when given a tuple of vectors of values to group by
|
||||
function extractGroupArgs(vs::Tuple, args...)
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
isempty(vs) && return GroupBy([""], [axes(args[1],1)])
|
||||
v = map(tuple, vs...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
@@ -1091,7 +1146,7 @@ legendEntryFromTuple(ns::NamedTuple) =
|
||||
join(["$k = $v" for (k, v) in pairs(ns)], ", ")
|
||||
|
||||
function extractGroupArgs(vs::NamedTuple, args...)
|
||||
isempty(vs) && return GroupBy([""], [1:size(args[1],1)])
|
||||
isempty(vs) && return GroupBy([""], [axes(args[1],1)])
|
||||
v = map(NamedTuple{keys(vs)}∘tuple, values(vs)...)
|
||||
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple)
|
||||
end
|
||||
@@ -1106,15 +1161,15 @@ end
|
||||
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
|
||||
filter_data(v, idxfilter) = v
|
||||
|
||||
function filter_data!(plotattributes::KW, idxfilter)
|
||||
function filter_data!(plotattributes::AKW, idxfilter)
|
||||
for s in (:x, :y, :z)
|
||||
plotattributes[s] = filter_data(get(plotattributes, s, nothing), idxfilter)
|
||||
end
|
||||
end
|
||||
|
||||
function _filter_input_data!(plotattributes::KW)
|
||||
function _filter_input_data!(plotattributes::AKW)
|
||||
idxfilter = pop!(plotattributes, :idxfilter, nothing)
|
||||
if idxfilter != nothing
|
||||
if idxfilter !== nothing
|
||||
filter_data!(plotattributes, idxfilter)
|
||||
end
|
||||
end
|
||||
@@ -1125,7 +1180,7 @@ end
|
||||
const _already_warned = Dict{Symbol,Set{Symbol}}()
|
||||
const _to_warn = Set{Symbol}()
|
||||
|
||||
function warnOnUnsupported_args(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported_args(pkg::AbstractBackend, plotattributes)
|
||||
empty!(_to_warn)
|
||||
bend = backend_name(pkg)
|
||||
already_warned = get!(_already_warned, bend, Set{Symbol}())
|
||||
@@ -1149,7 +1204,7 @@ end
|
||||
# _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg)
|
||||
# _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
|
||||
|
||||
function warnOnUnsupported(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported(pkg::AbstractBackend, plotattributes)
|
||||
if !is_seriestype_supported(pkg, plotattributes[:seriestype])
|
||||
@warn("seriestype $(plotattributes[:seriestype]) is unsupported with $pkg. Choose from: $(supported_seriestypes(pkg))")
|
||||
end
|
||||
@@ -1161,7 +1216,7 @@ function warnOnUnsupported(pkg::AbstractBackend, plotattributes::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::KW)
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::AKW)
|
||||
for k in (:xscale, :yscale, :zscale, :scale)
|
||||
if haskey(plotattributes, k)
|
||||
v = plotattributes[k]
|
||||
@@ -1180,7 +1235,7 @@ function convertLegendValue(val::Symbol)
|
||||
:best
|
||||
elseif val in (:no, :none)
|
||||
:none
|
||||
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend, :topright, :topleft, :bottomleft, :bottomright, :outertopright)
|
||||
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend, :topright, :topleft, :bottomleft, :bottomright, :outertopright, :outertopleft, :outertop, :outerright, :outerleft, :outerbottomright, :outerbottomleft, :outerbottom, :inline)
|
||||
val
|
||||
else
|
||||
error("Invalid symbol for legend: $val")
|
||||
@@ -1200,24 +1255,26 @@ convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
# anything else is returned as-is
|
||||
function slice_arg(v::AMat, idx::Int)
|
||||
c = mod1(idx, size(v,2))
|
||||
size(v,1) == 1 ? v[1,c] : v[:,c]
|
||||
m,n = axes(v)
|
||||
size(v,1) == 1 ? v[first(m),n[c]] : v[:,n[c]]
|
||||
end
|
||||
slice_arg(wrapper::InputWrapper, idx) = wrapper.obj
|
||||
slice_arg(v, idx) = v
|
||||
|
||||
|
||||
# given an argument key (k), we want to extract the argument value for this index.
|
||||
# matrices are sliced by column, otherwise we
|
||||
# if nothing is set (or container is empty), return the default or the existing value.
|
||||
function slice_arg!(plotattributes_in::KW,plotattributes_out::KW, k::Symbol, default_value, idx::Int, remove_pair::Bool)
|
||||
v = get(plotattributes_in, k, get(plotattributes_out, k, default_value))
|
||||
plotattributes_out[k] = if haskey(plotattributes_in, k) && typeof(v) <: AMat && !isempty(v)
|
||||
# given an argument key (k), extract the argument value for this index,
|
||||
# and set into plotattributes[k]. Matrices are sliced by column.
|
||||
# if nothing is set (or container is empty), return the existing value.
|
||||
function slice_arg!(plotattributes_in, plotattributes_out,
|
||||
k::Symbol, idx::Int, remove_pair::Bool)
|
||||
v = get(plotattributes_in, k, plotattributes_out[k])
|
||||
plotattributes_out[k] = if haskey(plotattributes_in, k) && typeof(v) <: AMat && !isempty(v)
|
||||
slice_arg(v, idx)
|
||||
else
|
||||
v
|
||||
end
|
||||
if remove_pair
|
||||
delete!(plotattributes_in, k)
|
||||
reset_kw!(plotattributes_in, k)
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -1226,26 +1283,20 @@ end
|
||||
|
||||
# # if the value is `:match` then we take whatever match_color is.
|
||||
# # this is mainly used for cascading defaults for foreground and background colors
|
||||
# function color_or_match!(plotattributes::KW, k::Symbol, match_color)
|
||||
# function color_or_match!(plotattributes::AKW, k::Symbol, match_color)
|
||||
# v = plotattributes[k]
|
||||
# plotattributes[k] = if v == :match
|
||||
# match_color
|
||||
# elseif v == nothing
|
||||
# elseif v === nothing
|
||||
# plot_color(RGBA(0,0,0,0))
|
||||
# else
|
||||
# v
|
||||
# end
|
||||
# end
|
||||
|
||||
function color_or_nothing!(plotattributes::KW, k::Symbol)
|
||||
function color_or_nothing!(plotattributes, k::Symbol)
|
||||
v = plotattributes[k]
|
||||
plotattributes[k] = if v == nothing || v == false
|
||||
RGBA{Float64}(0,0,0,0)
|
||||
elseif v != :match
|
||||
plot_color(v)
|
||||
else
|
||||
v
|
||||
end
|
||||
plotattributes[k] = v == :match ? v : plot_color(v)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1264,8 +1315,10 @@ const _match_map = KW(
|
||||
:bottom_margin => :margin,
|
||||
:titlefontfamily => :fontfamily_subplot,
|
||||
:legendfontfamily => :fontfamily_subplot,
|
||||
:legendtitlefontfamily => :fontfamily_subplot,
|
||||
:titlefontcolor => :foreground_color_subplot,
|
||||
:legendfontcolor => :foreground_color_subplot,
|
||||
:legendtitlefontcolor => :foreground_color_subplot,
|
||||
:tickfontcolor => :foreground_color_text,
|
||||
:guidefontcolor => :foreground_color_guide,
|
||||
)
|
||||
@@ -1342,18 +1395,18 @@ Base.get(series::Series, k::Symbol, v) = get(series.plotattributes, k, v)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function fg_color(plotattributes::KW)
|
||||
fg = default(plotattributes, :foreground_color)
|
||||
function fg_color(plotattributes::AKW)
|
||||
fg = get(plotattributes, :foreground_color, :auto)
|
||||
if fg == :auto
|
||||
bg = plot_color(default(plotattributes, :background_color))
|
||||
bg = plot_color(get(plotattributes, :background_color, :white))
|
||||
fg = isdark(bg) ? colorant"white" : colorant"black"
|
||||
else
|
||||
plot_color(fg)
|
||||
end
|
||||
end
|
||||
|
||||
function fg_color_sp(plotattributes::KW)
|
||||
fgsp = default(plotattributes, :foreground_color_subplot)
|
||||
function fg_color_sp(plotattributes::AKW)
|
||||
fgsp = get(plotattributes, :foreground_color_subplot, :match)
|
||||
if fg == :match
|
||||
fg_color(plotattributes)
|
||||
else
|
||||
@@ -1364,13 +1417,13 @@ end
|
||||
|
||||
|
||||
# update attr from an input dictionary
|
||||
function _update_plot_args(plt::Plot, plotattributes_in::KW)
|
||||
function _update_plot_args(plt::Plot, plotattributes_in::AKW)
|
||||
for (k,v) in _plot_defaults
|
||||
slice_arg!(plotattributes_in, plt.attr, k, v, 1, true)
|
||||
slice_arg!(plotattributes_in, plt.attr, k, 1, true)
|
||||
end
|
||||
|
||||
# handle colors
|
||||
plotattributes= plt.attr
|
||||
plotattributes= plt.attr
|
||||
plt[:background_color] = plot_color(plotattributes[:background_color])
|
||||
plt[:foreground_color] = fg_color(plotattributes)
|
||||
# bg = plot_color(plt.attr[:background_color])
|
||||
@@ -1417,7 +1470,7 @@ function _update_subplot_colors(sp::Subplot)
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::KW, letter::Symbol, subplot_index::Int)
|
||||
function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::AKW, letter::Symbol, subplot_index::Int)
|
||||
# get (maybe initialize) the axis
|
||||
axis = get_axis(sp, letter)
|
||||
|
||||
@@ -1433,13 +1486,10 @@ function _update_axis(plt::Plot, sp::Subplot, plotattributes_in::KW, letter::Sym
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis(axis::Axis, plotattributes_in::KW, letter::Symbol, subplot_index::Int)
|
||||
# grab magic args (for example `xaxis = (:flip, :log)`)
|
||||
args = wraptuple(get(plotattributes_in, Symbol(letter, :axis), ()))
|
||||
|
||||
function _update_axis(axis::Axis, plotattributes_in::AKW, letter::Symbol, subplot_index::Int)
|
||||
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
|
||||
kw = KW()
|
||||
for (k,v) in _axis_defaults
|
||||
for k in _all_axis_args
|
||||
# first get the args without the letter: `tickfont = font(10)`
|
||||
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
|
||||
if haskey(plotattributes_in, k)
|
||||
@@ -1454,7 +1504,7 @@ function _update_axis(axis::Axis, plotattributes_in::KW, letter::Symbol, subplot
|
||||
end
|
||||
|
||||
# update the axis
|
||||
attr!(axis, args...; kw...)
|
||||
attr!(axis; kw...)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1484,12 +1534,12 @@ function _update_axis_links(plt::Plot, axis::Axis, letter::Symbol)
|
||||
end
|
||||
|
||||
# update a subplots args and axes
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, plotattributes_in::KW, subplot_index::Int, remove_pair::Bool)
|
||||
anns = pop!(sp.attr, :annotations, [])
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, plotattributes_in, subplot_index::Int, remove_pair::Bool)
|
||||
anns = pop_kw!(sp.attr, :annotations)
|
||||
|
||||
# grab those args which apply to this subplot
|
||||
for (k,v) in _subplot_defaults
|
||||
slice_arg!(plotattributes_in, sp.attr, k, v, subplot_index, remove_pair)
|
||||
# # grab those args which apply to this subplot
|
||||
for k in keys(_subplot_defaults)
|
||||
slice_arg!(plotattributes_in, sp.attr, k, subplot_index, remove_pair)
|
||||
end
|
||||
|
||||
_update_subplot_periphery(sp, anns)
|
||||
@@ -1508,45 +1558,42 @@ function has_black_border_for_default(st::Symbol)
|
||||
like_histogram(st) || st in (:hexbin, :bar, :shape)
|
||||
end
|
||||
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
function getSeriesRGBColor(c, sp::Subplot, n::Int)
|
||||
# converts a symbol or string into a Colorant or ColorGradient
|
||||
# and assigns a color automatically
|
||||
function get_series_color(c, sp::Subplot, n::Int, seriestype)
|
||||
if c == :auto
|
||||
c = autopick(sp[:color_palette], n)
|
||||
c = like_surface(seriestype) ? cgrad() : autopick(sp[:color_palette], n)
|
||||
elseif isa(c, Int)
|
||||
c = autopick(sp[:color_palette], c)
|
||||
end
|
||||
plot_color(c)
|
||||
end
|
||||
|
||||
function getSeriesRGBColor(c::AbstractArray, sp::Subplot, n::Int)
|
||||
map(x->getSeriesRGBColor(x, sp, n), c)
|
||||
function get_series_color(c::AbstractArray, sp::Subplot, n::Int, seriestype)
|
||||
map(x->get_series_color(x, sp, n, seriestype), c)
|
||||
end
|
||||
|
||||
function ensure_gradient!(plotattributes::KW, csym::Symbol, asym::Symbol)
|
||||
function ensure_gradient!(plotattributes::AKW, csym::Symbol, asym::Symbol)
|
||||
if !isa(plotattributes[csym], ColorGradient)
|
||||
plotattributes[csym] = typeof(plotattributes[asym]) <: AbstractVector ? cgrad() : cgrad(alpha = plotattributes[asym])
|
||||
end
|
||||
end
|
||||
|
||||
function _replace_linewidth(plotattributes::KW)
|
||||
function _replace_linewidth(plotattributes::AKW)
|
||||
# get a good default linewidth... 0 for surface and heatmaps
|
||||
if get(plotattributes, :linewidth, :auto) == :auto
|
||||
if plotattributes[:linewidth] == :auto
|
||||
plotattributes[:linewidth] = (get(plotattributes, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_defaults!(plotattributes::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
# add default values to our dictionary, being careful not to delete what we just added!
|
||||
for (k,v) in _series_defaults
|
||||
slice_arg!(plotattributes, plotattributes, k, v, commandIndex, false)
|
||||
function _slice_series_args!(plotattributes::AKW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
for k in keys(_series_defaults)
|
||||
haskey(plotattributes, k) && slice_arg!(plotattributes, plotattributes, k, commandIndex, false)
|
||||
end
|
||||
|
||||
return plotattributes
|
||||
end
|
||||
|
||||
|
||||
function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
function _update_series_attributes!(plotattributes::AKW, plt::Plot, sp::Subplot)
|
||||
pkg = plt.backend
|
||||
globalIndex = plotattributes[:series_plotindex]
|
||||
plotIndex = _series_index(plotattributes, sp)
|
||||
@@ -1556,30 +1603,32 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
|
||||
# update alphas
|
||||
for asym in (:linealpha, :markeralpha, :fillalpha)
|
||||
if plotattributes[asym] == nothing
|
||||
if plotattributes[asym] === nothing
|
||||
plotattributes[asym] = plotattributes[:seriesalpha]
|
||||
end
|
||||
end
|
||||
if plotattributes[:markerstrokealpha] == nothing
|
||||
if plotattributes[:markerstrokealpha] === nothing
|
||||
plotattributes[:markerstrokealpha] = plotattributes[:markeralpha]
|
||||
end
|
||||
|
||||
# update series color
|
||||
plotattributes[:seriescolor] = getSeriesRGBColor(plotattributes[:seriescolor], sp, plotIndex)
|
||||
scolor = plotattributes[:seriescolor]
|
||||
stype = plotattributes[:seriestype]
|
||||
plotattributes[:seriescolor] = scolor = get_series_color(scolor, sp, plotIndex, stype)
|
||||
|
||||
# update other colors
|
||||
for s in (:line, :marker, :fill)
|
||||
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
|
||||
plotattributes[csym] = if plotattributes[csym] == :auto
|
||||
plot_color(if has_black_border_for_default(plotattributes[:seriestype]) && s == :line
|
||||
plot_color(if has_black_border_for_default(stype) && s == :line
|
||||
sp[:foreground_color_subplot]
|
||||
else
|
||||
plotattributes[:seriescolor]
|
||||
scolor
|
||||
end)
|
||||
elseif plotattributes[csym] == :match
|
||||
plot_color(plotattributes[:seriescolor])
|
||||
plot_color(scolor)
|
||||
else
|
||||
getSeriesRGBColor(plotattributes[csym], sp, plotIndex)
|
||||
get_series_color(plotattributes[csym], sp, plotIndex, stype)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1587,19 +1636,19 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
plotattributes[:markerstrokecolor] = if plotattributes[:markerstrokecolor] == :match
|
||||
plot_color(sp[:foreground_color_subplot])
|
||||
elseif plotattributes[:markerstrokecolor] == :auto
|
||||
getSeriesRGBColor(plotattributes[:markercolor], sp, plotIndex)
|
||||
get_series_color(plotattributes[:markercolor], sp, plotIndex, stype)
|
||||
else
|
||||
getSeriesRGBColor(plotattributes[:markerstrokecolor], sp, plotIndex)
|
||||
get_series_color(plotattributes[:markerstrokecolor], sp, plotIndex, stype)
|
||||
end
|
||||
|
||||
# if marker_z, fill_z or line_z are set, ensure we have a gradient
|
||||
if plotattributes[:marker_z] != nothing
|
||||
if plotattributes[:marker_z] !== nothing
|
||||
ensure_gradient!(plotattributes, :markercolor, :markeralpha)
|
||||
end
|
||||
if plotattributes[:line_z] != nothing
|
||||
if plotattributes[:line_z] !== nothing
|
||||
ensure_gradient!(plotattributes, :linecolor, :linealpha)
|
||||
end
|
||||
if plotattributes[:fill_z] != nothing
|
||||
if plotattributes[:fill_z] !== nothing
|
||||
ensure_gradient!(plotattributes, :fillcolor, :fillalpha)
|
||||
end
|
||||
|
||||
@@ -1614,6 +1663,7 @@ function _update_series_attributes!(plotattributes::KW, plt::Plot, sp::Subplot)
|
||||
# set label
|
||||
label = plotattributes[:label]
|
||||
label = (label == "AUTO" ? "y$globalIndex" : label)
|
||||
label = label in (:none, nothing, false) ? "" : label
|
||||
plotattributes[:label] = label
|
||||
|
||||
_replace_linewidth(plotattributes)
|
||||
|
||||
+394
-124
@@ -7,29 +7,20 @@
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
# init with values from _plot_defaults
|
||||
plotattributes = 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]
|
||||
plotattributes[k] = (lv == :match ? v : lv)
|
||||
end
|
||||
|
||||
# merge!(plotattributes, _axis_defaults)
|
||||
plotattributes[:discrete_values] = []
|
||||
attr = Attr(explicit, _axis_defaults_byletter[letter])
|
||||
|
||||
# update the defaults
|
||||
attr!(Axis([sp], plotattributes), args...; kw...)
|
||||
attr!(Axis([sp], attr), args...; kw...)
|
||||
end
|
||||
|
||||
function get_axis(sp::Subplot, letter::Symbol)
|
||||
@@ -41,7 +32,7 @@ function get_axis(sp::Subplot, letter::Symbol)
|
||||
end::Axis
|
||||
end
|
||||
|
||||
function process_axis_arg!(plotattributes::KW, arg, letter = "")
|
||||
function process_axis_arg!(plotattributes::AKW, arg, letter = "")
|
||||
T = typeof(arg)
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
@@ -67,7 +58,7 @@ function process_axis_arg!(plotattributes::KW, arg, letter = "")
|
||||
elseif T <: AVec
|
||||
plotattributes[Symbol(letter,:ticks)] = arg
|
||||
|
||||
elseif arg == nothing
|
||||
elseif arg === nothing
|
||||
plotattributes[Symbol(letter,:ticks)] = []
|
||||
|
||||
elseif T <: Bool || arg in _allShowaxisArgs
|
||||
@@ -93,6 +84,9 @@ function attr!(axis::Axis, args...; kw...)
|
||||
process_axis_arg!(plotattributes, arg)
|
||||
end
|
||||
|
||||
# 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(plotattributes, k)
|
||||
@@ -117,7 +111,7 @@ end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Base.show(io::IO, axis::Axis) = dumpdict(axis.plotattributes, "Axis", true)
|
||||
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)
|
||||
@@ -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);
|
||||
@@ -218,6 +212,8 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
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)
|
||||
@@ -238,9 +234,9 @@ 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
|
||||
@@ -251,7 +247,7 @@ function get_ticks(axis::Axis)
|
||||
# discrete ticks...
|
||||
n = length(dvals)
|
||||
rng = if ticks == :auto
|
||||
Int[round(Int,i) for i in range(1, stop=n, length=15)]
|
||||
Int[round(Int,i) for i in range(1, stop=n, length=min(n,15))]
|
||||
else # if ticks == :all
|
||||
1:n
|
||||
end
|
||||
@@ -261,7 +257,7 @@ function get_ticks(axis::Axis)
|
||||
(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}
|
||||
if !isempty(dvals) && typeof(ticks) <: Int
|
||||
@@ -269,7 +265,7 @@ function get_ticks(axis::Axis)
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
else
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
optimal_ticks_and_labels(sp, axis, ticks)
|
||||
end
|
||||
elseif typeof(ticks) <: NTuple{2, Any}
|
||||
# assuming we're passed (ticks, labels)
|
||||
@@ -286,16 +282,16 @@ _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(axis,ticks)
|
||||
axis[:minorticks] in (nothing, false) && !axis[:minorgrid] && return nothing
|
||||
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(axis)
|
||||
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] == :none
|
||||
elseif axis[:scale] in (:none, :identity)
|
||||
ratio = 1
|
||||
else
|
||||
return nothing
|
||||
@@ -309,7 +305,9 @@ function get_minor_ticks(axis,ticks)
|
||||
minorticks = typeof(ticks[1])[]
|
||||
for (i,hi) in enumerate(ticks[2:end])
|
||||
lo = ticks[i]
|
||||
append!(minorticks,collect(lo + (hi-lo)/n :(hi-lo)/n: hi - (hi-lo)/2n))
|
||||
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
|
||||
@@ -357,7 +355,7 @@ function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
function expand_extrema!(sp::Subplot, plotattributes::AKW)
|
||||
vert = isvertical(plotattributes)
|
||||
|
||||
# first expand for the data
|
||||
@@ -382,7 +380,7 @@ function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
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)
|
||||
@@ -399,10 +397,10 @@ function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
|
||||
# expand for fillrange
|
||||
fr = plotattributes[:fillrange]
|
||||
if fr == nothing && plotattributes[:seriestype] == :bar
|
||||
if fr === nothing && plotattributes[:seriestype] == :bar
|
||||
fr = 0.0
|
||||
end
|
||||
if fr != nothing && !all3D(plotattributes)
|
||||
if fr !== nothing && !all3D(plotattributes)
|
||||
axis = sp.attr[vert ? :yaxis : :xaxis]
|
||||
if typeof(fr) <: Tuple
|
||||
for fri in fr
|
||||
@@ -419,7 +417,7 @@ function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
data = plotattributes[dsym]
|
||||
|
||||
bw = plotattributes[:bar_width]
|
||||
if bw == nothing
|
||||
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)]
|
||||
@@ -459,7 +457,7 @@ const _widen_seriestypes = (:line, :path, :steppre, :steppost, :sticks, :scatter
|
||||
|
||||
function default_should_widen(axis::Axis)
|
||||
should_widen = false
|
||||
if !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.plotattributes[:seriestype] in _widen_seriestypes
|
||||
@@ -479,11 +477,13 @@ function round_limits(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
|
||||
@@ -497,12 +497,12 @@ 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
|
||||
@@ -513,6 +513,32 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
|
||||
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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -545,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
|
||||
@@ -556,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
|
||||
@@ -586,12 +612,12 @@ 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)
|
||||
xminorticks = get_minor_ticks(xaxis,xticks)
|
||||
yminorticks = get_minor_ticks(yaxis,yticks)
|
||||
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)
|
||||
@@ -605,126 +631,370 @@ function axis_drawing_info(sp::Subplot)
|
||||
|
||||
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
|
||||
end
|
||||
if !(xaxis[:minorticks] in (nothing, false)) || xaxis[:minorgrid]
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
ticks_in = xaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(ymin) + 0.01 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t2 = invf(f(ymax) - 0.01 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.01 * (f(ymax) - f(ymin)) * ticks_in)
|
||||
|
||||
for xminortick in xminorticks
|
||||
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, (xminortick, tick_start), (xminortick, tick_stop)) # bottom tick
|
||||
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
|
||||
# sp[:draw_axes_border] && push!(xaxis_segs, (xtick, ymax), (xtick, t2)) # top tick
|
||||
xaxis[:minorgrid] && push!(xminorgrid_segs, (xminortick, ymin), (xminortick, ymax)) # vertical grid
|
||||
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
|
||||
end
|
||||
if !(yaxis[:minorticks] in (nothing, false)) || yaxis[:minorgrid]
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
ticks_in = yaxis[:tick_direction] == :out ? -1 : 1
|
||||
t1 = invf(f(xmin) + 0.01 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t2 = invf(f(xmax) - 0.01 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
t3 = invf(f(0) + 0.01 * (f(xmax) - f(xmin)) * ticks_in)
|
||||
|
||||
for ytick in yminorticks
|
||||
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
|
||||
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
|
||||
# sp[:draw_axes_border] && push!(yaxis_segs, (xmax, ytick), (t2, ytick)) # right tick
|
||||
yaxis[:minorgrid] && push!(yminorgrid_segs, (xmin, ytick), (xmax, ytick)) # horizontal grid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
+185
-113
@@ -7,16 +7,20 @@ const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
|
||||
const _backends = Symbol[]
|
||||
const _initialized_backends = Set{Symbol}()
|
||||
const _default_backends = (:none, :gr, :plotly)
|
||||
const _backendPackage = Dict{Symbol, Symbol}()
|
||||
|
||||
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")
|
||||
backend_package(pkg::Symbol) = pkg in _default_backends ? :Plots : Symbol("Plots", _backendPackage[pkg])
|
||||
backend_package_name(sym::Symbol) = sym in _default_backends ? :Plots : _backendPackage[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)
|
||||
package_str = string(s)
|
||||
@@ -26,14 +30,13 @@ macro init_backend(s)
|
||||
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)
|
||||
_backendPackage[Symbol($str)] = Symbol($package_str)
|
||||
# include("backends/" * $str * ".jl")
|
||||
_backend_packages[Symbol($str)] = Symbol($package_str)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -42,12 +45,6 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
# don't do anything as a default
|
||||
_create_backend_figure(plt::Plot) = nothing
|
||||
_prepare_plot_object(plt::Plot) = nothing
|
||||
@@ -77,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
|
||||
@@ -108,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]
|
||||
@@ -138,33 +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 != ""
|
||||
sym = Symbol(lowercase(env_default))
|
||||
if sym in _backends
|
||||
if sym in _initialized_backends
|
||||
return backend(sym)
|
||||
else
|
||||
@warn("You have set `PLOTS_DEFAULT_BACKEND=$env_default` but `$(backend_package_name(sym))` is not loaded.")
|
||||
end
|
||||
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")
|
||||
# if pkgstr in keys(Pkg.installed())
|
||||
# return backend(Symbol(lowercase(pkgstr)))
|
||||
# end
|
||||
# end
|
||||
|
||||
# the default if nothing else is installed
|
||||
backend(:gr)
|
||||
end
|
||||
|
||||
|
||||
@@ -174,13 +160,11 @@ end
|
||||
Returns the current plotting package name. Initializes package on first call.
|
||||
"""
|
||||
function backend()
|
||||
if CURRENT_BACKEND.sym == :none
|
||||
_pick_default_backend()
|
||||
end
|
||||
|
||||
global CURRENT_BACKEND
|
||||
if CURRENT_BACKEND.sym == :none
|
||||
pickDefaultBackend()
|
||||
end
|
||||
|
||||
CURRENT_BACKEND.pkg
|
||||
CURRENT_BACKEND.pkg
|
||||
end
|
||||
|
||||
"""
|
||||
@@ -188,20 +172,13 @@ Set the plot backend.
|
||||
"""
|
||||
function backend(pkg::AbstractBackend)
|
||||
sym = backend_name(pkg)
|
||||
if sym in _initialized_backends
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
else
|
||||
# try
|
||||
_initialize_backend(pkg)
|
||||
push!(_initialized_backends, sym)
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
# catch
|
||||
# add_backend(sym)
|
||||
# end
|
||||
if !(sym in _initialized_backends)
|
||||
_initialize_backend(pkg)
|
||||
push!(_initialized_backends, sym)
|
||||
end
|
||||
backend()
|
||||
CURRENT_BACKEND.sym = sym
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
pkg
|
||||
end
|
||||
|
||||
function backend(sym::Symbol)
|
||||
@@ -209,8 +186,8 @@ function backend(sym::Symbol)
|
||||
backend(_backend_instance(sym))
|
||||
else
|
||||
@warn("`:$sym` is not a supported backend.")
|
||||
backend()
|
||||
end
|
||||
backend()
|
||||
end
|
||||
|
||||
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse, :glvisualize]
|
||||
@@ -251,7 +228,7 @@ const _base_supported_args = [
|
||||
:subplot_index,
|
||||
:discrete_values,
|
||||
:projection,
|
||||
|
||||
:show_empty_bins
|
||||
]
|
||||
|
||||
function merge_with_base_supported(v::AVec)
|
||||
@@ -274,6 +251,7 @@ end
|
||||
@init_backend PlotlyJS
|
||||
@init_backend GR
|
||||
@init_backend PGFPlots
|
||||
@init_backend PGFPlotsX
|
||||
@init_backend InspectDR
|
||||
@init_backend HDF5
|
||||
|
||||
@@ -316,13 +294,7 @@ function _initialize_backend(pkg::AbstractBackend)
|
||||
end
|
||||
end
|
||||
|
||||
function add_backend_string(pkg::AbstractBackend)
|
||||
sym = backend_package_name(pkg)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("$sym")
|
||||
"""
|
||||
end
|
||||
_initialize_backend(pkg::GRBackend) = nothing
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# gr
|
||||
@@ -352,7 +324,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign,
|
||||
:guidefontrotation, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
@@ -371,7 +343,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
const _gr_seriestype = [
|
||||
:path, :scatter, :straightline,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :path3d, :scatter3d, :surface, :wireframe,
|
||||
:contour, :path3d, :scatter3d, :surface, :wireframe, :volume,
|
||||
:shape
|
||||
]
|
||||
const _gr_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
@@ -379,16 +351,19 @@ const _gr_marker = _allMarkers
|
||||
const _gr_scale = [:identity, :log10]
|
||||
is_marker_supported(::GRBackend, shape::Shape) = true
|
||||
|
||||
function add_backend_string(::GRBackend)
|
||||
"""
|
||||
Pkg.add("GR")
|
||||
Pkg.build("GR")
|
||||
"""
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -413,7 +388,7 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :gridalpha, :gridlinewidth,
|
||||
:legend, :colorbar, :colorbar_title,
|
||||
:legend, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :fill_z, :line_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
@@ -444,17 +419,11 @@ const _plotly_marker = [
|
||||
]
|
||||
const _plotly_scale = [:identity, :log10]
|
||||
|
||||
defaultOutputFormat(plt::Plot{Plots.PlotlyBackend}) = "html"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# pgfplots
|
||||
|
||||
function add_backend_string(::PGFPlotsBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("PGFPlots")
|
||||
Pkg.build("PGFPlots")
|
||||
"""
|
||||
end
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend,
|
||||
@@ -492,29 +461,19 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
])
|
||||
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_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)
|
||||
sym = backend_package_name(pkg)
|
||||
@eval Main begin
|
||||
import PlotlyJS, ORCA
|
||||
export PlotlyJS
|
||||
end
|
||||
end
|
||||
|
||||
function add_backend_string(::PlotlyJSBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add(["PlotlyJS", "Blink", "ORCA"])
|
||||
import Blink
|
||||
Blink.AtomShell.install()
|
||||
"""
|
||||
end
|
||||
|
||||
const _plotlyjs_attr = _plotly_attr
|
||||
const _plotlyjs_seriestype = _plotly_seriestype
|
||||
const _plotlyjs_style = _plotly_style
|
||||
@@ -535,16 +494,6 @@ function _initialize_backend(::PyPlotBackend)
|
||||
end
|
||||
end
|
||||
|
||||
function add_backend_string(::PyPlotBackend)
|
||||
"""
|
||||
using Pkg
|
||||
withenv("PYTHON" => "") do
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
const _pyplot_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
@@ -564,7 +513,7 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
@@ -577,7 +526,6 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
:colorbar_title,
|
||||
:stride,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
@@ -598,14 +546,6 @@ const _pyplot_scale = [:identity, :ln, :log2, :log10]
|
||||
# ------------------------------------------------------------------------------
|
||||
# unicodeplots
|
||||
|
||||
function add_backend_string(::UnicodePlotsBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("UnicodePlots")
|
||||
Pkg.build("UnicodePlots")
|
||||
"""
|
||||
end
|
||||
|
||||
const _unicodeplots_attr = merge_with_base_supported([
|
||||
:label,
|
||||
:legend,
|
||||
@@ -728,3 +668,135 @@ const _inspectdr_marker = Symbol[
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
+936
-354
File diff suppressed because it is too large
Load Diff
+66
-36
@@ -83,6 +83,7 @@ if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
"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,
|
||||
@@ -109,7 +110,7 @@ _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].plotattributes, v.plotattributes)
|
||||
@@ -263,6 +264,15 @@ 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
|
||||
@@ -295,7 +305,8 @@ function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
||||
end
|
||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::Dict)
|
||||
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::AbstractRange)
|
||||
@@ -376,13 +387,22 @@ function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||
_hdf5plot_writetype(grp, Subplot)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, plotattributes::Dict)
|
||||
|
||||
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:
|
||||
# ----------------------------------------------------------------
|
||||
@@ -445,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")
|
||||
@@ -469,7 +483,7 @@ 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
|
||||
@@ -488,18 +502,18 @@ 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{PlotText}, 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}, dtid)
|
||||
function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations})
|
||||
grp = HDF5.g_open(grp, k)
|
||||
|
||||
strs = _hdf5plot_read(grp, "strs")
|
||||
@@ -508,29 +522,29 @@ function _hdf5plot_read(grp, k::String, T::Type{SeriesAnnotations}, dtid)
|
||||
scalefactor = _hdf5plot_read(grp, "scalefactor")
|
||||
return SeriesAnnotations(strs, font, baseshape, scalefactor)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Shape}, dtid)
|
||||
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}, dtid)
|
||||
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")
|
||||
@@ -544,22 +558,36 @@ 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{Surface}, 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}, dtid)
|
||||
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
|
||||
@@ -567,7 +595,7 @@ function _hdf5plot_read(grp, k::String)
|
||||
end
|
||||
|
||||
#Read in values in group to populate plotattributes:
|
||||
function _hdf5plot_read(grp, plotattributes::Dict)
|
||||
function _hdf5plot_readattr(grp, plotattributes::AbstractDict)
|
||||
gnames = names(grp)
|
||||
for k in gnames
|
||||
try
|
||||
@@ -581,6 +609,8 @@ function _hdf5plot_read(grp, plotattributes::Dict)
|
||||
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:
|
||||
# ----------------------------------------------------------------
|
||||
@@ -593,17 +623,17 @@ function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
||||
|
||||
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].plotattributes, 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"))
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
_hdf5_merge!(sp.attr, kwlist)
|
||||
attr = Attr(KW(), _plot_defaults)
|
||||
_hdf5plot_read(grp, attr)
|
||||
_hdf5_merge!(sp.attr, attr)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
@@ -121,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:
|
||||
@@ -294,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))
|
||||
|
||||
@@ -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
|
||||
+46
-29
@@ -26,7 +26,8 @@ const _pgfplots_markers = KW(
|
||||
:star6 => "asterisk",
|
||||
:diamond => "diamond*",
|
||||
:pentagon => "pentagon*",
|
||||
:hline => "-"
|
||||
:hline => "-",
|
||||
:vline => "|"
|
||||
)
|
||||
|
||||
const _pgfplots_legend_pos = KW(
|
||||
@@ -95,7 +96,7 @@ 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
|
||||
if fa !== nothing
|
||||
a = fa
|
||||
end
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
@@ -127,16 +128,18 @@ 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)))
|
||||
"""
|
||||
mark = $(get(_pgfplots_markers, shape, "*")),
|
||||
mark size = $(pgf_thickness_scaling(plotattributes) * 0.5 * _cycle(plotattributes[:markersize], i)),
|
||||
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"))
|
||||
}"""
|
||||
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)
|
||||
@@ -170,18 +173,18 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
elseif ispolar(sp)
|
||||
theta, r = filter_radial_data(plotattributes[:x], plotattributes[:y], axis_limits(sp[:yaxis]))
|
||||
theta, r = plotattributes[:x], plotattributes[:y]
|
||||
rad2deg.(theta), r
|
||||
else
|
||||
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)
|
||||
# args = map(a -> if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
# collect(a)
|
||||
# else
|
||||
# a
|
||||
# end, args)
|
||||
|
||||
if st in (:contour, :histogram2d)
|
||||
style = []
|
||||
@@ -215,12 +218,12 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
|
||||
# add to legend?
|
||||
if i == 1 && sp[:legend] != :none && should_add_to_legend(series)
|
||||
if plotattributes[:fillrange] != nothing
|
||||
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
|
||||
if st == :shape # || plotattributes[:fillrange] !== nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
end
|
||||
@@ -237,7 +240,7 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
kw[:style] = join(style, ',')
|
||||
|
||||
# add fillrange
|
||||
if series[:fillrange] != nothing && st != :shape
|
||||
if series[:fillrange] !== nothing && st != :shape
|
||||
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
|
||||
end
|
||||
|
||||
@@ -360,13 +363,13 @@ 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,","), "}"))
|
||||
@@ -398,8 +401,12 @@ function pgf_axis(sp::Subplot, letter)
|
||||
# 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
|
||||
@@ -463,7 +470,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
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
|
||||
|
||||
@@ -471,8 +478,18 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
if haskey(_pgfplots_legend_pos, legpos)
|
||||
kw[:legendPos] = _pgfplots_legend_pos[legpos]
|
||||
end
|
||||
cstr, a = pgf_color(plot_color(sp[:background_color_legend]))
|
||||
push!(style, string("legend style = {", pgf_linestyle(pgf_thickness_scaling(sp), sp[:foreground_color_legend], 1.0, "solid"), ",", "fill = $cstr,", "font = ", pgf_font(sp[:legendfontsize], pgf_thickness_scaling(sp)), "}"))
|
||||
cstr, bg_alpha = pgf_color(plot_color(sp[:background_color_legend]))
|
||||
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
|
||||
|
||||
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}"
|
||||
@@ -563,7 +580,7 @@ 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)
|
||||
PGFPlots.save(fn, backend_object(plt), include_preamble=plt.attr[:tex_output_standalone])
|
||||
write(io, read(open(fn), String))
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+64
-51
@@ -26,7 +26,6 @@ const plotly_remote_file_path = "https://cdn.plot.ly/plotly-latest.min.js"
|
||||
# end
|
||||
using UUIDs
|
||||
|
||||
push!(_initialized_backends, :plotly)
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const _plotly_legend_pos = KW(
|
||||
@@ -109,13 +108,13 @@ function shrink_by(lo, sz, ratio)
|
||||
end
|
||||
|
||||
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
aspect_ratio = get_aspect_ratio(sp)
|
||||
if aspect_ratio != :none
|
||||
if aspect_ratio == :equal
|
||||
aspect_ratio = 1.0
|
||||
end
|
||||
xmin,xmax = axis_limits(sp[:xaxis])
|
||||
ymin,ymax = axis_limits(sp[:yaxis])
|
||||
xmin,xmax = axis_limits(sp, :x)
|
||||
ymin,ymax = axis_limits(sp, :y)
|
||||
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
|
||||
parea_ratio = width(plotarea) / height(plotarea)
|
||||
if want_ratio > parea_ratio
|
||||
@@ -174,7 +173,7 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
||||
|
||||
ax[:tickangle] = -axis[:rotation]
|
||||
ax[:type] = plotly_scale(axis[:scale])
|
||||
lims = axis_limits(axis)
|
||||
lims = axis_limits(sp, letter)
|
||||
|
||||
if axis[:ticks] != :native || axis[:lims] != :auto
|
||||
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
||||
@@ -186,14 +185,9 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
||||
ax[:tickcolor] = framestyle in (:zerolines, :grid) || !axis[:showaxis] ? rgba_string(invisible()) : rgb_string(axis[:foreground_color_axis])
|
||||
ax[:linecolor] = rgba_string(axis[:foreground_color_axis])
|
||||
|
||||
# flip
|
||||
if axis[:flip]
|
||||
ax[:range] = reverse(ax[:range])
|
||||
end
|
||||
|
||||
# ticks
|
||||
if axis[:ticks] != :native
|
||||
ticks = get_ticks(axis)
|
||||
ticks = get_ticks(sp, axis)
|
||||
ttype = ticksType(ticks)
|
||||
if ttype == :ticks
|
||||
ax[:tickmode] = "array"
|
||||
@@ -208,20 +202,24 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot)
|
||||
ax[:showgrid] = false
|
||||
end
|
||||
|
||||
# flip
|
||||
if axis[:flip]
|
||||
ax[:range] = reverse(ax[:range])
|
||||
end
|
||||
|
||||
ax
|
||||
end
|
||||
|
||||
function plotly_polaraxis(axis::Axis)
|
||||
function plotly_polaraxis(sp::Subplot, axis::Axis)
|
||||
ax = KW(
|
||||
:visible => axis[:showaxis],
|
||||
:showline => axis[:grid],
|
||||
)
|
||||
|
||||
if axis[:letter] == :x
|
||||
ax[:range] = rad2deg.(axis_limits(axis))
|
||||
ax[:range] = rad2deg.(axis_limits(sp, :x))
|
||||
else
|
||||
ax[:range] = axis_limits(axis)
|
||||
ax[:range] = axis_limits(sp, :y)
|
||||
ax[:orientation] = -90
|
||||
end
|
||||
|
||||
@@ -284,8 +282,8 @@ function plotly_layout(plt::Plot)
|
||||
),
|
||||
)
|
||||
elseif ispolar(sp)
|
||||
plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp[:xaxis])
|
||||
plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp[:yaxis])
|
||||
plotattributes_out[Symbol("angularaxis$(spidx)")] = plotly_polaraxis(sp, sp[:xaxis])
|
||||
plotattributes_out[Symbol("radialaxis$(spidx)")] = plotly_polaraxis(sp, sp[:yaxis])
|
||||
else
|
||||
plotattributes_out[Symbol("xaxis$(x_idx)")] = plotly_axis(plt, sp[:xaxis], sp)
|
||||
# don't allow yaxis to be reupdated/reanchored in a linked subplot
|
||||
@@ -348,14 +346,14 @@ function plotly_layout(plt::Plot)
|
||||
end
|
||||
|
||||
function plotly_layout_json(plt::Plot)
|
||||
JSON.json(plotly_layout(plt))
|
||||
JSON.json(plotly_layout(plt), 4)
|
||||
end
|
||||
|
||||
|
||||
function plotly_colorscale(grad::ColorGradient, α)
|
||||
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in 1:length(grad.colors)]
|
||||
[[grad.values[i], rgba_string(plot_color(grad.colors[i], α))] for i in eachindex(grad.colors)]
|
||||
end
|
||||
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
|
||||
plotly_colorscale(c::Colorant,α) = plotly_colorscale(_as_gradient(c),α)
|
||||
function plotly_colorscale(c::AbstractVector{<:RGBA}, α)
|
||||
if length(c) == 1
|
||||
return [[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
|
||||
@@ -394,7 +392,7 @@ end
|
||||
# we split by NaNs and then construct/destruct the shapes to get the closed coords
|
||||
function plotly_close_shapes(x, y)
|
||||
xs, ys = nansplit(x), nansplit(y)
|
||||
for i=1:length(xs)
|
||||
for i=eachindex(xs)
|
||||
shape = Shape(xs[i], ys[i])
|
||||
xs[i], ys[i] = coords(shape)
|
||||
end
|
||||
@@ -404,7 +402,7 @@ end
|
||||
function plotly_data(series::Series, letter::Symbol, data)
|
||||
axis = series[:subplot][Symbol(letter, :axis)]
|
||||
|
||||
data = if axis[:ticks] == :native && data != nothing
|
||||
data = if axis[:ticks] == :native && data !== nothing
|
||||
plotly_native_data(axis, data)
|
||||
else
|
||||
data
|
||||
@@ -416,7 +414,8 @@ function plotly_data(series::Series, letter::Symbol, data)
|
||||
plotly_data(data)
|
||||
end
|
||||
end
|
||||
plotly_data(v) = v != nothing ? collect(v) : v
|
||||
plotly_data(v) = v !== nothing ? collect(v) : v
|
||||
plotly_data(v::AbstractArray) = v
|
||||
plotly_data(surf::Surface) = surf.surf
|
||||
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
|
||||
|
||||
@@ -437,7 +436,7 @@ plotly_native_data(axis::Axis, a::Surface) = Surface(plotly_native_data(axis, a.
|
||||
|
||||
function plotly_convert_to_datetime(x::AbstractArray, formatter::Function)
|
||||
if formatter == datetimeformatter
|
||||
map(xi -> replace(formatter(xi), "T", " "), x)
|
||||
map(xi -> replace(formatter(xi), "T" => " "), x)
|
||||
elseif formatter == dateformatter
|
||||
map(xi -> string(formatter(xi), " 00:00:00"), x)
|
||||
elseif formatter == timeformatter
|
||||
@@ -455,7 +454,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
st = series[:seriestype]
|
||||
|
||||
sp = series[:subplot]
|
||||
clims = get_clims(sp)
|
||||
clims = get_clims(sp, series)
|
||||
|
||||
if st == :shape
|
||||
return plotly_series_shapes(plt, series, clims)
|
||||
@@ -470,7 +469,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
plotattributes_out[:showlegend] = should_add_to_legend(series)
|
||||
|
||||
if st == :straightline
|
||||
x, y = straightline_data(series)
|
||||
x, y = straightline_data(series, 100)
|
||||
z = series[:z]
|
||||
else
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
@@ -507,13 +506,13 @@ function plotly_series(plt::Plot, series::Series)
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
|
||||
elseif st == :contour
|
||||
filled = isfilledcontour(series)
|
||||
plotattributes_out[:type] = "contour"
|
||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
|
||||
# plotattributes_out[:showscale] = series[:colorbar] != :none
|
||||
plotattributes_out[:ncontours] = series[:levels]
|
||||
plotattributes_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||
plotattributes_out[:ncontours] = series[:levels] + 2
|
||||
plotattributes_out[:contours] = KW(:coloring => filled ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
plotattributes_out[:showscale] = hascolorbar(sp) && hascolorbar(series)
|
||||
|
||||
elseif st in (:surface, :wireframe)
|
||||
plotattributes_out[:type] = "surface"
|
||||
@@ -530,7 +529,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
else
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
plotattributes_out[:opacity] = series[:fillalpha]
|
||||
if series[:fill_z] != nothing
|
||||
if series[:fill_z] !== nothing
|
||||
plotattributes_out[:surfacecolor] = plotly_surface_data(series, series[:fill_z])
|
||||
end
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
@@ -585,7 +584,7 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
)
|
||||
|
||||
x, y = (plotly_data(series, letter, data)
|
||||
for (letter, data) in zip((:x, :y), shape_data(series))
|
||||
for (letter, data) in zip((:x, :y), shape_data(series, 100))
|
||||
)
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
@@ -612,11 +611,11 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
plotly_hover!(plotattributes_out, _cycle(series[:hover], i))
|
||||
plotattributes_outs[i] = plotattributes_out
|
||||
end
|
||||
if series[:fill_z] != nothing
|
||||
if series[:fill_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
|
||||
elseif series[:line_z] != nothing
|
||||
elseif series[:line_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line))
|
||||
elseif series[:marker_z] != nothing
|
||||
elseif series[:marker_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
|
||||
end
|
||||
plotattributes_outs
|
||||
@@ -632,11 +631,9 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
segments = iter_segments(series)
|
||||
plotattributes_outs = Vector{KW}(undef, (hasfillrange ? 2 : 1 ) * length(segments))
|
||||
plotattributes_outs = fill(KW(), (hasfillrange ? 2 : 1 ) * length(segments))
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
!isscatter && length(rng) < 2 && continue
|
||||
|
||||
plotattributes_out = deepcopy(plotattributes_base)
|
||||
plotattributes_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
|
||||
plotattributes_out[:legendgroup] = series[:label]
|
||||
@@ -736,11 +733,11 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
end
|
||||
end
|
||||
|
||||
if series[:line_z] != nothing
|
||||
if series[:line_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :line))
|
||||
elseif series[:fill_z] != nothing
|
||||
elseif series[:fill_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
|
||||
elseif series[:marker_z] != nothing
|
||||
elseif series[:marker_z] !== nothing
|
||||
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :marker))
|
||||
end
|
||||
|
||||
@@ -775,7 +772,7 @@ end
|
||||
function plotly_polar!(plotattributes_out::KW, series::Series)
|
||||
# convert polar plots x/y to theta/radius
|
||||
if ispolar(series[:subplot])
|
||||
theta, r = filter_radial_data(pop!(plotattributes_out, :x), pop!(plotattributes_out, :y), axis_limits(series[:subplot][:yaxis]))
|
||||
theta, r = pop!(plotattributes_out, :x), pop!(plotattributes_out, :y)
|
||||
plotattributes_out[:t] = rad2deg.(theta)
|
||||
plotattributes_out[:r] = r
|
||||
end
|
||||
@@ -783,9 +780,9 @@ end
|
||||
|
||||
function plotly_hover!(plotattributes_out::KW, hover)
|
||||
# hover text
|
||||
if hover in (:none, false)
|
||||
if hover === nothing || all(in([:none, false]), hover)
|
||||
plotattributes_out[:hoverinfo] = "none"
|
||||
elseif hover != nothing
|
||||
elseif any(!isnothing, hover)
|
||||
plotattributes_out[:hoverinfo] = "text"
|
||||
plotattributes_out[:text] = hover
|
||||
end
|
||||
@@ -801,13 +798,16 @@ function plotly_series(plt::Plot)
|
||||
end
|
||||
|
||||
# get json string for a list of dictionaries, each representing the series params
|
||||
plotly_series_json(plt::Plot) = JSON.json(plotly_series(plt))
|
||||
plotly_series_json(plt::Plot) = JSON.json(plotly_series(plt), 4)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
html_head(plt::Plot{PlotlyBackend}) = plotly_html_head(plt)
|
||||
html_body(plt::Plot{PlotlyBackend}) = plotly_html_body(plt)
|
||||
|
||||
const ijulia_initialized = Ref(false)
|
||||
|
||||
function html_head(plt::Plot{PlotlyBackend})
|
||||
function plotly_html_head(plt::Plot)
|
||||
local_file = ("file://" * plotly_local_file_path)
|
||||
plotly = use_local_dependencies[] ? local_file : plotly_remote_file_path
|
||||
if isijulia() && !ijulia_initialized[]
|
||||
@@ -823,13 +823,11 @@ function html_head(plt::Plot{PlotlyBackend})
|
||||
""")
|
||||
ijulia_initialized[] = true
|
||||
end
|
||||
# IJulia just needs one initialization
|
||||
isijulia() && return ""
|
||||
return "<script src=$(repr(plotly))></script>"
|
||||
end
|
||||
|
||||
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||
if style == nothing
|
||||
function plotly_html_body(plt, style = nothing)
|
||||
if style === nothing
|
||||
w, h = plt[:size]
|
||||
style = "width:$(w)px;height:$(h)px;"
|
||||
end
|
||||
@@ -844,21 +842,36 @@ function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||
html
|
||||
end
|
||||
|
||||
function js_body(plt::Plot{PlotlyBackend}, uuid)
|
||||
function js_body(plt::Plot, uuid)
|
||||
js = """
|
||||
PLOT = document.getElementById('$(uuid)');
|
||||
Plotly.plot(PLOT, $(plotly_series_json(plt)), $(plotly_layout_json(plt)));
|
||||
"""
|
||||
end
|
||||
|
||||
function plotly_show_js(io::IO, plot::Plot)
|
||||
data = []
|
||||
for series in plot.series_list
|
||||
append!(data, plotly_series(plot, series))
|
||||
end
|
||||
layout = plotly_layout(plot)
|
||||
JSON.print(io, Dict(:data => data, :layout => layout))
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot{PlotlyBackend}) = true
|
||||
|
||||
function _show(io::IO, ::MIME"application/vnd.plotly.v1+json", plot::Plot{PlotlyBackend})
|
||||
plotly_show_js(io, plot)
|
||||
end
|
||||
|
||||
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
||||
write(io, html_head(plt) * html_body(plt))
|
||||
write(io, standalone_html(plt))
|
||||
end
|
||||
|
||||
|
||||
function _display(plt::Plot{PlotlyBackend})
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
|
||||
+34
-74
@@ -1,91 +1,49 @@
|
||||
# https://github.com/sglyon/PlotlyJS.jl
|
||||
|
||||
# https://github.com/spencerlyon2/PlotlyJS.jl
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), options = current().o.options)
|
||||
else
|
||||
PlotlyJS.plot()
|
||||
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
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
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
|
||||
const _plotlyjs_mimeformats = Dict(
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"image/svg+xml" => "svg",
|
||||
"image/eps" => "eps",
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
|
||||
kw = KW(xsym => (series.plotattributes[:x],), ysym => (series.plotattributes[: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(isequal(series), plt.series_list),
|
||||
kw
|
||||
)
|
||||
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)
|
||||
|
||||
html_head(plt::Plot{PlotlyJSBackend}) = plotly_html_head(plt)
|
||||
html_body(plt::Plot{PlotlyJSBackend}) = plotly_html_body(plt)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
_show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend}) = write(io, standalone_html(plt))
|
||||
|
||||
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 _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
|
||||
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
|
||||
_display(plt::Plot{PlotlyJSBackend}) = display(plotlyjs_syncplot(plt))
|
||||
|
||||
@require WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin
|
||||
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
||||
prepare_output(plt)
|
||||
WebIO.render(plt.o)
|
||||
plt_html = sprint(show, MIME("text/html"), plt)
|
||||
return WebIO.render(dom"div"(innerHTML=plt_html))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -94,3 +52,5 @@ function closeall(::PlotlyJSBackend)
|
||||
close(current().o)
|
||||
end
|
||||
end
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot{PlotlyJSBackend}) = true
|
||||
|
||||
+303
-183
File diff suppressed because it is too large
Load Diff
@@ -27,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]]
|
||||
@@ -92,7 +92,7 @@ end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, plotattributes::KW, addlegend::Bool, xlim, ylim)
|
||||
function addUnicodeSeries!(o, plotattributes, addlegend::Bool, xlim, ylim)
|
||||
# get the function, or special handling for step/bar/hist
|
||||
st = plotattributes[:seriestype]
|
||||
if st == :histogram2d
|
||||
@@ -116,7 +116,7 @@ function addUnicodeSeries!(o, plotattributes::KW, addlegend::Bool, xlim, ylim)
|
||||
x, y = if st == :straightline
|
||||
straightline_data(plotattributes)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
shape_data(plotattributes)
|
||||
else
|
||||
[collect(float(plotattributes[s])) for s in (:x, :y)]
|
||||
end
|
||||
|
||||
+68
-24
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
const P2 = StaticArrays.SVector{2,Float64}
|
||||
const P3 = StaticArrays.SVector{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))
|
||||
@@ -187,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
|
||||
@@ -202,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
|
||||
@@ -230,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
|
||||
@@ -256,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"
|
||||
@@ -301,6 +317,32 @@ function font(args...)
|
||||
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
|
||||
|
||||
Font(family, pointsize, halign, valign, rotation, color)
|
||||
end
|
||||
|
||||
@@ -344,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)
|
||||
@@ -486,7 +529,7 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
# end
|
||||
|
||||
# @show msw msh
|
||||
if anns != nothing && anns.baseshape != nothing
|
||||
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
|
||||
@@ -525,7 +568,7 @@ mutable struct EachAnn
|
||||
end
|
||||
|
||||
function Base.iterate(ea::EachAnn, i = 1)
|
||||
if ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
if ea.anns === nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
return nothing
|
||||
end
|
||||
|
||||
@@ -548,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
|
||||
@@ -569,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
|
||||
@@ -616,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)
|
||||
@@ -736,7 +783,7 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
"create a BezierCurve for plotting"
|
||||
mutable struct BezierCurve{T <: StaticArrays.SVector}
|
||||
mutable struct BezierCurve{T <: GeometryTypes.Point}
|
||||
control_points::Vector{T}
|
||||
end
|
||||
|
||||
@@ -749,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::StaticArrays.SVector{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, range(range..., stop=n, length=50))
|
||||
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]
|
||||
|
||||
+972
-473
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
|
||||
+75
-94
@@ -1,120 +1,101 @@
|
||||
const use_local_dependencies = Ref(false)
|
||||
using REPL
|
||||
|
||||
|
||||
function _plots_defaults()
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
Dict{Symbol,Any}(Main.PLOTS_DEFAULTS)
|
||||
else
|
||||
Dict{Symbol,Any}()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function __init__()
|
||||
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
if haskey(Main.PLOTS_DEFAULTS, :theme)
|
||||
theme(Main.PLOTS_DEFAULTS[:theme])
|
||||
end
|
||||
for (k,v) in Main.PLOTS_DEFAULTS
|
||||
k == :theme || default(k, v)
|
||||
end
|
||||
user_defaults = _plots_defaults()
|
||||
if haskey(user_defaults, :theme)
|
||||
theme(user_defaults[:theme])
|
||||
end
|
||||
pushdisplay(PlotsDisplay())
|
||||
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
|
||||
if PlotDisplay() in Base.Multimedia.displays
|
||||
while PlotsDisplay() in Base.Multimedia.displays
|
||||
popdisplay(PlotsDisplay())
|
||||
end
|
||||
pushdisplay(PlotsDisplay())
|
||||
insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, PlotsDisplay())
|
||||
end)
|
||||
|
||||
@require HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" include(joinpath(@__DIR__, "backends", "hdf5.jl"))
|
||||
@require InspectDR = "d0351b0e-4b05-5898-87b3-e2a8edfddd1d" include(joinpath(@__DIR__, "backends", "inspectdr.jl"))
|
||||
@require PGFPlots = "3b7a836e-365b-5785-a47d-02c71176b4aa" include(joinpath(@__DIR__, "backends", "pgfplots.jl"))
|
||||
@require PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" include(joinpath(@__DIR__, "backends", "plotlyjs.jl"))
|
||||
@require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" include(joinpath(@__DIR__, "backends", "pyplot.jl"))
|
||||
@require UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" include(joinpath(@__DIR__, "backends", "unicodeplots.jl"))
|
||||
@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
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
use_local = false
|
||||
@require IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" begin
|
||||
if IJulia.inited
|
||||
# IJulia is more stable with local file
|
||||
use_local = isfile(plotly_local_file_path)
|
||||
"""
|
||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||
_init_ijulia_plotting()
|
||||
|
||||
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 == :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
|
||||
_extra_mime_info!(plt, out)
|
||||
out
|
||||
end
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
IJulia.display_dict(plt::Plot) = _ijulia_display_dict(plt)
|
||||
end
|
||||
end
|
||||
|
||||
if haskey(ENV, "PLOTS_HOST_DEPENDENCY_LOCAL")
|
||||
use_local = ENV["PLOTS_HOST_DEPENDENCY_LOCAL"] == "true"
|
||||
use_local_dependencies[] = isfile(plotly_local_file_path) && use_local
|
||||
if use_local && !isfile(plotly_local_file_path)
|
||||
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
|
||||
use_local_dependencies[] = use_local_plotlyjs[]
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin
|
||||
PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot{<:PDFBackends})
|
||||
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
|
||||
_show(io::IO, mime::MIME"image/png", plt::Plot{<:PDFBackends}) = _show_pdfbackends(io, mime, plt)
|
||||
end
|
||||
end
|
||||
|
||||
+27
-10
@@ -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
|
||||
@@ -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
|
||||
@@ -464,12 +464,12 @@ end
|
||||
# constructors
|
||||
|
||||
# pass the layout arg through
|
||||
function layout_args(plotattributes::KW)
|
||||
layout_args(get(plotattributes, :layout, default(:layout)))
|
||||
function layout_args(plotattributes::AKW)
|
||||
layout_args(plotattributes[:layout])
|
||||
end
|
||||
|
||||
function layout_args(plotattributes::KW, n_override::Integer)
|
||||
layout, n = layout_args(get(plotattributes, :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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+29
-7
@@ -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
|
||||
@@ -191,7 +207,7 @@ end
|
||||
# for writing to io streams... first prepare, then callback
|
||||
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
|
||||
"application/eps", "application/pdf", "application/postscript",
|
||||
"application/x-tex")
|
||||
"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)
|
||||
@@ -199,9 +215,12 @@ for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml
|
||||
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)
|
||||
|
||||
@@ -230,15 +249,16 @@ closeall() = closeall(backend())
|
||||
# Atom PlotPane
|
||||
# ---------------------------------------------------------
|
||||
function showjuno(io::IO, m, plt)
|
||||
sz = plt[:size]
|
||||
sz = collect(plt[:size])
|
||||
dpi = plt[:dpi]
|
||||
thickness_scaling = plt[:thickness_scaling]
|
||||
|
||||
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] = Plots.DPI
|
||||
plt[:size] = [s * scale for s in sz]
|
||||
plt[:dpi] = jratio*Plots.DPI
|
||||
plt[:thickness_scaling] *= scale
|
||||
|
||||
prepare_output(plt)
|
||||
@@ -259,4 +279,6 @@ function _showjuno(io::IO, m::MIME"image/svg+xml", plt)
|
||||
end
|
||||
end
|
||||
|
||||
Base.showable(::MIME"application/prs.juno.plotpane+html", plt::Plot) = false
|
||||
|
||||
_showjuno(io::IO, m, plt) = _show(io, m, plt)
|
||||
|
||||
+59
-31
@@ -1,18 +1,42 @@
|
||||
# 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(plotattributes::KW, args)
|
||||
function _expand_seriestype_array(plotattributes::AKW, args)
|
||||
sts = get(plotattributes, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(plotattributes, :seriestype)
|
||||
reset_kw!(plotattributes, :seriestype)
|
||||
rd = Vector{RecipeData}(undef, size(sts, 1))
|
||||
for r in 1:size(sts, 1)
|
||||
for r in axes(sts, 1)
|
||||
dc = copy(plotattributes)
|
||||
dc[:seriestype] = sts[r:r,:]
|
||||
rd[r] = RecipeData(dc, args)
|
||||
@@ -23,7 +47,7 @@ function _expand_seriestype_array(plotattributes::KW, args)
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(plotattributes::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
|
||||
@@ -40,12 +64,8 @@ function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{Rec
|
||||
# remove subplot and axis args from plotattributes... they will be passed through in the kw_list
|
||||
if !isempty(args)
|
||||
for (k,v) in plotattributes
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
delete!(plotattributes, k)
|
||||
end
|
||||
if k in _all_subplot_args || k in _all_axis_args
|
||||
reset_kw!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -57,7 +77,7 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
@@ -80,7 +100,11 @@ function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
if isempty(next_series.args)
|
||||
_process_userrecipe(plt, kw_list, next_series)
|
||||
else
|
||||
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, 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
|
||||
@@ -108,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
|
||||
@@ -126,17 +150,17 @@ function _preprocess_userrecipe(kw::KW)
|
||||
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
|
||||
if get(kw, esym, nothing) !== nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
errkw = copy(kw)
|
||||
errkw[:seriestype] = esym
|
||||
@@ -147,7 +171,7 @@ 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]
|
||||
@@ -172,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)
|
||||
@@ -182,6 +206,7 @@ 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.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
@@ -203,7 +228,7 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, plotattributes::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) && (plotattributes[k] = pop!(kw, k))
|
||||
@@ -227,7 +252,7 @@ function _plot_setup(plt::Plot, plotattributes::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
|
||||
@@ -252,7 +277,7 @@ function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
plt[:inset_subplots] = nothing
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, plotattributes::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.
|
||||
@@ -261,16 +286,16 @@ function _subplot_setup(plt::Plot, plotattributes::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
|
||||
@@ -303,7 +328,7 @@ 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}, plotattributes::KW) where T
|
||||
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)
|
||||
@@ -327,7 +352,7 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# series types
|
||||
|
||||
function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
function _override_seriestype_check(plotattributes::AKW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st) && !(st in (:contour,:contour3d))
|
||||
z = plotattributes[:z]
|
||||
@@ -339,7 +364,7 @@ function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
st
|
||||
end
|
||||
|
||||
function _prepare_annotations(sp::Subplot, plotattributes::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])
|
||||
@@ -356,7 +381,7 @@ function _prepare_annotations(sp::Subplot, plotattributes::KW)
|
||||
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
|
||||
end
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, plotattributes::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(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y])
|
||||
@@ -385,7 +410,8 @@ end
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _process_seriesrecipe(plt::Plot, plotattributes::KW)
|
||||
function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
|
||||
#println("process $(typeof(plotattributes))")
|
||||
# replace seriestype aliases
|
||||
st = Symbol(plotattributes[:seriestype])
|
||||
st = plotattributes[:seriestype] = get(_typeAliases, st, st)
|
||||
@@ -405,7 +431,9 @@ function _process_seriesrecipe(plt::Plot, plotattributes::KW)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[: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
|
||||
|
||||
+11
-12
@@ -4,7 +4,7 @@ mutable struct CurrentPlot
|
||||
end
|
||||
const CURRENT_PLOT = CurrentPlot(nothing)
|
||||
|
||||
isplotnull() = CURRENT_PLOT.nullableplot == nothing
|
||||
isplotnull() = CURRENT_PLOT.nullableplot === nothing
|
||||
|
||||
"""
|
||||
current()
|
||||
@@ -96,7 +96,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
|
||||
series_attr = KW()
|
||||
for (k,v) in plotattributes
|
||||
if haskey(_series_defaults, k)
|
||||
if is_series_attr(k)
|
||||
series_attr[k] = pop!(plotattributes,k)
|
||||
end
|
||||
end
|
||||
@@ -119,7 +119,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
sp.attr[:subplot_index] = idx
|
||||
for series in serieslist
|
||||
merge!(series.plotattributes, series_attr)
|
||||
_add_defaults!(series.plotattributes, plt, sp, cmdidx)
|
||||
_slice_series_args!(series.plotattributes, plt, sp, cmdidx)
|
||||
push!(plt.series_list, series)
|
||||
_series_added(plt, series)
|
||||
cmdidx += 1
|
||||
@@ -163,12 +163,12 @@ 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, plotattributes::KW, args::Tuple)
|
||||
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
|
||||
|
||||
# --------------------------------
|
||||
@@ -217,21 +217,20 @@ function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
|
||||
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
|
||||
|
||||
# --------------------------------
|
||||
|
||||
+7
-2
@@ -50,8 +50,13 @@ function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
|
||||
desc = get(_arg_desc, attribute, "")
|
||||
first_period_idx = findfirst(isequal('.'), desc)
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
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]
|
||||
|
||||
+1116
File diff suppressed because it is too large
Load Diff
+419
-157
File diff suppressed because it is too large
Load Diff
+271
-284
@@ -7,92 +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(plotattributes::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :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, plotattributes::KW) = error("No user recipe defined for $(typeof(x))")
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::Nothing, plotattributes::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, plotattributes::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}, plotattributes::KW) where {T<:Number} = Any[v], nothing
|
||||
convertToAnyVector(v::AVec{Union{Missing, T}}, plotattributes::KW) where {T<:Number} = Any[replace(v, missing => NaN)], nothing
|
||||
|
||||
# string vector
|
||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:AbstractString} = Any[v], nothing
|
||||
convertToAnyVector(v::AVec{Union{Missing, T}}, plotattributes::KW) where {T<:AbstractString} = Any[replace(v, missing => "")], nothing
|
||||
|
||||
function convertToAnyVector(v::AMat, plotattributes::KW)
|
||||
v = handlemissings(v)
|
||||
if all3D(plotattributes)
|
||||
Any[Surface(v)]
|
||||
else
|
||||
Any[v[:,i] for i in 1:size(v,2)]
|
||||
end, nothing
|
||||
end
|
||||
|
||||
handlemissings(v::AMat) = v
|
||||
handlemissings(v::AMat{T}) where T <: Number = replace(v, missing => NaN)
|
||||
handlemissings(v::AMat{T}) where T <: String = replace(v, missing => "")
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function, plotattributes::KW) = Any[f], nothing
|
||||
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, plotattributes::KW) = Any[s], nothing
|
||||
|
||||
# volume
|
||||
convertToAnyVector(v::Volume, plotattributes::KW) = Any[v], nothing
|
||||
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, plotattributes::KW) = Any[v], nothing
|
||||
|
||||
# # dates
|
||||
convertToAnyVector(dts::AVec{D}, plotattributes::KW) where {D<:Union{Date,DateTime}} = 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, plotattributes::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, plotattributes)[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, plotattributes::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::Nothing, y::Nothing, z) = 1:size(z,1)
|
||||
compute_x(x::Nothing, 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::Nothing, y::Nothing, 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::Nothing) = 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)
|
||||
@@ -115,10 +108,16 @@ compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all no
|
||||
|
||||
# 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
|
||||
@@ -135,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)
|
||||
@@ -181,55 +170,111 @@ 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(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, 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(plotattributes, v::AbstractArray)
|
||||
isempty(v) && return Float64[]
|
||||
args = RecipesBase.apply_recipe(plotattributes, 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{<:DataPoint}}) = v
|
||||
function _apply_type_recipe(plotattributes, v::Surface)
|
||||
ret = _apply_type_recipe(plotattributes, v.surf)
|
||||
if typeof(ret) <: Formatted
|
||||
Formatted(Surface(ret.data), ret.formatter)
|
||||
else
|
||||
v
|
||||
Surface(ret.data)
|
||||
end
|
||||
end
|
||||
|
||||
# # special handling for Surface... need to properly unwrap and re-wrap
|
||||
# function _apply_type_recipe(plotattributes, v::Surface)
|
||||
# T = eltype(v.surf)
|
||||
# @show T
|
||||
# if T <: Integer || T <: AbstractFloat
|
||||
# v
|
||||
# else
|
||||
# ret = _apply_type_recipe(plotattributes, v.surf)
|
||||
# if typeof(ret) <: Formatted
|
||||
# Formatted(Surface(ret.data), ret.formatter)
|
||||
# else
|
||||
# v
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# don't do anything for datapoints or nothing
|
||||
_apply_type_recipe(plotattributes, v::AbstractArray{<:DataPoint}, letter) = v
|
||||
_apply_type_recipe(plotattributes, v::Nothing, letter) = v
|
||||
|
||||
# don't do anything for ints or floats
|
||||
_apply_type_recipe(plotattributes, 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
|
||||
@@ -238,10 +283,11 @@ _apply_type_recipe(plotattributes, v::AbstractArray{T}) where {T<:Union{Integer,
|
||||
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
|
||||
@@ -250,7 +296,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
|
||||
@@ -263,7 +310,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
|
||||
@@ -276,12 +323,14 @@ end
|
||||
end
|
||||
|
||||
|
||||
# # --------------------------------------------------------------------
|
||||
# # 1 argument
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# helper function to ensure relevant attributes are wrapped by Surface
|
||||
function wrap_surfaces(plotattributes::KW)
|
||||
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)
|
||||
@@ -290,75 +339,98 @@ function wrap_surfaces(plotattributes::KW)
|
||||
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
|
||||
@@ -372,54 +444,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
|
||||
xinv = invscalefunc(get(plotattributes, :xscale, :identity))
|
||||
xm = tryrange(f, xinv.([-5,-1,0,0.01]))
|
||||
xm, tryrange(f, filter(x->x>xm, xinv.([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)
|
||||
@@ -427,58 +486,25 @@ end
|
||||
x, f
|
||||
end
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 3 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# # 3d line or scatter
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
||||
# st = get(plotattributes, :seriestype, :none)
|
||||
# if st == :scatter
|
||||
# plotattributes[:seriestype] = :scatter3d
|
||||
# elseif !is3d(st)
|
||||
# plotattributes[:seriestype] = :path3d
|
||||
# end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
||||
# st = get(plotattributes, :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
|
||||
@@ -493,8 +519,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
|
||||
@@ -509,34 +534,30 @@ 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)
|
||||
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||
xs = _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||
xs, f
|
||||
_scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||
end
|
||||
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
||||
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)]
|
||||
xs = Any[_scaled_adapted_grid(f, xscale, yscale, xmin, xmax) for f in fs]
|
||||
xs, fs
|
||||
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, range(umin, stop = umax, length = n)
|
||||
|
||||
function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
|
||||
(xf, xinv), (yf, yinv) = ((scalefunc(s),invscalefunc(s)) for s in (xscale,yscale))
|
||||
xinv.(adapted_grid(yf∘f∘xinv, xf.((xmin, xmax))))
|
||||
xs, ys = adapted_grid(yf∘f∘xinv, xf.((xmin, xmax)))
|
||||
xinv.(xs), yinv.(ys)
|
||||
end
|
||||
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
# 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
|
||||
@@ -544,60 +565,26 @@ end
|
||||
fx, fy, fz, range(umin, stop = umax, length = numPoints)
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Lists of tuples and StaticArrays
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # 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 StaticArrays
|
||||
@recipe f(xy::AVec{StaticArrays.SVector{2,T}}) where {T<:Number} = unzip(xy)
|
||||
@recipe f(xy::StaticArrays.SVector{2,T}) where {T<:Number} = [xy[1]], [xy[2]]
|
||||
|
||||
#
|
||||
# # 3D StaticArrays
|
||||
@recipe f(xyz::AVec{StaticArrays.SVector{3,T}}) where {T<:Number} = unzip(xyz)
|
||||
@recipe f(xyz::StaticArrays.SVector{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(plotattributes)
|
||||
# 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)
|
||||
|
||||
@@ -611,7 +598,7 @@ end
|
||||
function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
|
||||
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.(Ref(x_ind), xi), i] = yi
|
||||
@@ -644,7 +631,7 @@ group_as_matrix(t) = false
|
||||
if length(g.args) == 1
|
||||
x = zeros(Int, lengthGroup)
|
||||
for indexes in groupby.groupIds
|
||||
x[indexes] = 1:length(indexes)
|
||||
x[indexes] = eachindex(indexes)
|
||||
end
|
||||
last_args = g.args
|
||||
else
|
||||
@@ -652,7 +639,7 @@ group_as_matrix(t) = false
|
||||
last_args = g.args[2:end]
|
||||
end
|
||||
x_u = unique(sort(x))
|
||||
x_ind = Dict(zip(x_u, 1:length(x_u)))
|
||||
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 = 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...)
|
||||
+1
-1
@@ -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
|
||||
)
|
||||
|
||||
+19
-36
@@ -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(typeof(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,7 +66,7 @@ _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
|
||||
@@ -100,7 +80,7 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
end
|
||||
end
|
||||
|
||||
srand(1)
|
||||
Random.seed!(1)
|
||||
|
||||
label := ""
|
||||
colorbar := false
|
||||
@@ -139,27 +119,30 @@ _get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func
|
||||
@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 = range(0, stop = 10π, length = n)
|
||||
x = ts .* cos.(ts)
|
||||
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
|
||||
|
||||
+60
-6
@@ -5,6 +5,7 @@
|
||||
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 <: AbstractDisplay end
|
||||
@@ -18,10 +19,64 @@ wrap(obj::T) where {T} = InputWrapper{T}(obj)
|
||||
Base.isempty(wrapper::InputWrapper) = false
|
||||
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
struct Attr <: AbstractDict{Symbol,Any}
|
||||
explicit::KW
|
||||
defaults::KW
|
||||
end
|
||||
|
||||
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::KW
|
||||
plotattributes::Attr
|
||||
end
|
||||
|
||||
attr(series::Series, k::Symbol) = series.plotattributes[k]
|
||||
@@ -36,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
|
||||
@@ -48,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}
|
||||
plotattributes::KW
|
||||
plotattributes::Attr
|
||||
end
|
||||
|
||||
mutable struct Extrema
|
||||
@@ -67,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}
|
||||
@@ -79,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
|
||||
|
||||
+375
-444
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
reference_images
|
||||
@@ -1,10 +0,0 @@
|
||||
StatPlots
|
||||
Images
|
||||
ImageMagick
|
||||
@osx QuartzImageIO
|
||||
FileIO
|
||||
GR 0.31.0
|
||||
RDatasets
|
||||
VisualRegressionTests
|
||||
UnicodePlots
|
||||
LaTeXStrings
|
||||
@@ -1,46 +0,0 @@
|
||||
using Pkg
|
||||
|
||||
# need this to use Conda
|
||||
# ENV["PYTHON"] = ""
|
||||
|
||||
to_add = [
|
||||
# PackageSpec(url="https://github.com/JuliaStats/KernelDensity.jl.git"),
|
||||
PackageSpec(name="PlotUtils", rev="master"),
|
||||
PackageSpec(name="RecipesBase", rev="master"),
|
||||
# PackageSpec(name="Blink", rev="master"),
|
||||
# PackageSpec(name="Rsvg", rev="master"),
|
||||
# PackageSpec(name="PlotlyJS", rev="master"),
|
||||
# PackageSpec(name="VisualRegressionTests", rev="master"),
|
||||
# PackageSpec("PyPlot"),
|
||||
# PackageSpec("InspectDR"),
|
||||
]
|
||||
|
||||
if isinteractive()
|
||||
Pkg.develop(PackageSpec(url="https://github.com/JuliaPlots/PlotReferenceImages.jl.git"))
|
||||
append!(to_add, [
|
||||
PackageSpec(name="FileIO"),
|
||||
PackageSpec(name="ImageMagick"),
|
||||
PackageSpec(name="UnicodePlots"),
|
||||
PackageSpec(name="VisualRegressionTests"),
|
||||
PackageSpec(name="Gtk"),
|
||||
# PlotlyJS:
|
||||
# PackageSpec(name="PlotlyJS"),
|
||||
# PackageSpec(name="Blink"),
|
||||
# PackageSpec(name="ORCA"),
|
||||
# PyPlot:
|
||||
# PackageSpec(name="PyPlot"),
|
||||
# PackageSpec(name="PyCall"),
|
||||
# PackageSpec(name="LaTeXStrings"),
|
||||
])
|
||||
else
|
||||
push!(to_add, PackageSpec(url="https://github.com/JuliaPlots/PlotReferenceImages.jl.git"))
|
||||
end
|
||||
|
||||
Pkg.add(to_add)
|
||||
|
||||
Pkg.build("ImageMagick")
|
||||
# Pkg.build("GR")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.build("PyPlot")
|
||||
+9
-73
@@ -1,92 +1,28 @@
|
||||
|
||||
using VisualRegressionTests
|
||||
# using ExamplePlots
|
||||
|
||||
if isinteractive()
|
||||
@eval Main import Gtk
|
||||
end
|
||||
|
||||
# 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
|
||||
import PlotReferenceImages
|
||||
using Random
|
||||
using 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)
|
||||
|
||||
import Plots._current_plots_version
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], tol = 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]
|
||||
Plots.theme(:default)
|
||||
@info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
backend(pkg)
|
||||
backend()
|
||||
|
||||
default(size=(500,300))
|
||||
# ensure consistent results
|
||||
Random.seed!(1234)
|
||||
|
||||
# reference image directory setup
|
||||
# refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
|
||||
refdir = joinpath(dirname(pathof(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, tol=tol, newfn = newfn)
|
||||
@@ -100,7 +36,7 @@ function image_comparison_facts(pkg::Symbol;
|
||||
tol = 1e-2) # acceptable error (percent)
|
||||
for i in 1:length(Plots._examples)
|
||||
i in skip && continue
|
||||
if only == nothing || i in only
|
||||
if only === nothing || i in only
|
||||
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, tol=tol) |> success == true
|
||||
end
|
||||
end
|
||||
|
||||
+148
-126
@@ -1,103 +1,119 @@
|
||||
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("add_packages.jl")
|
||||
include("imgcomp.jl")
|
||||
|
||||
# don't actually show the plots
|
||||
Random.seed!(1234)
|
||||
default(show=false, reuse=true)
|
||||
img_tol = 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, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
|
||||
@static if Sys.islinux()
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = [25, 30])
|
||||
plotly()
|
||||
@testset "Plotly" begin
|
||||
image_comparison_facts(:plotly, tol=img_tol, skip = Plots._backend_skips[:plotlyjs])
|
||||
end
|
||||
|
||||
pyplot()
|
||||
@testset "PyPlot" begin
|
||||
image_comparison_facts(:pyplot, tol=img_tol, skip = Plots._backend_skips[:pyplot])
|
||||
end
|
||||
|
||||
pgfplots()
|
||||
@testset "PGFPlots" begin
|
||||
image_comparison_facts(:pgfplots, tol=img_tol, skip = Plots._backend_skips[:pgfplots])
|
||||
end
|
||||
=#
|
||||
##
|
||||
|
||||
@testset "Backends" begin
|
||||
|
||||
@testset "GR" begin
|
||||
ENV["PLOTS_TEST"] = "true"
|
||||
ENV["GKSwstype"] = "100"
|
||||
@test gr() == Plots.GRBackend()
|
||||
@test backend() == Plots.GRBackend()
|
||||
|
||||
@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
|
||||
|
||||
# @static if isinteractive()
|
||||
# @testset "PyPlot" begin
|
||||
# @test pyplot() == Plots.PyPlotBackend()
|
||||
# @test backend() == Plots.PyPlotBackend()
|
||||
#
|
||||
# image_comparison_facts(:pyplot, tol=img_tol, skip = [2, 25, 30, 31])
|
||||
# end
|
||||
# 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
|
||||
end
|
||||
|
||||
# The plotlyjs testimages return a connection error on travis:
|
||||
# connect: connection refused (ECONNREFUSED)
|
||||
|
||||
# @static if isinteractive()
|
||||
# @testset "PlotlyJS" begin
|
||||
# @test plotlyjs() == Plots.PlotlyJSBackend()
|
||||
# @test backend() == Plots.PlotlyJSBackend()
|
||||
#
|
||||
# image_comparison_facts(:plotlyjs,
|
||||
# skip=[
|
||||
# 2, # animation (skipped for speed)
|
||||
# 25,
|
||||
# 27, # (polar plots) takes very long / not working
|
||||
# 30,
|
||||
# 31, # animation (skipped for speed)
|
||||
# ],
|
||||
# tol=img_tol)
|
||||
# end
|
||||
# end
|
||||
|
||||
# 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 "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
|
||||
# ],
|
||||
# tol=img_tol)
|
||||
# end
|
||||
|
||||
|
||||
# @testset "Plotly" begin
|
||||
# @test plotly() == Plots.PlotlyBackend()
|
||||
# @test backend() == Plots.PlotlyBackend()
|
||||
#
|
||||
# # # until png generation is reliable on OSX, just test on linux
|
||||
# # @static Sys.islinux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], tol=img_tol)
|
||||
# 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], tol=img_tol)
|
||||
# end
|
||||
|
||||
|
||||
|
||||
|
||||
@testset "Axes" begin
|
||||
p = plot()
|
||||
axis = p.subplots[1][:xaxis]
|
||||
@@ -113,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,295 @@
|
||||
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)
|
||||
plot(
|
||||
y,
|
||||
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
|
||||
leg = false,
|
||||
)
|
||||
annotate!([
|
||||
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
|
||||
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
|
||||
])
|
||||
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),
|
||||
],
|
||||
)
|
||||
# mktempdir() do path
|
||||
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
|
||||
# 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
|
||||
Reference in New Issue
Block a user