Compare commits
807 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 09a17ba783 | |||
| 046b291c1f | |||
| 187a19bfd1 | |||
| 91816e6715 | |||
| 13339125eb | |||
| 3197db9fc1 | |||
| 6e4aac1123 | |||
| 94450a26e9 | |||
| f571596eda | |||
| 8b6ca96e0a | |||
| 198de2607a | |||
| 7cd1c9e8c8 | |||
| 9aa201dcb3 | |||
| 25b09a7962 | |||
| 80151ac1a3 | |||
| bad5668ff7 | |||
| cad0f15aed | |||
| 9c42c30d8a | |||
| c471d1e4c3 | |||
| 7ae4fae68c | |||
| 2405d5151b | |||
| 4f667291bd | |||
| 49ad3a51aa | |||
| f2e98acc65 | |||
| b68adde751 | |||
| 7220eca7a8 | |||
| 10dd9b5120 | |||
| 7590c133fd | |||
| 2873685517 | |||
| 9887892a92 | |||
| 6fa15751ae | |||
| 24e3fcbb6c | |||
| 91f51778f1 | |||
| 5c2bf5d76c | |||
| c4233e2896 | |||
| e0b7bdc1a5 | |||
| 91ec39e141 | |||
| 602dbdf1d2 | |||
| 3eabaf074a | |||
| a81178121f | |||
| e1a83aab1d | |||
| 84601bacdb | |||
| e991e2bf37 | |||
| 9b6fc4bc6b | |||
| c9e832600c | |||
| 25190d945d | |||
| b2f2ffcfed | |||
| 00821c1ae3 | |||
| 937acbc218 | |||
| e0f9313b8b | |||
| 6d0d8ea90a | |||
| ebf32390ef | |||
| eefea15069 | |||
| 531dd43284 | |||
| e483ce85dc | |||
| 80da14c1fb | |||
| 374580a3d6 | |||
| 4a51e4e7fb | |||
| ae969dad38 | |||
| 7fcd008f2f | |||
| def5be630a | |||
| de3848ff53 | |||
| 8d4ab6bb94 | |||
| 28ac117d68 | |||
| 065ff48b15 | |||
| 69bdd6db83 | |||
| cd270dd407 | |||
| b4944a6f81 | |||
| 4049bf20c2 | |||
| 805ca0d378 | |||
| fcaae346a7 | |||
| 0684102511 | |||
| 6ca87f6d33 | |||
| 3c96831cdc | |||
| b551f6714b | |||
| c005cc211f | |||
| 92b0d5c0be | |||
| 45dc9e5c0d | |||
| 7c505949fc | |||
| 905accdb7d | |||
| 30f3e3cf56 | |||
| 71131fd00c | |||
| 60b8b0080c | |||
| b30ff9b41b | |||
| cf1a0e3c33 | |||
| e94fe13178 | |||
| b2c4561a7d | |||
| ef63c8d66e | |||
| 00be8a2237 | |||
| 6593e3cfe6 | |||
| ac73798b9d | |||
| 1697b99bbc | |||
| 1e9467a221 | |||
| b56238089c | |||
| 47d8aa871b | |||
| 87627b8320 | |||
| c419f87b64 | |||
| 9ae22a1ad7 | |||
| b4dfd35be6 | |||
| 916654b3ec | |||
| 2acd088226 | |||
| 2d217edc15 | |||
| b55d13bc21 | |||
| 1f9468e029 | |||
| 593804096d | |||
| befb14682d | |||
| 07a6700ab6 | |||
| 8131953e49 | |||
| 7e360aa915 | |||
| 0710fba638 | |||
| ad1bcbb0ac | |||
| afb7a17e5f | |||
| badf302756 | |||
| 99941dde8f | |||
| e5c55b6962 | |||
| 7a7f4cb306 | |||
| c0ff8f80b1 | |||
| d084e31065 | |||
| 12008ef1c6 | |||
| 23ec52c6b4 | |||
| 5485740291 | |||
| 6dc0823b73 | |||
| 0f9220c965 | |||
| 5c21261aea | |||
| 1fda0df03e | |||
| d4406bb806 | |||
| b1095e29bc | |||
| 3e56aa43e4 | |||
| c2dc030fae | |||
| 5565680b53 | |||
| 340cd0a1fe | |||
| 2844e706a9 | |||
| 55ee64b4e6 | |||
| 3849700a28 | |||
| 3dd0049973 | |||
| 8e6ccf1b20 | |||
| c544dbbbb7 | |||
| 5205ac38ad | |||
| 0143807515 | |||
| 5176ab1382 | |||
| b0d54f7c5d | |||
| 96f0335af4 | |||
| 572f8ebfde | |||
| 07a8d844d2 | |||
| c48abef5f8 | |||
| 9b3679792d | |||
| cb00983db3 | |||
| 83686866cc | |||
| 7a49249384 | |||
| 0548793bfd | |||
| cd770f70c0 | |||
| a3f1950a18 | |||
| 1874c28cad | |||
| 317f01a6b4 | |||
| 5e8eff074d | |||
| db028f2774 | |||
| a82a154be9 | |||
| af01927807 | |||
| f60aac93ab | |||
| 4860bf3f19 | |||
| adf2af82b4 | |||
| 15ddc5f06c | |||
| 8d16563014 | |||
| 3559b539e3 | |||
| da765a5eec | |||
| 72c4962e97 | |||
| 789cf1d66b | |||
| 64ec922d7f | |||
| 85b3aefd67 | |||
| af0dabec99 | |||
| de85983247 | |||
| 7eaae3a6d5 | |||
| 403b57c076 | |||
| dd434b89d2 | |||
| 1a3d58e09b | |||
| 89ae239743 | |||
| c785d1509b | |||
| 9e412555bb | |||
| 0717805773 | |||
| d045c5a390 | |||
| 3cc0d0c735 | |||
| 9a3c727532 | |||
| a7f2484d45 | |||
| 96d97699a0 | |||
| e92e832258 | |||
| 3a769549d8 | |||
| 0eab0dc680 | |||
| 2b48f50de4 | |||
| fa60eacfc5 | |||
| d2a4136d4f | |||
| 7d88a746ea | |||
| 7f4537bdc6 | |||
| d68829820b | |||
| 9cf3036ab5 | |||
| 2467eb71c1 | |||
| 345bb5d1aa | |||
| 8d50828998 | |||
| 201de82baf | |||
| 99519ac3da | |||
| a575ab7c74 | |||
| c4f79814df | |||
| 6135f6271c | |||
| 8ef70f07f0 | |||
| 17b7e37885 | |||
| 35c17044e1 | |||
| 7085d98f41 | |||
| ee73e32b0e | |||
| 371430c172 | |||
| 495753b517 | |||
| 211be0a863 | |||
| 12c9a8e6c3 | |||
| fb5d9e0c01 | |||
| 61a2d962c0 | |||
| 440622830a | |||
| 2652990432 | |||
| 652e2f27b6 | |||
| 77a82eaa6a | |||
| 624a181b23 | |||
| 865ac52442 | |||
| 4f064d7c02 | |||
| facb67e94e | |||
| 124c838e06 | |||
| 026e894e1d | |||
| 2ed0f1661a | |||
| 00f285cba3 | |||
| ddf5bf472f | |||
| 534c7799e5 | |||
| b254aaa081 | |||
| 76f2860c74 | |||
| 5ebcb77d0d | |||
| 4847752ef4 | |||
| 84ec8d61ae | |||
| 0f13551b6d | |||
| 3e66c6cce4 | |||
| 09de3a5e7a | |||
| 00483d4c0d | |||
| 1e3d10ad31 | |||
| dc31cd7103 | |||
| cffb670f72 | |||
| 781fea7431 | |||
| 78b0b7e01d | |||
| e96367cd4b | |||
| 30af3c7f8a | |||
| 264405827e | |||
| 083721bbc1 | |||
| 91ed04ff83 | |||
| cbbd4fd029 | |||
| f6bc7721a8 | |||
| 32ec9e82bf | |||
| 69a7a8a04e | |||
| ccb5194bb5 | |||
| 2d280edf0c | |||
| afe4817150 | |||
| 6754b3746e | |||
| 287f83f4d0 | |||
| bf518a961e | |||
| 4a55467eef | |||
| 56cf5249f9 | |||
| 17e8bc6949 | |||
| 02ede8020f | |||
| 54158a0340 | |||
| 7ce96a4e3d | |||
| 2067b70445 | |||
| d02658e0ba | |||
| 27e68ed6a9 | |||
| b3e0ac017f | |||
| 8bc437a84e | |||
| 11e5a94fc4 | |||
| c11941cbc1 | |||
| f792aea98e | |||
| 12e5937a91 | |||
| 60b9cd8789 | |||
| 41e4c80494 | |||
| bd2756c0bf | |||
| 1f3c4948c6 | |||
| 062f9a195c | |||
| a3e8af40b2 | |||
| 49f4773e81 | |||
| ee78385b4c | |||
| e99ccd0051 | |||
| cbc022524b | |||
| 34d9d042aa | |||
| 22f69a99e2 | |||
| 4ee5539e3d | |||
| 048636e894 | |||
| 5797ad2d5b | |||
| ba9a8d52cc | |||
| ee18a9dd6c | |||
| 517c2f456c | |||
| e8734daa4d | |||
| 1fd7c6e2f1 | |||
| bc638ea06e | |||
| 5b0ba79a35 | |||
| 173c7b2aec | |||
| fd37a9c660 | |||
| fc460554e7 | |||
| 73490903a3 | |||
| 964c0ac420 | |||
| f59ad1620a | |||
| 4797f18819 | |||
| b0d73a3314 | |||
| 562e60f964 | |||
| fde0fa673b | |||
| c93d5e7830 | |||
| e305b973b2 | |||
| 506d3611fd | |||
| 14b644777d | |||
| b9c23c75f4 | |||
| af60d03060 | |||
| adb66cae9d | |||
| aeaa0c5f8d | |||
| b009da4b47 | |||
| 8abd70bed8 | |||
| 91cd6c1954 | |||
| 7a702489d0 | |||
| d33331f650 | |||
| 8e3bd07c91 | |||
| ad55551819 | |||
| 1d38e50281 | |||
| cef0882e80 | |||
| 5a07c4917e | |||
| e498d091cd | |||
| 8ba30c5453 | |||
| 685e2eaff7 | |||
| 6ca8471c64 | |||
| e30f4652af | |||
| 9acb89ba8a | |||
| 9b9a775a41 | |||
| a6f11cdb31 | |||
| 777642ef55 | |||
| d1d7fd258f | |||
| 7e4635de26 | |||
| d7b2173066 | |||
| 55e9c8c9eb | |||
| c34e1d3c19 | |||
| f89b1ba303 | |||
| fc7b6dd0c6 | |||
| 1b5240a513 | |||
| 4bcf43c84b | |||
| b57abfd563 | |||
| bfc26fa407 | |||
| e7b435b47f | |||
| dd6842522f | |||
| f56d944870 | |||
| 772b79306d | |||
| 4ef2c6286b | |||
| a5f7c1b867 | |||
| 4d8ef2a008 | |||
| 8bc28d472f | |||
| 3753ab1b55 | |||
| babc600915 | |||
| b2f92e246e | |||
| 92f9ccce5c | |||
| 0ace28b784 | |||
| bd06f0c713 | |||
| 4e5c88eb74 | |||
| 004ad15875 | |||
| 1c0a731ff6 | |||
| 9a5f42c251 | |||
| c8cdade884 | |||
| bbff66a041 | |||
| 6b6394ba97 | |||
| d11c6a20bf | |||
| 6c396e1d88 | |||
| bffd9bcb53 | |||
| 7cee448d68 | |||
| c273bf6a7b | |||
| 047a13a8dc | |||
| 717aed5a06 | |||
| 7d58090e59 | |||
| 501df8cfb9 | |||
| 7f6fe09a00 | |||
| d183d579cf | |||
| bfa2fd2edc | |||
| 7c257a09f6 | |||
| da4111579d | |||
| 6b117234bc | |||
| c4bac660cf | |||
| ee2e7d509c | |||
| 461e93fc56 | |||
| c74adc6521 | |||
| 7cc60d0b0c | |||
| d8f0612f5a | |||
| cd4b41a537 | |||
| fdb2ee2847 | |||
| 9c4cc5acda | |||
| f901254519 | |||
| 18032bb9af | |||
| a7a5fe36f9 | |||
| 32e1536955 | |||
| 77c2ee6782 | |||
| dbe5b78307 | |||
| 9578fe03b9 | |||
| ef48b66ac4 | |||
| b9d586c009 | |||
| 1d51982cb0 | |||
| 046985277d | |||
| d679337700 | |||
| 2d5f896069 | |||
| de51beebe8 | |||
| 80b25657bc | |||
| 0db60348aa | |||
| 5017024557 | |||
| 94863d69b1 | |||
| 06466b060f | |||
| 3ac9f3d9cc | |||
| d335a0221d | |||
| a118d73871 | |||
| 6c13d87a00 | |||
| 9e2264c841 | |||
| c0248069a1 | |||
| 5ba4c10a18 | |||
| 82ceb71bd0 | |||
| a4636fd88d | |||
| e19e25473c | |||
| bde415edd7 | |||
| 2004090ce5 | |||
| 18ea542cc4 | |||
| 11a856fbb3 | |||
| 1ed7899296 | |||
| a79a40b1e8 | |||
| d4075a1ef5 | |||
| 99f3b7ae99 | |||
| fcb5b355b1 | |||
| 43d26af34d | |||
| 5e1c67c042 | |||
| 11ef2be506 | |||
| 0f9bb7793c | |||
| decd7c2f60 | |||
| edf88ffdf6 | |||
| 435a7c809b | |||
| d79c66e5b6 | |||
| 10a8520805 | |||
| cf6c16102b | |||
| 32baaea5cb | |||
| 27e2795576 | |||
| c21f3810b9 | |||
| 877e7ab940 | |||
| 7a9a871be3 | |||
| de31ce1c73 | |||
| 8b6dba8301 | |||
| af859b4c13 | |||
| 9b5295ff00 | |||
| c1a94d704e | |||
| c2b56878fc | |||
| 4757bd89c0 | |||
| 1e56bf939d | |||
| dbd5ae46dd | |||
| 7c6095b6e8 | |||
| 04888646e7 | |||
| f6ed3fbb76 | |||
| 7bf2918438 | |||
| 92fefc7089 | |||
| e6df4d977d | |||
| 62ca787e6e | |||
| 32d59fff61 | |||
| 1949b6fe0f | |||
| 95a5b5025a | |||
| aa22fd2ff2 | |||
| 50df9a70c9 | |||
| 6d88e150d0 | |||
| b1817e513b | |||
| ce737a9491 | |||
| 6ff4dce62a | |||
| 89b5582d0d | |||
| 09c5e8a375 | |||
| a1a7dcd6b8 | |||
| d223d872fb | |||
| 91eaa32ad7 | |||
| 6cdccf4353 | |||
| ac9aee97fd | |||
| 5be95be9ae | |||
| e402f5dfce | |||
| 61be113714 | |||
| a637f43edd | |||
| 4bb43887a4 | |||
| 802b20be6f | |||
| 216a9c3666 | |||
| d52b2a9adc | |||
| 81782c3927 | |||
| 2c2ebbba0e | |||
| 3d323a734d | |||
| 93263a61b1 | |||
| 8b551e5285 | |||
| e11c91ddd9 | |||
| 938c83533b | |||
| 46136d616f | |||
| 8fe902e96c | |||
| 2fb7dddffb | |||
| 5d1876ab6d | |||
| 1aa4e7eee6 | |||
| 995e12bd57 | |||
| 349b6e8254 | |||
| ee6dbb9ac7 | |||
| 5b85cd83f2 | |||
| 1645c3f66b | |||
| 084d45141d | |||
| 50bec7a461 | |||
| f80afddd8f | |||
| 5d2c630b05 | |||
| f655b2313c | |||
| 2a819c2849 | |||
| 0ef13dcf7a | |||
| 5e5f3c1e95 | |||
| c76ca2dd6e | |||
| ab490b8f95 | |||
| bd42d9e7cc | |||
| e3d8f4a223 | |||
| 34567e1282 | |||
| acfb14a1f5 | |||
| 6a3495819a | |||
| bf6e8d3c1f | |||
| 66f08cc1a3 | |||
| 470012501b | |||
| 986bd02e5e | |||
| 9b8510ab3e | |||
| 0db9710e0f | |||
| 7f231057fd | |||
| bb687e47cc | |||
| 0f3928ec9a | |||
| a8380334df | |||
| 70bead1eb1 | |||
| df63530b89 | |||
| 34dedd36e6 | |||
| eb6c15caba | |||
| 39a7815f80 | |||
| 4bbe367aab | |||
| 7d50c7e4fe | |||
| 5d0adb1783 | |||
| 81ef4bdee8 | |||
| 2cf365d36d | |||
| 22d8ecffe5 | |||
| b987331b4f | |||
| eff8159d46 | |||
| f0e4f4dc9d | |||
| e02a70249e | |||
| 899afd625a | |||
| 863ec08681 | |||
| 28c958fb20 | |||
| 4c9f4a55d5 | |||
| a7fcf77251 | |||
| a57745ed64 | |||
| dd16c7d560 | |||
| 7f3780a20d | |||
| 99c8713e7f | |||
| 49eb2c426d | |||
| 4c55ff5df8 | |||
| 714f8dea1d | |||
| 1419a919aa | |||
| 6d4717fc96 | |||
| 6a8f4caee3 | |||
| dbf96a69d7 | |||
| 0519f18bd7 | |||
| f7765c80b8 | |||
| f2e2ffa170 | |||
| b2a816452d | |||
| 299e9a85f2 | |||
| ad34863bd6 | |||
| 129a91b36f | |||
| 78f10de8cd | |||
| 189b2e5cd6 | |||
| d4f67ec3b5 | |||
| 7ec54b9c2e | |||
| 60d20ea998 | |||
| e80a73cc1c | |||
| 6e51a08694 | |||
| 0da6f6b4a4 | |||
| ba3e75e710 | |||
| 8fee5e0de7 | |||
| 68a060b7eb | |||
| 9f5be974b6 | |||
| ea976d5f08 | |||
| 892d68e792 | |||
| 5a0744e2f1 | |||
| c01d8199f9 | |||
| df023d6bdb | |||
| 4bfb562477 | |||
| 4bc01b92bd | |||
| ee0f4e11ea | |||
| 1aa780f0a2 | |||
| cf58f3cbf2 | |||
| 18619e9010 | |||
| ae37dce0f3 | |||
| e48831d092 | |||
| 99f25ef744 | |||
| acf6e86c0f | |||
| 19192b622b | |||
| 183f399341 | |||
| 240c1efab9 | |||
| 8bbdb0f1b8 | |||
| ecee135a63 | |||
| 51d4630405 | |||
| 60e7695546 | |||
| 6d14a858a3 | |||
| 08a6f3af36 | |||
| cc43202e8f | |||
| 8fb3f3b1c9 | |||
| 2fe2ff474c | |||
| f0ad851aa7 | |||
| ab29a712c0 | |||
| 8dd264c4da | |||
| 40282ae23a | |||
| ed75369c6d | |||
| b34f4141a2 | |||
| 5d5d8f073a | |||
| f690961af2 | |||
| 12aa43ff5f | |||
| 4f171e3eb5 | |||
| 671f5c3a68 | |||
| 1f12185441 | |||
| a3e599e38d | |||
| 5a3779e431 | |||
| 5ddcdef1b4 | |||
| 7b6b44b284 | |||
| a691d5c7e8 | |||
| 7cf40b16df | |||
| ad6b707abf | |||
| 133052b58c | |||
| 226bf7e705 | |||
| cdcbdc308c | |||
| 5bfa6151ac | |||
| 020760f0c8 | |||
| 0c4750547a | |||
| 9c46509f84 | |||
| 928a2d646b | |||
| 7ffd1fc9c2 | |||
| 6d4f456826 | |||
| e78bf87377 | |||
| c76fc5b909 | |||
| eecd14f53a | |||
| ef29b4a5b0 | |||
| d6748792e9 | |||
| ae9c26504d | |||
| 7f7b543e18 | |||
| 2464d7bd9a | |||
| 3ce71999e2 | |||
| 7f9d83c088 | |||
| e8f6e7b56c | |||
| 94c0957e11 | |||
| b0ffe03c99 | |||
| 8ea878a2a2 | |||
| bbbab914d8 | |||
| 735a45b4e9 | |||
| a4558302f2 | |||
| d8ba26f790 | |||
| dcc3055dec | |||
| 6db7116db9 | |||
| 422988b3f6 | |||
| 2f04c5fec8 | |||
| e811aa44b0 | |||
| 4c7ab232d5 | |||
| fc42641469 | |||
| b71cfce105 | |||
| be55c1ad6f | |||
| 979a846298 | |||
| a4c59ce964 | |||
| 208c70f933 | |||
| 9dcbc06b3d | |||
| e3f6da382d | |||
| cc201ce4e7 | |||
| 08bdd11410 | |||
| bac192d825 | |||
| 55c968f138 | |||
| 6df2bc2790 | |||
| f8576c7135 | |||
| 7d220f571d | |||
| eda1ebe36b | |||
| ae4288719f | |||
| a016b655f4 | |||
| f4bc2e0649 | |||
| cf6f7035b6 | |||
| cb7498a30d | |||
| ccb3cadd2a | |||
| 0fb1a0ed6b | |||
| 94e79f1e4c | |||
| 65a28e76e5 | |||
| 0c5a9172ab | |||
| 1c21a0d609 | |||
| 16d18552f8 | |||
| 001cb80cfe | |||
| bd14d9c07b | |||
| 08bbde327f | |||
| 12488bf2b0 | |||
| 4bf0d69f79 | |||
| 78e3b69cc5 | |||
| 531a38bc39 | |||
| 77fa5454e2 | |||
| 0cd382a1ce | |||
| 9fc0c98722 | |||
| af23074820 | |||
| fdf2d5b20b | |||
| b37f2141a1 | |||
| 520dc80eeb | |||
| 878fa48afa | |||
| 514cd1bd37 | |||
| f5ee5ea814 | |||
| e3b0f7cd94 | |||
| 5567038cec | |||
| 0327c9101e | |||
| 3d86d5f530 | |||
| 073724a84c | |||
| 6a129bfe92 | |||
| 53a4c36aab | |||
| eb36f4eca0 | |||
| 54838d386d | |||
| 0235821f24 | |||
| efd116ec52 | |||
| ff0f1c5c50 | |||
| 6bbb26d2ea | |||
| 2a1140c0b3 | |||
| a80c7ada27 | |||
| 9bc6d0c7d1 | |||
| f3566a4933 | |||
| cd958a6c68 | |||
| 4f6a920f72 | |||
| b717cf1dc0 | |||
| 33e5a51a28 | |||
| ff751ca423 | |||
| 93df7f4367 | |||
| dca5a7ce4d | |||
| bf4dd78a7d | |||
| 489e484117 | |||
| 6e1cc8449b | |||
| 148ad8032f | |||
| 6d53594850 | |||
| ee7a3b3686 | |||
| 18188516ec | |||
| bcc2f089be | |||
| 07843c00d3 | |||
| de12915eba | |||
| 4395718e0c | |||
| 913fdf5247 | |||
| 650c1730c9 | |||
| fba1c66499 | |||
| 1c04823877 | |||
| 6c84624d71 | |||
| 69fa9e7b10 | |||
| 350237a774 | |||
| b73ea55bbd | |||
| 9eac6a1db0 | |||
| ea6ab46a13 | |||
| 5f84865365 | |||
| 5b3c41bd77 | |||
| c6b7ff6c75 | |||
| 337a107168 | |||
| a7485cc7f5 | |||
| 8b4edae358 | |||
| b6dca5e9b1 | |||
| 371f0a6a73 | |||
| b777a2d3c8 | |||
| bdfc4636f0 | |||
| 7ba128db71 | |||
| 0f1d8478ce | |||
| 9eae2ad76b | |||
| f43f2956f5 | |||
| 2dc03cc537 | |||
| 233b0d0de2 | |||
| dc9a044141 | |||
| 518fba759f | |||
| 7dfa1ec984 | |||
| da5a538f89 | |||
| a518796736 | |||
| 13111b3779 | |||
| 8412d77067 | |||
| fe0ea168f3 | |||
| af9d97d561 | |||
| 478f5b6e20 | |||
| c89b125b9a | |||
| a981dc71f9 | |||
| 331dd49326 | |||
| b69c37f1bb | |||
| 20ef846c38 | |||
| cd9f00300c | |||
| d6e5b57a08 | |||
| d5e5d81ee2 | |||
| dee24e6f08 | |||
| 9a2a54b885 | |||
| 4c2b01ccb0 | |||
| ddf4136022 | |||
| 4f8fc61689 | |||
| c8a7a050de | |||
| fb04b3d78b | |||
| 0f28b59c5f | |||
| e2795341d9 | |||
| 04c20b84ad | |||
| 04f5d7e411 | |||
| 82877dc53e | |||
| c7eb22021f | |||
| 654fa0426b | |||
| ae6c1c0c66 | |||
| b3f5939689 | |||
| 184404beac | |||
| 456b8258c3 | |||
| a1896d189c | |||
| e47dde9f41 | |||
| 4be19ba6d8 | |||
| 4320c7b9fb | |||
| 2dcf7fb3dd | |||
| 6c548f025e | |||
| 3fd745a5df | |||
| 5e968e0aa8 | |||
| f41426c85a | |||
| 41877e362a | |||
| 7741872578 | |||
| d1dcd536f5 | |||
| 177d380092 | |||
| ad2eaf7aef |
@@ -5,3 +5,4 @@
|
||||
examples/.ipynb_checkpoints/*
|
||||
examples/meetup/.ipynb_checkpoints/*
|
||||
deps/plotly-latest.min.js
|
||||
deps/build.log
|
||||
|
||||
+9
-8
@@ -4,10 +4,11 @@ os:
|
||||
- linux
|
||||
# - osx
|
||||
julia:
|
||||
- 0.6
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
# - 1.0
|
||||
- nightly
|
||||
# matrix:
|
||||
# allow_failures:
|
||||
# - julia: nightly
|
||||
|
||||
# # before install:
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
@@ -42,10 +43,10 @@ before_install:
|
||||
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 'Pkg.clone(pwd()); Pkg.build("Plots")'
|
||||
- julia test/travis_commands.jl
|
||||
# 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")'
|
||||
|
||||
@@ -3,13 +3,215 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.12!
|
||||
- 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.4
|
||||
- `backports` branch is for Julia 0.5
|
||||
|
||||
---
|
||||
## (current master)
|
||||
- All new development should target Julia 1.x!
|
||||
|
||||
## 0.20.0
|
||||
Many updates, min julia 1.0
|
||||
- change display type to use PlotsDisplay (fixes Juno integration)
|
||||
- change all internal uses of `d` to `plotattributes` (no user change)
|
||||
- change spy implementation to use `scatter` not `heatmap`
|
||||
- sort x axes when passing a vector of strings as x
|
||||
- improve performance of marker_z
|
||||
- update CI to 1.0
|
||||
- minor depwarn ifixes
|
||||
- only draw one colorbar with GR
|
||||
- add colorbar_title to GR and pgfplots
|
||||
- fix savefig with latexstrings for PyPlot
|
||||
- fix NamedTuple integration
|
||||
- don't export `P2` and `P3`
|
||||
- make it possible to use 2-argument function as argument to marker_z
|
||||
- make `plotattr` work again
|
||||
|
||||
## 0.19.3
|
||||
- fix some julia 0.7 deprecations
|
||||
- fix 32-bit OS functionality
|
||||
|
||||
## 0.19.2
|
||||
- several small fixes for 1.0 compatibility
|
||||
|
||||
## 0.19.1
|
||||
- don't broadcast plot_color
|
||||
|
||||
## 0.19.0
|
||||
- Refactor conditional loading to use Requires
|
||||
- Many fixes for 1.0 compatibility
|
||||
|
||||
## 0.18.0
|
||||
- update minor version to 0.7
|
||||
|
||||
## 0.17.4
|
||||
- fix thickness_scaling for pyplot
|
||||
|
||||
## 0.17.3
|
||||
- Log-scale heatmap edge computation
|
||||
- Fix size and dpi for GR and PyPlot
|
||||
- Fix fillrange with line segments on PyPlot and Plotly
|
||||
- fix flip for heatmap and image on GR
|
||||
- New attributes for PGFPlots
|
||||
- Widen axes for most series types and log scales
|
||||
- Plotly: fix log scale with no ticks
|
||||
- Fix axis flip on Plotly
|
||||
- Fix hover and zcolor interaction in Plotly
|
||||
- WebIO integration for PlotlyJS backend
|
||||
|
||||
## 0.17.2
|
||||
- fix single subplot in plotly
|
||||
- implement `(xyz)lims = :round`
|
||||
- PyPlot: fix bg_legend = invisible()
|
||||
- set fallback tick specification for axes with discrete values
|
||||
- restructure of show methods
|
||||
|
||||
## 0.17.1
|
||||
- Fix contour for PGFPlots
|
||||
- 32Bit fix: Int64 -> Int
|
||||
- Make series of shapes and segments toggle together in Plotly(JS)
|
||||
- Fix marker arguments
|
||||
- Fix processing order of series recipes
|
||||
- Fix Plotly(JS) ribbon
|
||||
- Contour plots with x,y in grid form on PyPlot
|
||||
|
||||
## 0.17.0
|
||||
- Add GR dependency to make it the default backend
|
||||
- Improve histogram2d bin estimation
|
||||
- Allow vector arguments for certain series attributes and support line_z and fill_z on GR, PyPlot, Plotly(JS) and PGFPlots
|
||||
- Automatic scientific notation for tick labels
|
||||
- Allow to set the theme in PLOTS_DEFAULTS
|
||||
- Implement plots_heatmap seriestype providing a Plots recipe for heatmaps
|
||||
|
||||
## 0.16.0
|
||||
- fix 3D plotting in PyPlot
|
||||
- Infinite objects
|
||||
|
||||
## 0.15.1
|
||||
|
||||
- fix scientific notation for labels in GR
|
||||
- fix labels with logscale
|
||||
- fix image cropping with GR
|
||||
- fix grouping of annotations
|
||||
- fix annotations in Plotly
|
||||
- allow saving notebook with plots as pdf from IJulia
|
||||
- fix fillrange and ribbon for step recipes
|
||||
- implement native ticks that respond to zoom
|
||||
- fix bar plot with one bar
|
||||
- contour labels and colorbar fixes
|
||||
- interactive linked axis for PyPlot
|
||||
- add `NamedTuple` syntax to group with named legend
|
||||
- use bar recipe in Plotly
|
||||
- implement categorical ticks
|
||||
|
||||
## 0.15.0
|
||||
|
||||
- improve resolution of png output of GR with savefig()
|
||||
- add check for ticks=nothing
|
||||
- allow transparency in heatmaps
|
||||
- fix line_z for GR
|
||||
- fix legendcolor for pyplot
|
||||
- fix pyplot ignoring alpha values of images
|
||||
- don't let `abline!` change subplot limits
|
||||
- update showtheme recipe
|
||||
|
||||
## 0.14.2
|
||||
|
||||
- fix plotly bar lines bug
|
||||
- allow passing multiple series to `ribbon`
|
||||
- add a new example for `line_z`
|
||||
|
||||
## 0.14.1
|
||||
|
||||
- Add linestyle argument to the legend
|
||||
- Plotly: bar_width and stroke_width support for bar plots
|
||||
- abline! does not change axis limits
|
||||
- Fix default log scale ticks in GR backend
|
||||
- Use the :fontsize keys so the scalefontsizes command works
|
||||
- Prepare support for new PlotTheme type in PlotThemes
|
||||
|
||||
## 0.14.0
|
||||
|
||||
- remove use of imagemagick; saving gifs now requires ffmpeg
|
||||
- improvements to ffmpeg gif quality and speed
|
||||
- overhaul of fonts, allows setting fonts in recipes and with magic arguments
|
||||
- added `camera` attribute to control camera position for 3d plots
|
||||
- added `showaxis` attribute to control which axes to display
|
||||
- improvements of polar plots axes, and better backend consistency
|
||||
- changed the 'spy' recipe back to using heatmap
|
||||
- added `scatterpath` seriestype
|
||||
- allow plotlyjs to save svg
|
||||
- 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
|
||||
- allow changing the color of legend box
|
||||
- implement `title_location` for gr
|
||||
- add `hline` marker to pgfplots - fixes errorbars
|
||||
- pyplot legends now show marker types
|
||||
- pyplot colorbars take font style from y axis
|
||||
- pyplot tickmarks color the same as axis color
|
||||
- allow setting linewidth for contour in gr
|
||||
- allow legend to be outside plot area for pgfplots
|
||||
- expand axis extrema for heatmap
|
||||
- extendg grid lines to axis limits
|
||||
- fix `line_z` for pyplot and gr
|
||||
- fixed colorbar problem for flipped axes with gr
|
||||
- fix marker_z for 3d plots in gr
|
||||
- fix `weights` functionality for histograms
|
||||
- fix gr annotations with colorbar
|
||||
- fix aspect ratio in gr
|
||||
- fix "hidden window" problem after savefig in gr
|
||||
- fix pgfplots logscale ticks error
|
||||
- fix pgfplots legends symbols
|
||||
- fix axis linking for plotlyjs
|
||||
- fix plotting of grayscale images
|
||||
|
||||
## 0.13.1
|
||||
|
||||
- fix a bug when passing a vector of functions with no bounds (e.g. `plot([sin, cos])`)
|
||||
- export `pct` and `px` from Plots.PlotMeasures
|
||||
|
||||
## 0.13.0
|
||||
|
||||
- support `plotattributes` rather than `d` in recipes
|
||||
- no longer export `w`, `h` and names from Measures.jl; use `using Plots.PlotMeasures` to get these names back
|
||||
- `bar_width` now depends on the minimum distance between bars, not the mean
|
||||
- better automatic x axis limits for plotting Functions
|
||||
- `tick_direction` attribute now allows ticks to be on the inside of the plot border
|
||||
- removed a bug where `p1 = plot(randn(10)); plot(p1, p2)` made `display(p1)` impossible
|
||||
- allow `plot([])` to generate an empty plot
|
||||
- add `origin` framestyle
|
||||
- ensure finite bin number on histograms with only one unique value
|
||||
- better automatic histogram bins for 2d histograms
|
||||
- more informative error message on passing unsupported seriestype in a recipe
|
||||
- allow grouping in user recipes
|
||||
- GR now has `line_z` and `fill_z` attributes for determining the color of shapes and lines
|
||||
- change GR default view angle for 3D plots to match that of PyPlot
|
||||
- fix `clims` on GR
|
||||
- fix `marker_z` for plotly backend
|
||||
- implement `framestyle` for plotly
|
||||
- fix logscale bug error for values < 1e-16 on pyplot
|
||||
- fix an issue on pyplot where >1 colorbar would be shown if there was >1 series
|
||||
- fix `writemime` for eps
|
||||
|
||||
## 0.12.4
|
||||
|
||||
- added a new `framestyle` argument with choices: :box, :semi, :axes, :grid and :none
|
||||
- changed the default bar width to 0.8
|
||||
- added working ribbon to plotly backend
|
||||
- ensure that automatic ticks always generate 4 to 8 ticks
|
||||
- group now groups keyword arguments of the same length as the input
|
||||
- allow passing DateTime objects as ticks
|
||||
- allow specifying the number of ticks as an integre
|
||||
- fix bug on errorbars in gr
|
||||
- fixed some but not all world age issues
|
||||
- better margin with room for text
|
||||
- added a `match` option for linecolor
|
||||
- better error message un unsupported series types
|
||||
- add a 'stride' keyword for the pyplot backend
|
||||
|
||||
## 0.12.3
|
||||
|
||||
@@ -61,7 +263,7 @@
|
||||
#### 0.11.0
|
||||
|
||||
- julia 0.6 compatibility
|
||||
- matplotlib 0.2.0 compatibility
|
||||
- matplotlib 2.0 compatibility
|
||||
- add inspectdr backend
|
||||
- improved histogram functionality:
|
||||
- added a `:stephist` and `:scatterhist` series type as well as ``:barhist` (the default)
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://ci.appveyor.com/project/tbreloff/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) -->
|
||||
|
||||
#### Author: Thomas Breloff (@tbreloff)
|
||||
#### Created by Tom Breloff (@tbreloff)
|
||||
|
||||
#### Maintained by the [JuliaPlot members](https://github.com/orgs/JuliaPlots/people)
|
||||
|
||||
Plots is a plotting API and toolset. My goals with the package are:
|
||||
|
||||
@@ -19,5 +22,3 @@ Plots is a plotting API and toolset. My goals with the package are:
|
||||
- **Consistent**. Don't commit to one graphics package, use the same code everywhere.
|
||||
- **Lightweight**. Very few dependencies.
|
||||
- **Smart**. Attempts to figure out what you **want** it to do... not just what you **tell** it.
|
||||
|
||||
View the [full documentation](http://juliaplots.github.io).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
julia 0.6
|
||||
julia 1.0
|
||||
|
||||
RecipesBase 0.2.0
|
||||
RecipesBase 0.6.0
|
||||
PlotUtils 0.4.1
|
||||
PlotThemes 0.1.3
|
||||
Reexport
|
||||
@@ -13,3 +13,4 @@ JSON
|
||||
NaNMath
|
||||
Requires
|
||||
Contour
|
||||
GR 0.34.0
|
||||
|
||||
+26
-22
@@ -1,15 +1,23 @@
|
||||
environment:
|
||||
matrix:
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
|
||||
- julia_version: 0.7
|
||||
- julia_version: 1
|
||||
- julia_version: nightly
|
||||
|
||||
platform:
|
||||
- x86 # 32-bit
|
||||
- x64 # 64-bit
|
||||
|
||||
# # Uncomment the following lines to allow failures on nightly julia
|
||||
# # (tests will run but not make your overall status red)
|
||||
matrix:
|
||||
allow_failures:
|
||||
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" #check and address
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
|
||||
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
|
||||
- julia_version: nightly
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release-.*/
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
@@ -18,22 +26,18 @@ notifications:
|
||||
on_build_status_changed: false
|
||||
|
||||
install:
|
||||
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12"
|
||||
# If there's a newer build queued for the same PR, cancel this one
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
|
||||
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
|
||||
throw "There are newer queued builds for this pull request, failing early." }
|
||||
# Download most recent Julia Windows binary
|
||||
- ps: (new-object net.webclient).DownloadFile($env:JULIA_URL, "C:\projects\julia-binary.exe")
|
||||
# Run installer silently, output to C:\projects\julia
|
||||
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
|
||||
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
|
||||
|
||||
build_script:
|
||||
# Need to convert from shallow to complete for Pkg.clone to work
|
||||
- IF EXIST .git\shallow (git fetch --unshallow)
|
||||
- C:\projects\julia\bin\julia -e "versioninfo(); Pkg.clone(pwd(), \"Plots\"); Pkg.build(\"Plots\")"
|
||||
- echo "%JL_BUILD_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
|
||||
|
||||
test_script:
|
||||
# - C:\projects\julia\bin\julia -e "Pkg.test(\"Plots\")"
|
||||
- C:\projects\julia\bin\julia -e "include(Pkg.dir(\"Plots\", \"test\", \"travis_commands.jl\"))"
|
||||
- echo "%JL_TEST_SCRIPT%"
|
||||
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"
|
||||
|
||||
# # Uncomment to support code coverage upload. Should only be enabled for packages
|
||||
# # which would have coverage gaps without running on Windows
|
||||
# on_success:
|
||||
# - echo "%JL_CODECOV_SCRIPT%"
|
||||
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
|
||||
|
||||
Vendored
+1
-1
@@ -3,6 +3,6 @@
|
||||
|
||||
local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
|
||||
if !isfile(local_fn)
|
||||
info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
|
||||
@info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
|
||||
download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
|
||||
end
|
||||
|
||||
+57
-54
@@ -1,11 +1,11 @@
|
||||
__precompile__(true)
|
||||
|
||||
module Plots
|
||||
|
||||
using Reexport
|
||||
|
||||
import StaticArrays
|
||||
using StaticArrays.FixedSizeArrays
|
||||
using Dates, Printf, Statistics, Base64, LinearAlgebra
|
||||
import SparseArrays: findnz
|
||||
|
||||
@reexport using RecipesBase
|
||||
import RecipesBase: plot, plot!, animate
|
||||
@@ -14,6 +14,9 @@ using Base.Meta
|
||||
@reexport using PlotThemes
|
||||
import Showoff
|
||||
import StatsBase
|
||||
import JSON
|
||||
|
||||
using Requires
|
||||
|
||||
export
|
||||
grid,
|
||||
@@ -101,8 +104,6 @@ export
|
||||
rotate,
|
||||
rotate!,
|
||||
center,
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve,
|
||||
|
||||
plotattr
|
||||
@@ -110,13 +111,13 @@ export
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import NaNMath # define functions that ignores NaNs. To overcome the destructive effects of https://github.com/JuliaLang/julia/pull/12563
|
||||
ignorenan_minimum{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.minimum(x)
|
||||
ignorenan_minimum(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.minimum(x)
|
||||
ignorenan_minimum(x) = Base.minimum(x)
|
||||
ignorenan_maximum{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.maximum(x)
|
||||
ignorenan_maximum(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.maximum(x)
|
||||
ignorenan_maximum(x) = Base.maximum(x)
|
||||
ignorenan_mean{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.mean(x)
|
||||
ignorenan_mean(x) = Base.mean(x)
|
||||
ignorenan_extrema{F<:AbstractFloat}(x::AbstractArray{F}) = NaNMath.extrema(x)
|
||||
ignorenan_mean(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.mean(x)
|
||||
ignorenan_mean(x) = Statistics.mean(x)
|
||||
ignorenan_extrema(x::AbstractArray{F}) where {F<:AbstractFloat} = NaNMath.extrema(x)
|
||||
ignorenan_extrema(x) = Base.extrema(x)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -133,11 +134,20 @@ ignorenan_extrema(x) = Base.extrema(x)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import Measures
|
||||
module PlotMeasures
|
||||
import Measures
|
||||
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
|
||||
const BBox = Measures.Absolute2DBox
|
||||
export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
|
||||
|
||||
# allow pixels and percentages
|
||||
const px = AbsoluteLength(0.254)
|
||||
const pct = Length{:pct, Float64}(1.0)
|
||||
export BBox, BoundingBox, mm, cm, inch, px, pct, pt, w, h
|
||||
end
|
||||
|
||||
using .PlotMeasures
|
||||
import .PlotMeasures: Length, AbsoluteLength, Measure, width, height
|
||||
# ---------------------------------------------------------
|
||||
|
||||
include("types.jl")
|
||||
@@ -158,6 +168,7 @@ include("arg_desc.jl")
|
||||
include("plotattr.jl")
|
||||
include("backends.jl")
|
||||
include("output.jl")
|
||||
include("init.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -171,10 +182,13 @@ include("output.jl")
|
||||
@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
|
||||
@@ -206,13 +220,13 @@ xlabel!(s::AbstractString; kw...) = plot!(; xlabel = s, kw...)
|
||||
ylabel!(s::AbstractString; kw...) = plot!(; ylabel = s, kw...)
|
||||
|
||||
"Set xlims for an existing plot"
|
||||
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; xlims = lims, kw...)
|
||||
xlims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; xlims = lims, kw...)
|
||||
|
||||
"Set ylims for an existing plot"
|
||||
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; ylims = lims, kw...)
|
||||
ylims!(lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(; ylims = lims, kw...)
|
||||
|
||||
"Set zlims for an existing plot"
|
||||
zlims!{T<:Real,S<:Real}(lims::Tuple{T,S}; kw...) = plot!(; zlims = lims, kw...)
|
||||
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...)
|
||||
@@ -220,19 +234,19 @@ zlims!(zmin::Real, zmax::Real; kw...) = plot!(; zlims = (zmi
|
||||
|
||||
|
||||
"Set xticks for an existing plot"
|
||||
xticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; xticks = v, kw...)
|
||||
xticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; xticks = v, kw...)
|
||||
|
||||
"Set yticks for an existing plot"
|
||||
yticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; yticks = v, kw...)
|
||||
yticks!(v::TicksArgs; kw...) where {T<:Real} = plot!(; yticks = v, kw...)
|
||||
|
||||
xticks!{T<:Real,S<:AbstractString}(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; xticks = (ticks,labels), kw...)
|
||||
yticks!{T<:Real,S<:AbstractString}(
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; yticks = (ticks,labels), 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!{T<:Tuple}(anns::AVec{T}; 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...)
|
||||
@@ -249,29 +263,29 @@ xgrid!(args...; kw...) = plot!(; xgrid = args
|
||||
ygrid!(args...; kw...) = plot!(; ygrid = args, kw...)
|
||||
|
||||
let PlotOrSubplot = Union{Plot, Subplot}
|
||||
title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||
ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||
xlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
|
||||
ylims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
|
||||
zlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
|
||||
xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||
ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||
zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||
xticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
|
||||
yticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
|
||||
xticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
yticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
|
||||
ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
|
||||
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
annotate!{T<:Tuple}(plt::PlotOrSubplot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||
yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||
xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||
yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
global xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||
global ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||
global xlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; xlims = lims, kw...)
|
||||
global ylims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; ylims = lims, kw...)
|
||||
global zlims!(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) where {T<:Real,S<:Real} = plot!(plt; zlims = lims, kw...)
|
||||
global xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||
global ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||
global zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||
global xticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) where {T<:Real} = plot!(plt; xticks = ticks, kw...)
|
||||
global yticks!(plt::PlotOrSubplot, ticks::TicksArgs; kw...) where {T<:Real} = plot!(plt; yticks = ticks, kw...)
|
||||
global xticks!(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
global yticks!(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) where {T<:Real,S<:AbstractString} = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
global xgrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xgrid = args, kw...)
|
||||
global ygrid!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; ygrid = args, kw...)
|
||||
global annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
global annotate!(plt::PlotOrSubplot, anns::AVec{T}; kw...) where {T<:Tuple} = plot!(plt; annotation = anns, kw...)
|
||||
global xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||
global yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||
global xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||
global yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
end
|
||||
|
||||
|
||||
@@ -279,15 +293,4 @@ end
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
# for compatibility with Requires.jl:
|
||||
@init begin
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
for (k,v) in Main.PLOTS_DEFAULTS
|
||||
default(k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
end # module
|
||||
|
||||
+30
-35
@@ -1,5 +1,5 @@
|
||||
"Represents an animation object"
|
||||
immutable Animation
|
||||
struct Animation
|
||||
dir::String
|
||||
frames::Vector{String}
|
||||
end
|
||||
@@ -14,7 +14,7 @@ end
|
||||
|
||||
Add a plot (the current plot if not specified) to an existing animation
|
||||
"""
|
||||
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
|
||||
function frame(anim::Animation, plt::P=current()) where P<:AbstractPlot
|
||||
i = length(anim.frames) + 1
|
||||
filename = @sprintf("%06d.png", i)
|
||||
png(plt, joinpath(anim.dir, filename))
|
||||
@@ -25,7 +25,7 @@ giffn() = (isijulia() ? "tmp.gif" : tempname()*".gif")
|
||||
movfn() = (isijulia() ? "tmp.mov" : tempname()*".mov")
|
||||
mp4fn() = (isijulia() ? "tmp.mp4" : tempname()*".mp4")
|
||||
|
||||
type FrameIterator
|
||||
mutable struct FrameIterator
|
||||
itr
|
||||
every::Int
|
||||
kw
|
||||
@@ -54,47 +54,40 @@ end
|
||||
# -----------------------------------------------
|
||||
|
||||
"Wraps the location of an animated gif so that it can be displayed"
|
||||
immutable AnimatedGif
|
||||
struct AnimatedGif
|
||||
filename::String
|
||||
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; kw...)
|
||||
mp4(anim::Animation, fn = mp4fn(); 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...)
|
||||
|
||||
const _imagemagick_initialized = Ref(false)
|
||||
|
||||
function buildanimation(animdir::AbstractString, fn::AbstractString;
|
||||
fps::Integer = 20, loop::Integer = 0)
|
||||
function buildanimation(animdir::AbstractString, fn::AbstractString,
|
||||
is_animated_gif::Bool=true;
|
||||
fps::Integer = 20, loop::Integer = 0,
|
||||
variable_palette::Bool=false,
|
||||
show_msg::Bool=true)
|
||||
fn = abspath(fn)
|
||||
try
|
||||
if !_imagemagick_initialized[]
|
||||
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
|
||||
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
|
||||
include(file)
|
||||
end
|
||||
_imagemagick_initialized[] = true
|
||||
|
||||
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`)
|
||||
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"`)
|
||||
# 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`)
|
||||
end
|
||||
|
||||
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||
# high quality
|
||||
speed = round(Int, 100 / fps)
|
||||
run(`convert -delay $speed -loop $loop $(joinpath(animdir, "*.png")) -alpha off $fn`)
|
||||
|
||||
catch err
|
||||
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
||||
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`.
|
||||
You may also need to install the imagemagick c++ library through your operating system.
|
||||
Will try ffmpeg, but it's lower quality...)""")
|
||||
|
||||
# low quality
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -y $fn`)
|
||||
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
|
||||
else
|
||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -pix_fmt yuv420p -y $fn`)
|
||||
end
|
||||
|
||||
info("Saved animation to ", fn)
|
||||
show_msg && @info("Saved animation to ", fn)
|
||||
AnimatedGif(fn)
|
||||
end
|
||||
|
||||
@@ -123,6 +116,7 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
# add the call to frame to the end of each iteration
|
||||
animsym = gensym("anim")
|
||||
countersym = gensym("counter")
|
||||
freqassert = :()
|
||||
block = forloop.args[2]
|
||||
|
||||
# create filter
|
||||
@@ -135,7 +129,7 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
# filter every `freq` frames (starting with the first frame)
|
||||
@assert n == 2
|
||||
freq = args[2]
|
||||
@assert isa(freq, Integer) && freq > 0
|
||||
freqassert = :(@assert isa($freq, Integer) && $freq > 0)
|
||||
:(mod1($countersym, $freq) == 1)
|
||||
|
||||
elseif args[1] == :when
|
||||
@@ -148,15 +142,16 @@ function _animate(forloop::Expr, args...; callgif = false)
|
||||
end
|
||||
|
||||
push!(block.args, :(if $filterexpr; frame($animsym); end))
|
||||
push!(block.args, :($countersym += 1))
|
||||
push!(block.args, :(global $countersym += 1))
|
||||
|
||||
# add a final call to `gif(anim)`?
|
||||
retval = callgif ? :(gif($animsym)) : animsym
|
||||
|
||||
# full expression:
|
||||
esc(quote
|
||||
$freqassert # if filtering, check frequency is an Integer > 0
|
||||
$animsym = Animation() # init animation object
|
||||
$countersym = 1 # init iteration counter
|
||||
global $countersym = 1 # init iteration counter
|
||||
$forloop # for loop, saving a frame after each iteration
|
||||
$retval # return the animation object, or the gif
|
||||
end)
|
||||
|
||||
+42
-9
@@ -21,14 +21,14 @@ const _arg_desc = KW(
|
||||
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
||||
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
|
||||
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `linspace(extrema(x)..., 25)`",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd). For fine-grained control pass a Vector of break values, e.g. `range(min(x), stop = extrema(x), length = 25)`",
|
||||
:smooth => "Bool. Add a regression line?",
|
||||
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
|
||||
:x => "Various. Input data. First Dimension",
|
||||
:y => "Various. Input data. Second Dimension",
|
||||
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
|
||||
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
||||
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
|
||||
: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.",
|
||||
: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).",
|
||||
@@ -43,6 +43,7 @@ const _arg_desc = KW(
|
||||
: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.",
|
||||
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
|
||||
: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.",
|
||||
@@ -64,13 +65,20 @@ const _arg_desc = KW(
|
||||
: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",
|
||||
:thickness_scaling => "Number. Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1.",
|
||||
:display_type => "Symbol (`:auto`, `:gui`, or `:inline`). When supported, `display` will either open a GUI window or plot inline.",
|
||||
:extra_kwargs => "KW (Dict{Symbol,Any}). Pass a map of extra keyword args which may be specific to a backend.",
|
||||
:fontfamily => "String or Symbol. Default font family for title, legend entries, tick labels and guides",
|
||||
|
||||
# subplot args
|
||||
:title => "String. Subplot title.",
|
||||
:title_location => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`",
|
||||
:titlefont => "Font. Font of subplot title.",
|
||||
:titlefontfamily => "String or Symbol. Font family of subplot title.",
|
||||
:titlefontsize => "Integer. Font pointsize of subplot title.",
|
||||
:titlefonthalign => "Symbol. Font horizontal alignment of subplot title: :hcenter, :left, :right or :center",
|
||||
:titlefontvalign => "Symbol. Font vertical alignment of subplot title: :vcenter, :top, :bottom or :center",
|
||||
:titlefontrotation => "Real. Font rotation of subplot title",
|
||||
:titlefontcolor => "Color Type. Font color of subplot title",
|
||||
:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.",
|
||||
:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.",
|
||||
:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).",
|
||||
@@ -79,7 +87,12 @@ const _arg_desc = KW(
|
||||
: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)",
|
||||
:legendtitle => "String or nothing (default). Sets the legend title.",
|
||||
: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",
|
||||
: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.",
|
||||
@@ -94,25 +107,45 @@ const _arg_desc = KW(
|
||||
:subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.",
|
||||
:colorbar_title => "String. Title of colorbar.",
|
||||
:framestyle => "Symbol. Style of the axes frame. Choose from $(_allFramestyles)",
|
||||
:camera => "NTuple{2, Real}. Sets the view angle (azimuthal, elevation) for 3D plots",
|
||||
|
||||
# axis args
|
||||
:guide => "String. Axis guide (label).",
|
||||
:lims => "NTuple{2,Number}. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).",
|
||||
: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, or :auto. A method which converts a number to a string for tick labeling.",
|
||||
:tickfont => "Font. Font of axis tick labels.",
|
||||
:guidefont => "Font. Font of axis guide (label).",
|
||||
: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",
|
||||
:tickfontvalign => "Symbol. Font vertical alignment of tick labels: :vcenter, :top, :bottom or :center",
|
||||
:tickfontrotation => "Real. Font rotation of tick labels",
|
||||
:tickfontcolor => "Color Type. Font color of tick labels",
|
||||
:guidefontfamily => "String or Symbol. Font family of axes guides.",
|
||||
:guidefontsize => "Integer. Font pointsize of axes guides.",
|
||||
:guidefonthalign => "Symbol. Font horizontal alignment of axes guides: :hcenter, :left, :right or :center",
|
||||
:guidefontvalign => "Symbol. Font vertical alignment of axes guides: :vcenter, :top, :bottom or :center",
|
||||
:guidefontrotation => "Real. Font rotation of axes guides",
|
||||
:guidefontcolor => "Color Type. Font color of axes guides",
|
||||
:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.",
|
||||
:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).",
|
||||
:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.",
|
||||
:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).",
|
||||
:mirror => "Bool. Switch the side of the tick labels (right or top).",
|
||||
:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`",
|
||||
:grid => "Bool, Symbol, String or `nothing`. Show the grid lines? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`",
|
||||
:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
|
||||
:gridalpha => "Number in [0,1]. The alpha/opacity override for the grid lines.",
|
||||
:gridstyle => "Symbol. Style of the grid lines. Choose from $(_allStyles)",
|
||||
:gridlinewidth => "Number. Width of the grid lines (in pixels)",
|
||||
:foreground_color_minor_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of minor grid lines.",
|
||||
:minorgrid => "Bool. Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines",
|
||||
:minorticks => "Integer. Intervals to divide the gap between major ticks into",
|
||||
:minorgridalpha => "Number in [0,1]. The alpha/opacity override for the minorgrid lines.",
|
||||
:minorgridstyle => "Symbol. Style of the minor grid lines. Choose from $(_allStyles)",
|
||||
:minorgridlinewidth => "Number. Width of the minor grid lines (in pixels)",
|
||||
:tick_direction => "Symbol. Direction of the ticks. `:in` or `:out`",
|
||||
:showaxis => "Bool, Symbol or String. Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`",
|
||||
:widen => "Bool. Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders. Defaults to `true`.",
|
||||
)
|
||||
|
||||
+512
-267
File diff suppressed because it is too large
Load Diff
+273
-94
@@ -8,7 +8,7 @@
|
||||
|
||||
function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
# init with values from _plot_defaults
|
||||
d = KW(
|
||||
plotattributes = KW(
|
||||
:letter => letter,
|
||||
# :extrema => (Inf, -Inf),
|
||||
:extrema => Extrema(),
|
||||
@@ -22,14 +22,14 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
for (k,v) in _axis_defaults
|
||||
lk = Symbol(letter, k)
|
||||
lv = _axis_defaults_byletter[lk]
|
||||
d[k] = (lv == :match ? v : lv)
|
||||
plotattributes[k] = (lv == :match ? v : lv)
|
||||
end
|
||||
|
||||
# merge!(d, _axis_defaults)
|
||||
d[:discrete_values] = []
|
||||
# merge!(plotattributes, _axis_defaults)
|
||||
plotattributes[:discrete_values] = []
|
||||
|
||||
# update the defaults
|
||||
attr!(Axis([sp], d), args...; kw...)
|
||||
attr!(Axis([sp], plotattributes), args...; kw...)
|
||||
end
|
||||
|
||||
function get_axis(sp::Subplot, letter::Symbol)
|
||||
@@ -41,43 +41,46 @@ function get_axis(sp::Subplot, letter::Symbol)
|
||||
end::Axis
|
||||
end
|
||||
|
||||
function process_axis_arg!(d::KW, arg, letter = "")
|
||||
function process_axis_arg!(plotattributes::KW, arg, letter = "")
|
||||
T = typeof(arg)
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
if typeof(arg) <: Font
|
||||
d[Symbol(letter,:tickfont)] = arg
|
||||
d[Symbol(letter,:guidefont)] = arg
|
||||
plotattributes[Symbol(letter,:tickfont)] = arg
|
||||
plotattributes[Symbol(letter,:guidefont)] = arg
|
||||
|
||||
elseif arg in _allScales
|
||||
d[Symbol(letter,:scale)] = arg
|
||||
plotattributes[Symbol(letter,:scale)] = arg
|
||||
|
||||
elseif arg in (:flip, :invert, :inverted)
|
||||
d[Symbol(letter,:flip)] = true
|
||||
plotattributes[Symbol(letter,:flip)] = true
|
||||
|
||||
elseif T <: AbstractString
|
||||
d[Symbol(letter,:guide)] = arg
|
||||
plotattributes[Symbol(letter,:guide)] = arg
|
||||
|
||||
# xlims/ylims
|
||||
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
|
||||
sym = typeof(arg[1]) <: Number ? :lims : :ticks
|
||||
d[Symbol(letter,sym)] = arg
|
||||
plotattributes[Symbol(letter,sym)] = arg
|
||||
|
||||
# xticks/yticks
|
||||
elseif T <: AVec
|
||||
d[Symbol(letter,:ticks)] = arg
|
||||
plotattributes[Symbol(letter,:ticks)] = arg
|
||||
|
||||
elseif arg == nothing
|
||||
d[Symbol(letter,:ticks)] = []
|
||||
plotattributes[Symbol(letter,:ticks)] = []
|
||||
|
||||
elseif T <: Bool || arg in _allShowaxisArgs
|
||||
plotattributes[Symbol(letter,:showaxis)] = showaxis(arg, letter)
|
||||
|
||||
elseif typeof(arg) <: Number
|
||||
d[Symbol(letter,:rotation)] = arg
|
||||
plotattributes[Symbol(letter,:rotation)] = arg
|
||||
|
||||
elseif typeof(arg) <: Function
|
||||
d[Symbol(letter,:formatter)] = arg
|
||||
plotattributes[Symbol(letter,:formatter)] = arg
|
||||
|
||||
else
|
||||
warn("Skipped $(letter)axis arg $arg")
|
||||
elseif !handleColors!(plotattributes, arg, Symbol(letter, :foreground_color_axis))
|
||||
@warn("Skipped $(letter)axis arg $arg")
|
||||
|
||||
end
|
||||
end
|
||||
@@ -85,28 +88,28 @@ end
|
||||
# update an Axis object with magic args and keywords
|
||||
function attr!(axis::Axis, args...; kw...)
|
||||
# first process args
|
||||
d = axis.d
|
||||
plotattributes = axis.plotattributes
|
||||
for arg in args
|
||||
process_axis_arg!(d, arg)
|
||||
process_axis_arg!(plotattributes, arg)
|
||||
end
|
||||
|
||||
# then override for any keywords... only those keywords that already exists in d
|
||||
# then override for any keywords... only those keywords that already exists in plotattributes
|
||||
for (k,v) in kw
|
||||
if haskey(d, k)
|
||||
if haskey(plotattributes, k)
|
||||
if k == :discrete_values
|
||||
# add these discrete values to the axis
|
||||
for vi in v
|
||||
discrete_value!(axis, vi)
|
||||
end
|
||||
else
|
||||
d[k] = v
|
||||
plotattributes[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# replace scale aliases
|
||||
if haskey(_scaleAliases, d[:scale])
|
||||
d[:scale] = _scaleAliases[d[:scale]]
|
||||
if haskey(_scaleAliases, plotattributes[:scale])
|
||||
plotattributes[:scale] = _scaleAliases[plotattributes[:scale]]
|
||||
end
|
||||
|
||||
axis
|
||||
@@ -114,10 +117,10 @@ end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Base.show(io::IO, axis::Axis) = dumpdict(axis.d, "Axis", true)
|
||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.d, k)
|
||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
|
||||
Base.show(io::IO, axis::Axis) = dumpdict(axis.plotattributes, "Axis", true)
|
||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.plotattributes, k)
|
||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.plotattributes, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.plotattributes, k)
|
||||
ignorenan_extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
|
||||
|
||||
@@ -209,12 +212,20 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
formatter = axis[:formatter]
|
||||
if formatter == :auto
|
||||
# the default behavior is to make strings of the scaled values and then apply the labelfunc
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :auto))
|
||||
elseif formatter == :plain
|
||||
# Leave the numbers in plain format
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, :plain))
|
||||
elseif formatter == :scientific
|
||||
Showoff.showoff(unscaled_ticks, :scientific)
|
||||
else
|
||||
# there was an override for the formatter... use that on the unscaled ticks
|
||||
map(formatter, unscaled_ticks)
|
||||
# if the formatter left us with numbers, still apply the default formatter
|
||||
# However it leave us with the problem of unicode number decoding by the backend
|
||||
# if eltype(unscaled_ticks) <: Number
|
||||
# Showoff.showoff(unscaled_ticks, :auto)
|
||||
# end
|
||||
end
|
||||
else
|
||||
# no finite ticks to show...
|
||||
@@ -231,16 +242,35 @@ function get_ticks(axis::Axis)
|
||||
ticks = _transform_ticks(axis[:ticks])
|
||||
ticks in (nothing, false) && return nothing
|
||||
|
||||
# treat :native ticks as :auto
|
||||
ticks = ticks == :native ? :auto : ticks
|
||||
|
||||
dvals = axis[:discrete_values]
|
||||
cv, dv = if !isempty(dvals) && ticks == :auto
|
||||
# discrete ticks...
|
||||
axis[:continuous_values], dvals
|
||||
elseif ticks == :auto
|
||||
# compute optimal ticks and labels
|
||||
optimal_ticks_and_labels(axis)
|
||||
cv, dv = if typeof(ticks) <: Symbol
|
||||
if !isempty(dvals)
|
||||
# discrete ticks...
|
||||
n = length(dvals)
|
||||
rng = if ticks == :auto
|
||||
Int[round(Int,i) for i in range(1, stop=n, length=15)]
|
||||
else # if ticks == :all
|
||||
1:n
|
||||
end
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
elseif ispolar(axis.sps[1]) && axis[:letter] == :x
|
||||
#force theta axis to be full circle
|
||||
(collect(0:pi/4:7pi/4), string.(0:45:315))
|
||||
else
|
||||
# compute optimal ticks and labels
|
||||
optimal_ticks_and_labels(axis)
|
||||
end
|
||||
elseif typeof(ticks) <: Union{AVec, Int}
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
if !isempty(dvals) && typeof(ticks) <: Int
|
||||
rng = Int[round(Int,i) for i in range(1, stop=length(dvals), length=ticks)]
|
||||
axis[:continuous_values][rng], dvals[rng]
|
||||
else
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
end
|
||||
elseif typeof(ticks) <: NTuple{2, Any}
|
||||
# assuming we're passed (ticks, labels)
|
||||
ticks
|
||||
@@ -249,19 +279,41 @@ function get_ticks(axis::Axis)
|
||||
end
|
||||
# @show ticks dvals cv dv
|
||||
|
||||
# TODO: better/smarter cutoff values for sampling ticks
|
||||
if length(cv) > 30 && ticks == :auto
|
||||
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
|
||||
cv[rng], dv[rng]
|
||||
else
|
||||
cv, dv
|
||||
end
|
||||
return cv, dv
|
||||
end
|
||||
|
||||
_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
|
||||
ticks = ticks[1]
|
||||
length(ticks) < 2 && return nothing
|
||||
|
||||
amin, amax = axis_limits(axis)
|
||||
#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
|
||||
ratio = 1
|
||||
else
|
||||
return nothing
|
||||
end
|
||||
first_step = ticks[2] - ticks[1]
|
||||
last_step = ticks[end] - ticks[end-1]
|
||||
ticks = [ticks[1] - first_step/ratio; ticks; ticks[end] + last_step*ratio]
|
||||
|
||||
#Default to 5 intervals between major ticks
|
||||
n = typeof(axis[:minorticks]) <: Integer && axis[:minorticks] > 1 ? axis[:minorticks] : 5
|
||||
minorticks = typeof(ticks[1])[]
|
||||
for (i,hi) in enumerate(ticks[2:end])
|
||||
lo = ticks[i]
|
||||
append!(minorticks,collect(lo + (hi-lo)/n :(hi-lo)/n: hi - (hi-lo)/2n))
|
||||
end
|
||||
minorticks[amin .<= minorticks .<= amax]
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -270,14 +322,14 @@ function reset_extrema!(sp::Subplot)
|
||||
sp[Symbol(asym,:axis)][:extrema] = Extrema()
|
||||
end
|
||||
for series in sp.series_list
|
||||
expand_extrema!(sp, series.d)
|
||||
expand_extrema!(sp, series.plotattributes)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(ex::Extrema, v::Number)
|
||||
ex.emin = NaNMath.min(v, ex.emin)
|
||||
ex.emax = NaNMath.max(v, ex.emax)
|
||||
ex.emin = isfinite(v) ? min(v, ex.emin) : ex.emin
|
||||
ex.emax = isfinite(v) ? max(v, ex.emax) : ex.emax
|
||||
ex
|
||||
end
|
||||
|
||||
@@ -286,17 +338,17 @@ function expand_extrema!(axis::Axis, v::Number)
|
||||
end
|
||||
|
||||
# these shouldn't impact the extrema
|
||||
expand_extrema!(axis::Axis, ::Void) = axis[:extrema]
|
||||
expand_extrema!(axis::Axis, ::Nothing) = axis[:extrema]
|
||||
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
|
||||
|
||||
|
||||
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
|
||||
function expand_extrema!(axis::Axis, v::Tuple{MIN,MAX}) where {MIN<:Number,MAX<:Number}
|
||||
ex = axis[:extrema]
|
||||
ex.emin = NaNMath.min(v[1], ex.emin)
|
||||
ex.emax = NaNMath.max(v[2], ex.emax)
|
||||
ex.emin = isfinite(v[1]) ? min(v[1], ex.emin) : ex.emin
|
||||
ex.emax = isfinite(v[2]) ? max(v[2], ex.emax) : ex.emax
|
||||
ex
|
||||
end
|
||||
function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
|
||||
function expand_extrema!(axis::Axis, v::AVec{N}) where N<:Number
|
||||
ex = axis[:extrema]
|
||||
for vi in v
|
||||
expand_extrema!(ex, vi)
|
||||
@@ -305,16 +357,19 @@ function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(sp::Subplot, d::KW)
|
||||
vert = isvertical(d)
|
||||
function expand_extrema!(sp::Subplot, plotattributes::KW)
|
||||
vert = isvertical(plotattributes)
|
||||
|
||||
# first expand for the data
|
||||
for letter in (:x, :y, :z)
|
||||
data = d[if vert
|
||||
data = plotattributes[if vert
|
||||
letter
|
||||
else
|
||||
letter == :x ? :y : letter == :y ? :x : :z
|
||||
end]
|
||||
if letter != :z && plotattributes[:seriestype] == :straightline && any(series[:seriestype] != :straightline for series in series_list(sp)) && data[1] != data[2]
|
||||
data = [NaN]
|
||||
end
|
||||
axis = sp[Symbol(letter, "axis")]
|
||||
|
||||
if isa(data, Volume)
|
||||
@@ -324,30 +379,30 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
elseif eltype(data) <: Number || (isa(data, Surface) && all(di -> isa(di, Number), data.surf))
|
||||
if !(eltype(data) <: Number)
|
||||
# huh... must have been a mis-typed surface? lets swap it out
|
||||
data = d[letter] = Surface(Matrix{Float64}(data.surf))
|
||||
data = plotattributes[letter] = Surface(Matrix{Float64}(data.surf))
|
||||
end
|
||||
expand_extrema!(axis, data)
|
||||
elseif data != nothing
|
||||
# TODO: need more here... gotta track the discrete reference value
|
||||
# as well as any coord offset (think of boxplot shape coords... they all
|
||||
# correspond to the same x-value)
|
||||
d[letter], d[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
|
||||
expand_extrema!(axis, d[letter])
|
||||
plotattributes[letter], plotattributes[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
|
||||
expand_extrema!(axis, plotattributes[letter])
|
||||
end
|
||||
end
|
||||
|
||||
# # expand for fillrange/bar_width
|
||||
# fillaxis, baraxis = sp.attr[:yaxis], sp.attr[:xaxis]
|
||||
# if isvertical(d)
|
||||
# if isvertical(plotattributes)
|
||||
# fillaxis, baraxis = baraxis, fillaxis
|
||||
# end
|
||||
|
||||
# expand for fillrange
|
||||
fr = d[:fillrange]
|
||||
if fr == nothing && d[:seriestype] == :bar
|
||||
fr = plotattributes[:fillrange]
|
||||
if fr == nothing && plotattributes[:seriestype] == :bar
|
||||
fr = 0.0
|
||||
end
|
||||
if fr != nothing
|
||||
if fr != nothing && !all3D(plotattributes)
|
||||
axis = sp.attr[vert ? :yaxis : :xaxis]
|
||||
if typeof(fr) <: Tuple
|
||||
for fri in fr
|
||||
@@ -359,19 +414,28 @@ function expand_extrema!(sp::Subplot, d::KW)
|
||||
end
|
||||
|
||||
# expand for bar_width
|
||||
if d[:seriestype] == :bar
|
||||
if plotattributes[:seriestype] == :bar
|
||||
dsym = vert ? :x : :y
|
||||
data = d[dsym]
|
||||
data = plotattributes[dsym]
|
||||
|
||||
bw = d[:bar_width]
|
||||
bw = plotattributes[:bar_width]
|
||||
if bw == nothing
|
||||
bw = d[:bar_width] = ignorenan_mean(diff(data))
|
||||
bw = plotattributes[:bar_width] = _bar_width * ignorenan_minimum(filter(x->x>0,diff(sort(data))))
|
||||
end
|
||||
axis = sp.attr[Symbol(dsym, :axis)]
|
||||
expand_extrema!(axis, ignorenan_maximum(data) + 0.5maximum(bw))
|
||||
expand_extrema!(axis, ignorenan_minimum(data) - 0.5minimum(bw))
|
||||
end
|
||||
|
||||
# expand for heatmaps
|
||||
if plotattributes[:seriestype] == :heatmap
|
||||
for letter in (:x, :y)
|
||||
data = plotattributes[letter]
|
||||
axis = sp[Symbol(letter, "axis")]
|
||||
scale = get(plotattributes, Symbol(letter, "scale"), :identity)
|
||||
expand_extrema!(axis, heatmap_edges(data, scale))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function expand_extrema!(sp::Subplot, xmin, xmax, ymin, ymax)
|
||||
@@ -382,21 +446,23 @@ end
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# push the limits out slightly
|
||||
function widen(lmin, lmax)
|
||||
span = lmax - lmin
|
||||
function widen(lmin, lmax, scale = :identity)
|
||||
f, invf = scalefunc(scale), invscalefunc(scale)
|
||||
span = f(lmax) - f(lmin)
|
||||
# eps = NaNMath.max(1e-16, min(1e-2span, 1e-10))
|
||||
eps = NaNMath.max(1e-16, 0.03span)
|
||||
lmin-eps, lmax+eps
|
||||
invf(f(lmin)-eps), invf(f(lmax)+eps)
|
||||
end
|
||||
|
||||
# figure out if widening is a good idea. if there's a scale set it's too tricky,
|
||||
# so lazy out and don't widen
|
||||
# figure out if widening is a good idea.
|
||||
const _widen_seriestypes = (:line, :path, :steppre, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d)
|
||||
|
||||
function default_should_widen(axis::Axis)
|
||||
should_widen = false
|
||||
if axis[:scale] == :identity && !is_2tuple(axis[:lims])
|
||||
if !is_2tuple(axis[:lims])
|
||||
for sp in axis.sps
|
||||
for series in series_list(sp)
|
||||
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
|
||||
if series.plotattributes[:seriestype] in _widen_seriestypes
|
||||
should_widen = true
|
||||
end
|
||||
end
|
||||
@@ -405,6 +471,13 @@ function default_should_widen(axis::Axis)
|
||||
should_widen
|
||||
end
|
||||
|
||||
function round_limits(amin,amax)
|
||||
scale = 10^(1-round(log10(amax - amin)))
|
||||
amin = floor(amin*scale)/scale
|
||||
amax = ceil(amax*scale)/scale
|
||||
amin, amax
|
||||
end
|
||||
|
||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
|
||||
ex = axis[:extrema]
|
||||
@@ -424,8 +497,19 @@ 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 should_widen
|
||||
widen(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)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
elseif should_widen && axis[:widen]
|
||||
widen(amin, amax, axis[:scale])
|
||||
elseif lims == :round
|
||||
round_limits(amin,amax)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
@@ -488,12 +572,12 @@ end
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function pie_labels(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
if haskey(d,:x_discrete_indices)
|
||||
dvals = sp.attr[:xaxis].d[:discrete_values]
|
||||
[dvals[idx] for idx in d[:x_discrete_indices]]
|
||||
plotattributes = series.plotattributes
|
||||
if haskey(plotattributes,:x_discrete_indices)
|
||||
dvals = sp.attr[:xaxis].plotattributes[:discrete_values]
|
||||
[dvals[idx] for idx in plotattributes[:x_discrete_indices]]
|
||||
else
|
||||
d[:x]
|
||||
plotattributes[:x]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -506,46 +590,141 @@ function axis_drawing_info(sp::Subplot)
|
||||
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)
|
||||
xaxis_segs = Segments(2)
|
||||
yaxis_segs = Segments(2)
|
||||
xtick_segs = Segments(2)
|
||||
ytick_segs = Segments(2)
|
||||
xgrid_segs = Segments(2)
|
||||
ygrid_segs = Segments(2)
|
||||
xminorgrid_segs = Segments(2)
|
||||
yminorgrid_segs = Segments(2)
|
||||
xborder_segs = Segments(2)
|
||||
yborder_segs = Segments(2)
|
||||
|
||||
if !(sp[:framestyle] == :none)
|
||||
if sp[:framestyle] != :none
|
||||
# xaxis
|
||||
sp[:framestyle] == :grid || push!(xaxis_segs, (xmin,ymin), (xmax,ymin)) # bottom spine / xaxis
|
||||
sp[:framestyle] in (:semi, :box) && push!(xborder_segs, (xmin,ymax), (xmax,ymax)) # top spine
|
||||
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
|
||||
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))
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin)))
|
||||
t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin)))
|
||||
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)
|
||||
|
||||
for xtick in xticks[1]
|
||||
push!(xaxis_segs, (xtick, ymin), (xtick, t1)) # bottom tick
|
||||
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, t1), (xtick, t2)) # vertical grid
|
||||
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
|
||||
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
|
||||
sp[:framestyle] == :grid || push!(yaxis_segs, (xmin,ymin), (xmin,ymax)) # left spine / yaxis
|
||||
sp[:framestyle] in (:semi, :box) && push!(yborder_segs, (xmax,ymin), (xmax,ymax)) # right spine
|
||||
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
|
||||
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))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin)))
|
||||
t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin)))
|
||||
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)
|
||||
|
||||
for ytick in yticks[1]
|
||||
push!(yaxis_segs, (xmin, ytick), (t1, ytick)) # left tick
|
||||
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, (t1, ytick), (t2, ytick)) # horizontal grid
|
||||
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
|
||||
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, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs
|
||||
xticks, yticks, xaxis_segs, yaxis_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs
|
||||
end
|
||||
|
||||
+161
-51
@@ -1,10 +1,13 @@
|
||||
using Pkg
|
||||
|
||||
immutable NoBackend <: AbstractBackend end
|
||||
struct NoBackend <: AbstractBackend end
|
||||
|
||||
const _backendType = Dict{Symbol, DataType}(:none => NoBackend)
|
||||
const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none)
|
||||
const _backends = Symbol[]
|
||||
const _initialized_backends = Set{Symbol}()
|
||||
const _default_backends = (:none, :gr, :plotly)
|
||||
const _backendPackage = Dict{Symbol, Symbol}()
|
||||
|
||||
"Returns a list of supported backends"
|
||||
backends() = _backends
|
||||
@@ -12,34 +15,38 @@ 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]
|
||||
|
||||
macro init_backend(s)
|
||||
str = lowercase(string(s))
|
||||
package_str = string(s)
|
||||
str = lowercase(package_str)
|
||||
sym = Symbol(str)
|
||||
T = Symbol(string(s) * "Backend")
|
||||
esc(quote
|
||||
immutable $T <: AbstractBackend end
|
||||
struct $T <: AbstractBackend end
|
||||
export $sym
|
||||
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
|
||||
backend_name(::$T) = Symbol($str)
|
||||
backend_package_name(pkg::$T) = backend_package_name(Symbol($str))
|
||||
push!(_backends, Symbol($str))
|
||||
_backendType[Symbol($str)] = $T
|
||||
_backendSymbol[$T] = Symbol($str)
|
||||
include("backends/" * $str * ".jl")
|
||||
_backendPackage[Symbol($str)] = Symbol($package_str)
|
||||
# include("backends/" * $str * ".jl")
|
||||
end)
|
||||
end
|
||||
|
||||
include("backends/web.jl")
|
||||
# include("backends/web.jl")
|
||||
# include("backends/supported.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function add_backend(pkg::Symbol)
|
||||
info("To do a standard install of $pkg, copy and run this:\n\n")
|
||||
@info("To do a standard install of $pkg, copy and run this:\n\n")
|
||||
println(add_backend_string(_backend_instance(pkg)))
|
||||
println()
|
||||
end
|
||||
add_backend_string(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))")
|
||||
|
||||
# don't do anything as a default
|
||||
_create_backend_figure(plt::Plot) = nothing
|
||||
@@ -51,8 +58,8 @@ _series_updated(plt::Plot, series::Series) = nothing
|
||||
|
||||
_before_layout_calcs(plt::Plot) = nothing
|
||||
|
||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
|
||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
|
||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefontsize] * pt
|
||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefontsize] * pt
|
||||
|
||||
"Returns the (width,height) of a text label."
|
||||
function text_size(lablen::Int, sz::Number, rot::Number = 0)
|
||||
@@ -93,7 +100,7 @@ function tick_padding(axis::Axis)
|
||||
# hgt
|
||||
|
||||
# get the height of the rotated label
|
||||
text_size(longest_label, axis[:tickfont].pointsize, rot)[2]
|
||||
text_size(longest_label, axis[:tickfontsize], rot)[2]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -123,7 +130,7 @@ _update_plot_object(plt::Plot) = nothing
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
type CurrentBackend
|
||||
mutable struct CurrentBackend
|
||||
sym::Symbol
|
||||
pkg::AbstractBackend
|
||||
end
|
||||
@@ -134,31 +141,30 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
|
||||
function pickDefaultBackend()
|
||||
env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "")
|
||||
if env_default != ""
|
||||
try
|
||||
Pkg.installed(env_default) # this will error if not installed
|
||||
sym = Symbol(lowercase(env_default))
|
||||
if haskey(_backendType, sym)
|
||||
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 it is not a valid backend package. Choose from:\n\t",
|
||||
join(sort(_backends), "\n\t"))
|
||||
@warn("You have set `PLOTS_DEFAULT_BACKEND=$env_default` but `$(backend_package_name(sym))` is not loaded.")
|
||||
end
|
||||
catch
|
||||
warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not installed.")
|
||||
else
|
||||
@warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t",
|
||||
join(sort(_backends), "\n\t"))
|
||||
end
|
||||
end
|
||||
|
||||
# the ordering/inclusion of this package list is my semi-arbitrary guess at
|
||||
# which one someone will want to use if they have the package installed...accounting for
|
||||
# features, speed, and robustness
|
||||
for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize")
|
||||
if Pkg.installed(pkgstr) != nothing
|
||||
return backend(Symbol(lowercase(pkgstr)))
|
||||
end
|
||||
end
|
||||
# for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize")
|
||||
# if pkgstr in keys(Pkg.installed())
|
||||
# return backend(Symbol(lowercase(pkgstr)))
|
||||
# end
|
||||
# end
|
||||
|
||||
# the default if nothing else is installed
|
||||
backend(:plotly)
|
||||
backend(:gr)
|
||||
end
|
||||
|
||||
|
||||
@@ -174,24 +180,6 @@ function backend()
|
||||
pickDefaultBackend()
|
||||
end
|
||||
|
||||
sym = CURRENT_BACKEND.sym
|
||||
if !(sym in _initialized_backends)
|
||||
|
||||
# # initialize
|
||||
# println("[Plots.jl] Initializing backend: ", sym)
|
||||
|
||||
inst = _backend_instance(sym)
|
||||
try
|
||||
_initialize_backend(inst)
|
||||
catch err
|
||||
warn("Couldn't initialize $sym. (might need to install it?)")
|
||||
add_backend(sym)
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
push!(_initialized_backends, sym)
|
||||
|
||||
end
|
||||
CURRENT_BACKEND.pkg
|
||||
end
|
||||
|
||||
@@ -199,16 +187,29 @@ end
|
||||
Set the plot backend.
|
||||
"""
|
||||
function backend(pkg::AbstractBackend)
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
warn_on_deprecated_backend(CURRENT_BACKEND.sym)
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
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
|
||||
end
|
||||
backend()
|
||||
end
|
||||
|
||||
function backend(modname::Symbol)
|
||||
warn_on_deprecated_backend(modname)
|
||||
CURRENT_BACKEND.sym = modname
|
||||
CURRENT_BACKEND.pkg = _backend_instance(modname)
|
||||
function backend(sym::Symbol)
|
||||
if sym in _backends
|
||||
backend(_backend_instance(sym))
|
||||
else
|
||||
@warn("`:$sym` is not a supported backend.")
|
||||
end
|
||||
backend()
|
||||
end
|
||||
|
||||
@@ -216,7 +217,7 @@ const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
|
||||
|
||||
function warn_on_deprecated_backend(bsym::Symbol)
|
||||
if bsym in _deprecated_backends
|
||||
warn("Backend $bsym has been deprecated. It may not work as originally intended.")
|
||||
@warn("Backend $bsym has been deprecated. It may not work as originally intended.")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -308,3 +309,112 @@ end
|
||||
|
||||
# is_subplot_supported(::AbstractBackend) = false
|
||||
# is_subplot_supported() = is_subplot_supported(backend())
|
||||
|
||||
|
||||
################################################################################
|
||||
# initialize the backends
|
||||
|
||||
function _initialize_backend(pkg::AbstractBackend)
|
||||
sym = backend_package_name(pkg)
|
||||
@eval Main begin
|
||||
import $sym
|
||||
export $sym
|
||||
end
|
||||
end
|
||||
|
||||
function add_backend_string(pkg::AbstractBackend)
|
||||
sym = backend_package_name(pkg)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("$sym")
|
||||
"""
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# glvisualize
|
||||
|
||||
function _initialize_backend(::GLVisualizeBackend; kw...)
|
||||
@eval Main begin
|
||||
import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour
|
||||
import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle, Point, Vec
|
||||
import FileIO, Images
|
||||
export GLVisualize
|
||||
import Reactive: Signal
|
||||
import GLAbstraction: Style
|
||||
import GLVisualize: visualize
|
||||
import Plots.GL
|
||||
import UnicodeFun
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# hdf5
|
||||
|
||||
function _initialize_backend(::HDF5Backend)
|
||||
@eval Main begin
|
||||
import HDF5
|
||||
export HDF5
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PGFPLOTS
|
||||
|
||||
function add_backend_string(::PGFPlotsBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("PGFPlots")
|
||||
Pkg.build("PGFPlots")
|
||||
"""
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# plotlyjs
|
||||
|
||||
function add_backend_string(::PlotlyJSBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("PlotlyJS")
|
||||
Pkg.add("Rsvg")
|
||||
import Blink
|
||||
Blink.AtomShell.install()
|
||||
"""
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# pyplot
|
||||
|
||||
function _initialize_backend(::PyPlotBackend)
|
||||
@eval Main begin
|
||||
import PyPlot, PyCall
|
||||
import LaTeXStrings
|
||||
|
||||
export PyPlot
|
||||
|
||||
# we don't want every command to update the figure
|
||||
PyPlot.ioff()
|
||||
end
|
||||
end
|
||||
|
||||
function add_backend_string(::PyPlotBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.add("PyCall")
|
||||
Pkg.add("LaTeXStrings")
|
||||
withenv("PYTHON" => "") do
|
||||
Pkg.build("PyCall")
|
||||
Pkg.build("PyPlot")
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# unicodeplots
|
||||
function add_backend_string(::UnicodePlotsBackend)
|
||||
"""
|
||||
using Pkg
|
||||
Pkg.add("UnicodePlots")
|
||||
Pkg.build("UnicodePlots")
|
||||
"""
|
||||
end
|
||||
|
||||
+257
-232
@@ -20,10 +20,13 @@ const _glvisualize_attr = merge_with_base_supported([
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:title, :title_location,
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:titlefontsize, :titlefontcolor,
|
||||
:legendfontsize, :legendfontcolor,
|
||||
:tickfontsize,
|
||||
:guidefontsize, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :colorbar,
|
||||
:marker_z,
|
||||
@@ -41,9 +44,10 @@ const _glvisualize_attr = merge_with_base_supported([
|
||||
:dpi,
|
||||
:hover,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
])
|
||||
const _glvisualize_seriestype = [
|
||||
:path, :shape,
|
||||
:path, :shape, :straightline,
|
||||
:scatter, :hexbin,
|
||||
:bar, :boxplot,
|
||||
:heatmap, :image, :volume,
|
||||
@@ -53,36 +57,11 @@ const _glvisualize_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _glvisualize_marker = _allMarkers
|
||||
const _glvisualize_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::GLVisualizeBackend; kw...)
|
||||
@eval begin
|
||||
import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour
|
||||
import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle, Point, Vec
|
||||
import FileIO, Images
|
||||
export GLVisualize
|
||||
import Reactive: Signal
|
||||
import GLAbstraction: Style
|
||||
import GLVisualize: visualize
|
||||
import Plots.GL
|
||||
import UnicodeFun
|
||||
Plots.slice_arg{C<:Colorant}(img::Matrix{C}, idx::Int) = img
|
||||
is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true
|
||||
is_marker_supported{C<:Colorant}(::GLVisualizeBackend, shape::Union{Vector{Matrix{C}}, Matrix{C}}) = true
|
||||
is_marker_supported(::GLVisualizeBackend, shape::Shape) = true
|
||||
const GL = Plots
|
||||
end
|
||||
end
|
||||
|
||||
function add_backend_string(b::GLVisualizeBackend)
|
||||
"""
|
||||
if !Plots.is_installed("GLVisualize")
|
||||
Pkg.add("GLVisualize")
|
||||
end
|
||||
"""
|
||||
end
|
||||
slice_arg(img::Matrix{C}, idx::Int) where {C<:Colorant} = img
|
||||
is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true
|
||||
is_marker_supported(::GLVisualizeBackend, shape::Union{Vector{Matrix{C}}, Matrix{C}}) where {C<:Colorant} = true
|
||||
is_marker_supported(::GLVisualizeBackend, shape::Shape) = true
|
||||
GL = Plots
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -203,7 +182,7 @@ function gl_marker(shape::Symbol)
|
||||
end
|
||||
end
|
||||
|
||||
function extract_limits(sp, d, kw_args)
|
||||
function extract_limits(sp, plotattributes, kw_args)
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
if isfinite(clims[1]) && isfinite(clims[2])
|
||||
@@ -213,76 +192,82 @@ function extract_limits(sp, d, kw_args)
|
||||
nothing
|
||||
end
|
||||
|
||||
to_vec{T <: StaticArrays.StaticVector}(::Type{T}, vec::T) = vec
|
||||
to_vec{T <: StaticArrays.StaticVector}(::Type{T}, s::Number) = T(s)
|
||||
to_vec(::Type{T}, vec::T) where {T <: StaticArrays.StaticVector} = vec
|
||||
to_vec(::Type{T}, s::Number) where {T <: StaticArrays.StaticVector} = T(s)
|
||||
|
||||
to_vec{T <: StaticArrays.StaticVector{2}}(::Type{T}, vec::StaticArrays.StaticVector{3}) = T(vec[1], vec[2])
|
||||
to_vec{T <: StaticArrays.StaticVector{3}}(::Type{T}, vec::StaticArrays.StaticVector{2}) = T(vec[1], vec[2], 0)
|
||||
to_vec(::Type{T}, vec::StaticArrays.StaticVector{3}) where {T <: StaticArrays.StaticVector{2}} = T(vec[1], vec[2])
|
||||
to_vec(::Type{T}, vec::StaticArrays.StaticVector{2}) where {T <: StaticArrays.StaticVector{3}} = T(vec[1], vec[2], 0)
|
||||
|
||||
to_vec{T <: StaticArrays.StaticVector}(::Type{T}, vecs::AbstractVector) = map(x-> to_vec(T, x), vecs)
|
||||
to_vec(::Type{T}, vecs::AbstractVector) where {T <: StaticArrays.StaticVector} = map(x-> to_vec(T, x), vecs)
|
||||
|
||||
function extract_marker(d, kw_args)
|
||||
dim = Plots.is3d(d) ? 3 : 2
|
||||
function extract_marker(plotattributes, kw_args)
|
||||
dim = Plots.is3d(plotattributes) ? 3 : 2
|
||||
scaling = dim == 3 ? 0.003 : 2
|
||||
if haskey(d, :markershape)
|
||||
shape = d[:markershape]
|
||||
if haskey(plotattributes, :markershape)
|
||||
shape = plotattributes[:markershape]
|
||||
shape = gl_marker(shape)
|
||||
if shape != :none
|
||||
kw_args[:primitive] = shape
|
||||
end
|
||||
end
|
||||
dim = isa(kw_args[:primitive], GLVisualize.Sprites) ? 2 : 3
|
||||
if haskey(d, :markersize)
|
||||
msize = d[:markersize]
|
||||
if haskey(plotattributes, :markersize)
|
||||
msize = plotattributes[:markersize]
|
||||
kw_args[:scale] = to_vec(GeometryTypes.Vec{dim, Float32}, msize .* scaling)
|
||||
end
|
||||
if haskey(d, :offset)
|
||||
kw_args[:offset] = d[:offset]
|
||||
if haskey(plotattributes, :offset)
|
||||
kw_args[:offset] = plotattributes[:offset]
|
||||
end
|
||||
# get the color
|
||||
key = :markercolor
|
||||
haskey(d, key) || return
|
||||
c = gl_color(d[key])
|
||||
if isa(c, AbstractVector) && d[:marker_z] != nothing
|
||||
extract_colornorm(d, kw_args)
|
||||
haskey(plotattributes, key) || return
|
||||
c = gl_color(plotattributes[key])
|
||||
if isa(c, AbstractVector) && plotattributes[:marker_z] != nothing
|
||||
extract_colornorm(plotattributes, kw_args)
|
||||
kw_args[:color] = nothing
|
||||
kw_args[:color_map] = c
|
||||
kw_args[:intensity] = convert(Vector{Float32}, d[:marker_z])
|
||||
kw_args[:intensity] = convert(Vector{Float32}, plotattributes[:marker_z])
|
||||
else
|
||||
kw_args[:color] = c
|
||||
end
|
||||
key = :markerstrokecolor
|
||||
haskey(d, key) || return
|
||||
c = gl_color(d[key])
|
||||
haskey(plotattributes, key) || return
|
||||
c = gl_color(plotattributes[key])
|
||||
if c != nothing
|
||||
if !(isa(c, Colorant) || (isa(c, Vector) && eltype(c) <: Colorant))
|
||||
error("Stroke Color not supported: $c")
|
||||
end
|
||||
kw_args[:stroke_color] = c
|
||||
kw_args[:stroke_width] = Float32(d[:markerstrokewidth])
|
||||
kw_args[:stroke_width] = Float32(plotattributes[:markerstrokewidth])
|
||||
end
|
||||
end
|
||||
|
||||
function _extract_surface(d::Plots.Surface)
|
||||
d.surf
|
||||
function _extract_surface(plotattributes::Plots.Surface)
|
||||
plotattributes.surf
|
||||
end
|
||||
function _extract_surface(d::AbstractArray)
|
||||
d
|
||||
function _extract_surface(plotattributes::AbstractArray)
|
||||
plotattributes
|
||||
end
|
||||
|
||||
# TODO when to transpose??
|
||||
function extract_surface(d)
|
||||
map(_extract_surface, (d[:x], d[:y], d[:z]))
|
||||
function extract_surface(plotattributes)
|
||||
map(_extract_surface, (plotattributes[:x], plotattributes[:y], plotattributes[:z]))
|
||||
end
|
||||
function topoints{P}(::Type{P}, array)
|
||||
function topoints(::Type{P}, array) where P
|
||||
[P(x) for x in zip(array...)]
|
||||
end
|
||||
function extract_points(d)
|
||||
dim = is3d(d) ? 3 : 2
|
||||
array = (d[:x], d[:y], d[:z])[1:dim]
|
||||
function extract_points(plotattributes)
|
||||
dim = is3d(plotattributes) ? 3 : 2
|
||||
array = if plotattributes[:seriestype] == :straightline
|
||||
straightline_data(plotattributes)
|
||||
elseif plotattributes[:seriestype] == :shape
|
||||
shape_data(plotattributes)
|
||||
else
|
||||
(plotattributes[:x], plotattributes[:y], plotattributes[:z])[1:dim]
|
||||
end
|
||||
topoints(Point{dim, Float32}, array)
|
||||
end
|
||||
function make_gradient{C <: Colorant}(grad::Vector{C})
|
||||
function make_gradient(grad::Vector{C}) where C <: Colorant
|
||||
grad
|
||||
end
|
||||
function make_gradient(grad::ColorGradient)
|
||||
@@ -290,54 +275,54 @@ function make_gradient(grad::ColorGradient)
|
||||
end
|
||||
make_gradient(c) = make_gradient(cgrad())
|
||||
|
||||
function extract_any_color(d, kw_args)
|
||||
if d[:marker_z] == nothing
|
||||
c = scalar_color(d, :fill)
|
||||
extract_c(d, kw_args, :fill)
|
||||
function extract_any_color(plotattributes, kw_args)
|
||||
if plotattributes[:marker_z] == nothing
|
||||
c = scalar_color(plotattributes, :fill)
|
||||
extract_c(plotattributes, kw_args, :fill)
|
||||
if isa(c, Colorant)
|
||||
kw_args[:color] = c
|
||||
else
|
||||
kw_args[:color] = nothing
|
||||
kw_args[:color_map] = make_gradient(c)
|
||||
clims = d[:subplot][:clims]
|
||||
clims = plotattributes[:subplot][:clims]
|
||||
if Plots.is_2tuple(clims)
|
||||
if isfinite(clims[1]) && isfinite(clims[2])
|
||||
kw_args[:color_norm] = Vec2f0(clims)
|
||||
end
|
||||
elseif clims == :auto
|
||||
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(d[:y]))
|
||||
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(plotattributes[:y]))
|
||||
end
|
||||
end
|
||||
else
|
||||
kw_args[:color] = nothing
|
||||
clims = d[:subplot][:clims]
|
||||
clims = plotattributes[:subplot][:clims]
|
||||
if Plots.is_2tuple(clims)
|
||||
if isfinite(clims[1]) && isfinite(clims[2])
|
||||
kw_args[:color_norm] = Vec2f0(clims)
|
||||
end
|
||||
elseif clims == :auto
|
||||
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(d[:y]))
|
||||
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(plotattributes[:y]))
|
||||
else
|
||||
error("Unsupported limits: $clims")
|
||||
end
|
||||
kw_args[:intensity] = convert(Vector{Float32}, d[:marker_z])
|
||||
kw_args[:color_map] = gl_color_map(d, :marker)
|
||||
kw_args[:intensity] = convert(Vector{Float32}, plotattributes[:marker_z])
|
||||
kw_args[:color_map] = gl_color_map(plotattributes, :marker)
|
||||
end
|
||||
end
|
||||
|
||||
function extract_stroke(d, kw_args)
|
||||
extract_c(d, kw_args, :line)
|
||||
if haskey(d, :linewidth)
|
||||
kw_args[:thickness] = d[:linewidth] * 3
|
||||
function extract_stroke(plotattributes, kw_args)
|
||||
extract_c(plotattributes, kw_args, :line)
|
||||
if haskey(plotattributes, :linewidth)
|
||||
kw_args[:thickness] = Float32(plotattributes[:linewidth] * 3)
|
||||
end
|
||||
end
|
||||
|
||||
function extract_color(d, sym)
|
||||
d[Symbol("$(sym)color")]
|
||||
function extract_color(plotattributes, sym)
|
||||
plotattributes[Symbol("$(sym)color")]
|
||||
end
|
||||
|
||||
gl_color(c::PlotUtils.ColorGradient) = c.colors
|
||||
gl_color{T<:Colorant}(c::Vector{T}) = c
|
||||
gl_color(c::Vector{T}) where {T<:Colorant} = c
|
||||
gl_color(c::RGBA{Float32}) = c
|
||||
gl_color(c::Colorant) = RGBA{Float32}(c)
|
||||
|
||||
@@ -350,12 +335,12 @@ function gl_color(c, a)
|
||||
c = convertColor(c, a)
|
||||
RGBA{Float32}(c)
|
||||
end
|
||||
function scalar_color(d, sym)
|
||||
gl_color(extract_color(d, sym))
|
||||
function scalar_color(plotattributes, sym)
|
||||
gl_color(extract_color(plotattributes, sym))
|
||||
end
|
||||
|
||||
function gl_color_map(d, sym)
|
||||
colors = extract_color(d, sym)
|
||||
function gl_color_map(plotattributes, sym)
|
||||
colors = extract_color(plotattributes, sym)
|
||||
_gl_color_map(colors)
|
||||
end
|
||||
function _gl_color_map(colors::PlotUtils.ColorGradient)
|
||||
@@ -383,7 +368,7 @@ function gappy(x, ps)
|
||||
return last(ps) - x
|
||||
end
|
||||
function ticks(points, resolution)
|
||||
Float16[gappy(x, points) for x = linspace(first(points),last(points), resolution)]
|
||||
Float16[gappy(x, points) for x = range(first(points), stop=last(points), length=resolution)]
|
||||
end
|
||||
|
||||
|
||||
@@ -392,11 +377,11 @@ function insert_pattern!(points, kw_args)
|
||||
kw_args[:pattern] = tex
|
||||
kw_args[:pattern_length] = Float32(last(points))
|
||||
end
|
||||
function extract_linestyle(d, kw_args)
|
||||
haskey(d, :linestyle) || return
|
||||
ls = d[:linestyle]
|
||||
lw = d[:linewidth]
|
||||
kw_args[:thickness] = lw
|
||||
function extract_linestyle(plotattributes, kw_args)
|
||||
haskey(plotattributes, :linestyle) || return
|
||||
ls = plotattributes[:linestyle]
|
||||
lw = plotattributes[:linewidth]
|
||||
kw_args[:thickness] = Float32(lw)
|
||||
if ls == :dash
|
||||
points = [0.0, lw, 2lw, 3lw, 4lw]
|
||||
insert_pattern!(points, kw_args)
|
||||
@@ -415,7 +400,7 @@ function extract_linestyle(d, kw_args)
|
||||
points = [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap, dtick+dgap+ptick+pgap+ptick, dtick+dgap+ptick+pgap+ptick+pgap]
|
||||
insert_pattern!(points, kw_args)
|
||||
end
|
||||
extract_c(d, kw_args, :line)
|
||||
extract_c(plotattributes, kw_args, :line)
|
||||
nothing
|
||||
end
|
||||
|
||||
@@ -482,8 +467,8 @@ function hover(to_hover, to_display, window)
|
||||
nothing
|
||||
end
|
||||
|
||||
function extract_extrema(d, kw_args)
|
||||
xmin, xmax = ignorenan_extrema(d[:x]); ymin, ymax = ignorenan_extrema(d[:y])
|
||||
function extract_extrema(plotattributes, kw_args)
|
||||
xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y])
|
||||
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(xmin, ymin, xmax-xmin, ymax-ymin)
|
||||
nothing
|
||||
end
|
||||
@@ -494,50 +479,50 @@ function extract_font(font, kw_args)
|
||||
kw_args[:color] = gl_color(font.color)
|
||||
end
|
||||
|
||||
function extract_colornorm(d, kw_args)
|
||||
clims = d[:subplot][:clims]
|
||||
function extract_colornorm(plotattributes, kw_args)
|
||||
clims = plotattributes[:subplot][:clims]
|
||||
if Plots.is_2tuple(clims)
|
||||
if isfinite(clims[1]) && isfinite(clims[2])
|
||||
kw_args[:color_norm] = Vec2f0(clims)
|
||||
end
|
||||
elseif clims == :auto
|
||||
z = if haskey(d, :marker_z) && d[:marker_z] != nothing
|
||||
d[:marker_z]
|
||||
elseif haskey(d, :line_z) && d[:line_z] != nothing
|
||||
d[:line_z]
|
||||
elseif isa(d[:z], Plots.Surface)
|
||||
d[:z].surf
|
||||
z = if haskey(plotattributes, :marker_z) && plotattributes[:marker_z] != nothing
|
||||
plotattributes[:marker_z]
|
||||
elseif haskey(plotattributes, :line_z) && plotattributes[:line_z] != nothing
|
||||
plotattributes[:line_z]
|
||||
elseif isa(plotattributes[:z], Plots.Surface)
|
||||
plotattributes[:z].surf
|
||||
else
|
||||
d[:y]
|
||||
plotattributes[:y]
|
||||
end
|
||||
kw_args[:color_norm] = Vec2f0(ignorenan_extrema(z))
|
||||
kw_args[:intensity] = map(Float32, collect(z))
|
||||
end
|
||||
end
|
||||
|
||||
function extract_gradient(d, kw_args, sym)
|
||||
function extract_gradient(plotattributes, kw_args, sym)
|
||||
key = Symbol("$(sym)color")
|
||||
haskey(d, key) || return
|
||||
c = make_gradient(d[key])
|
||||
haskey(plotattributes, key) || return
|
||||
c = make_gradient(plotattributes[key])
|
||||
kw_args[:color] = nothing
|
||||
extract_colornorm(d, kw_args)
|
||||
extract_colornorm(plotattributes, kw_args)
|
||||
kw_args[:color_map] = c
|
||||
return
|
||||
end
|
||||
|
||||
function extract_c(d, kw_args, sym)
|
||||
function extract_c(plotattributes, kw_args, sym)
|
||||
key = Symbol("$(sym)color")
|
||||
haskey(d, key) || return
|
||||
c = gl_color(d[key])
|
||||
haskey(plotattributes, key) || return
|
||||
c = gl_color(plotattributes[key])
|
||||
kw_args[:color] = nothing
|
||||
kw_args[:color_map] = nothing
|
||||
kw_args[:color_norm] = nothing
|
||||
if (
|
||||
isa(c, AbstractVector) &&
|
||||
((haskey(d, :marker_z) && d[:marker_z] != nothing) ||
|
||||
(haskey(d, :line_z) && d[:line_z] != nothing))
|
||||
((haskey(plotattributes, :marker_z) && plotattributes[:marker_z] != nothing) ||
|
||||
(haskey(plotattributes, :line_z) && plotattributes[:line_z] != nothing))
|
||||
)
|
||||
extract_colornorm(d, kw_args)
|
||||
extract_colornorm(plotattributes, kw_args)
|
||||
kw_args[:color_map] = c
|
||||
else
|
||||
kw_args[:color] = c
|
||||
@@ -545,16 +530,16 @@ function extract_c(d, kw_args, sym)
|
||||
return
|
||||
end
|
||||
|
||||
function extract_stroke(d, kw_args, sym)
|
||||
function extract_stroke(plotattributes, kw_args, sym)
|
||||
key = Symbol("$(sym)strokecolor")
|
||||
haskey(d, key) || return
|
||||
c = gl_color(d[key])
|
||||
haskey(plotattributes, key) || return
|
||||
c = gl_color(plotattributes[key])
|
||||
if c != nothing
|
||||
if !isa(c, Colorant)
|
||||
error("Stroke Color not supported: $c")
|
||||
end
|
||||
kw_args[:stroke_color] = c
|
||||
kw_args[:stroke_width] = Float32(d[Symbol("$(sym)strokewidth")]) * 2
|
||||
kw_args[:stroke_width] = Float32(plotattributes[Symbol("$(sym)strokewidth")]) * 2
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -566,12 +551,12 @@ function draw_grid_lines(sp, grid_segs, thickness, style, model, color)
|
||||
kw_args = Dict{Symbol, Any}(
|
||||
:model => model
|
||||
)
|
||||
d = Dict(
|
||||
plotattributes = Dict(
|
||||
:linestyle => style,
|
||||
:linewidth => thickness,
|
||||
:linewidth => Float32(thickness),
|
||||
:linecolor => color
|
||||
)
|
||||
Plots.extract_linestyle(d, kw_args)
|
||||
Plots.extract_linestyle(plotattributes, kw_args)
|
||||
GL.gl_lines(map(Point2f0, grid_segs.pts), kw_args)
|
||||
end
|
||||
|
||||
@@ -604,10 +589,10 @@ end
|
||||
pointsize(font) = font.pointsize * 2
|
||||
|
||||
function draw_ticks(
|
||||
axis, ticks, isx, lims, m, text = "",
|
||||
axis, ticks, isx, isorigin, lims, m, text = "",
|
||||
positions = Point2f0[], offsets=Vec2f0[]
|
||||
)
|
||||
sz = pointsize(axis[:tickfont])
|
||||
sz = pointsize(tickfont(axis))
|
||||
atlas = GLVisualize.get_texture_atlas()
|
||||
font = GLVisualize.defaultfont()
|
||||
|
||||
@@ -622,7 +607,11 @@ function draw_ticks(
|
||||
for (cv, dv) in zip(ticks...)
|
||||
|
||||
x, y = cv, lims[1]
|
||||
xy = isx ? (x, y) : (y, x)
|
||||
xy = if isorigin
|
||||
isx ? (x, 0) : (0, x)
|
||||
else
|
||||
isx ? (x, y) : (y, x)
|
||||
end
|
||||
_pos = m * GeometryTypes.Vec4f0(xy[1], xy[2], 0, 1)
|
||||
startpos = Point2f0(_pos[1], _pos[2]) - axis_gap
|
||||
str = string(dv)
|
||||
@@ -678,7 +667,7 @@ function text_model(font, pivot)
|
||||
end
|
||||
end
|
||||
function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, area)
|
||||
xticks, yticks, xspine_segs, yspine_segs, xgrid_segs, ygrid_segs, xborder_segs, yborder_segs = Plots.axis_drawing_info(sp)
|
||||
xticks, yticks, xspine_segs, yspine_segs, xtick_segs, ytick_segs, xgrid_segs, ygrid_segs, xminorgrid_segs, yminorgrid_segs, xborder_segs, yborder_segs = Plots.axis_drawing_info(sp)
|
||||
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
|
||||
|
||||
xgc = Colors.color(Plots.gl_color(xaxis[:foreground_color_grid]))
|
||||
@@ -692,6 +681,14 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
||||
grid = draw_grid_lines(sp, ygrid_segs, yaxis[:gridlinewidth], yaxis[:gridstyle], model, RGBA(ygc, yaxis[:gridalpha]))
|
||||
push!(axis_vis, grid)
|
||||
end
|
||||
if xaxis[:minorgrid]
|
||||
minorgrid = draw_minorgrid_lines(sp, xminorgrid_segs, xaxis[:minorgridlinewidth], xaxis[:minorgridstyle], model, RGBA(xgc, xaxis[:minorgridalpha]))
|
||||
push!(axis_vis, minorgrid)
|
||||
end
|
||||
if yaxis[:minorgrid]
|
||||
minorgrid = draw_minorgrid_lines(sp, yminorgrid_segs, yaxis[:minorgridlinewidth], yaxis[:minorgridstyle], model, RGBA(ygc, yaxis[:minorgridalpha]))
|
||||
push!(axis_vis, minorgrid)
|
||||
end
|
||||
|
||||
xac = Colors.color(Plots.gl_color(xaxis[:foreground_color_axis]))
|
||||
yac = Colors.color(Plots.gl_color(yaxis[:foreground_color_axis]))
|
||||
@@ -703,26 +700,55 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
||||
spine = draw_grid_lines(sp, yspine_segs, 1f0, :solid, model, RGBA(yac, 1.0f0))
|
||||
push!(axis_vis, spine)
|
||||
end
|
||||
if sp[:framestyle] in (:zerolines, :grid)
|
||||
if alpha(xaxis[:foreground_color_grid]) > 0
|
||||
spine = draw_grid_lines(sp, xtick_segs, 1f0, :solid, model, RGBA(xgc, xaxis[:gridalpha]))
|
||||
push!(axis_vis, spine)
|
||||
end
|
||||
if alpha(yaxis[:foreground_color_grid]) > 0
|
||||
spine = draw_grid_lines(sp, ytick_segs, 1f0, :solid, model, RGBA(ygc, yaxis[:gridalpha]))
|
||||
push!(axis_vis, spine)
|
||||
end
|
||||
else
|
||||
if alpha(xaxis[:foreground_color_axis]) > 0
|
||||
spine = draw_grid_lines(sp, xtick_segs, 1f0, :solid, model, RGBA(xac, 1.0f0))
|
||||
push!(axis_vis, spine)
|
||||
end
|
||||
if alpha(yaxis[:foreground_color_axis]) > 0
|
||||
spine = draw_grid_lines(sp, ytick_segs, 1f0, :solid, model, RGBA(yac, 1.0f0))
|
||||
push!(axis_vis, spine)
|
||||
end
|
||||
end
|
||||
fcolor = Plots.gl_color(xaxis[:foreground_color_axis])
|
||||
|
||||
xlim = Plots.axis_limits(xaxis)
|
||||
ylim = Plots.axis_limits(yaxis)
|
||||
|
||||
if !(xaxis[:ticks] in (nothing, false, :none)) && !(sp[:framestyle] == :none)
|
||||
if !(xaxis[:ticks] in (nothing, false, :none)) && !(sp[:framestyle] == :none) && xaxis[:showaxis]
|
||||
ticklabels = map(model) do m
|
||||
mirror = xaxis[:mirror]
|
||||
t, positions, offsets = draw_ticks(xaxis, xticks, true, ylim, m)
|
||||
mirror = xaxis[:mirror]
|
||||
t, positions, offsets = draw_ticks(
|
||||
yaxis, yticks, false, xlim, m,
|
||||
t, positions, offsets
|
||||
)
|
||||
t, positions, offsets = draw_ticks(xaxis, xticks, true, sp[:framestyle] == :origin, ylim, m)
|
||||
end
|
||||
kw_args = Dict{Symbol, Any}(
|
||||
:position => map(x-> x[2], ticklabels),
|
||||
:offset => map(last, ticklabels),
|
||||
:color => fcolor,
|
||||
:relative_scale => pointsize(xaxis[:tickfont]),
|
||||
:relative_scale => pointsize(tickfont(xaxis)),
|
||||
:scale_primitive => false
|
||||
)
|
||||
push!(axis_vis, visualize(map(first, ticklabels), Style(:default), kw_args))
|
||||
end
|
||||
|
||||
if !(yaxis[:ticks] in (nothing, false, :none)) && !(sp[:framestyle] == :none) && yaxis[:showaxis]
|
||||
ticklabels = map(model) do m
|
||||
mirror = yaxis[:mirror]
|
||||
t, positions, offsets = draw_ticks(yaxis, yticks, false, sp[:framestyle] == :origin, xlim, m)
|
||||
end
|
||||
kw_args = Dict{Symbol, Any}(
|
||||
:position => map(x-> x[2], ticklabels),
|
||||
:offset => map(last, ticklabels),
|
||||
:color => fcolor,
|
||||
:relative_scale => pointsize(tickfont(xaxis)),
|
||||
:scale_primitive => false
|
||||
)
|
||||
push!(axis_vis, visualize(map(first, ticklabels), Style(:default), kw_args))
|
||||
@@ -739,8 +765,8 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
||||
|
||||
area_w = GeometryTypes.widths(area)
|
||||
if sp[:title] != ""
|
||||
tf = sp[:titlefont]; color = gl_color(sp[:foreground_color_title])
|
||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, tf.rotation, color)
|
||||
tf = titlefont(sp)
|
||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, tf.rotation, tf.color)
|
||||
xy = Point2f0(area.w/2, area_w[2] + pointsize(tf)/2)
|
||||
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
|
||||
extract_font(font, kw)
|
||||
@@ -748,9 +774,9 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
||||
push!(axis_vis, glvisualize_text(xy, t, kw))
|
||||
end
|
||||
if xaxis[:guide] != ""
|
||||
tf = xaxis[:guidefont]; color = gl_color(xaxis[:foreground_color_guide])
|
||||
tf = guidefont(xaxis)
|
||||
xy = Point2f0(area.w/2, - pointsize(tf)/2)
|
||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :bottom, tf.rotation, color)
|
||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :bottom, tf.rotation, tf.color)
|
||||
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
|
||||
t = PlotText(xaxis[:guide], font)
|
||||
extract_font(font, kw)
|
||||
@@ -758,8 +784,8 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
||||
end
|
||||
|
||||
if yaxis[:guide] != ""
|
||||
tf = yaxis[:guidefont]; color = gl_color(yaxis[:foreground_color_guide])
|
||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, 90f0, color)
|
||||
tf = guidefont(yaxis)
|
||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, 90f0, tf.color)
|
||||
xy = Point2f0(-pointsize(tf)/2, area.h/2)
|
||||
kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
|
||||
t = PlotText(yaxis[:guide], font)
|
||||
@@ -782,10 +808,10 @@ function gl_draw_axes_3d(sp, model)
|
||||
)
|
||||
end
|
||||
|
||||
function gl_bar(d, kw_args)
|
||||
x, y = d[:x], d[:y]
|
||||
function gl_bar(plotattributes, kw_args)
|
||||
x, y = plotattributes[:x], plotattributes[:y]
|
||||
nx, ny = length(x), length(y)
|
||||
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
||||
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=x]
|
||||
x = if nx == ny
|
||||
cv
|
||||
@@ -806,18 +832,18 @@ function gl_bar(d, kw_args)
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
fillto = d[:fillrange]
|
||||
fillto = plotattributes[:fillrange]
|
||||
if fillto == nothing
|
||||
fillto = 0
|
||||
end
|
||||
# create the bar shapes by adding x/y segments
|
||||
positions, scales = Array{Point2f0}(ny), Array{Vec2f0}(ny)
|
||||
positions, scales = Array{Point2f0}(undef, ny), Array{Vec2f0}(undef, ny)
|
||||
m = Reactive.value(kw_args[:model])
|
||||
sx, sy = m[1,1], m[2,2]
|
||||
for i=1:ny
|
||||
center = x[i]
|
||||
hwi = abs(_cycle(hw,i)); yi = y[i]; fi = _cycle(fillto,i)
|
||||
if Plots.isvertical(d)
|
||||
if Plots.isvertical(plotattributes)
|
||||
sz = (hwi*sx, yi*sy)
|
||||
else
|
||||
sz = (yi*sx, hwi*2*sy)
|
||||
@@ -836,10 +862,10 @@ const _box_halfwidth = 0.4
|
||||
|
||||
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
|
||||
function gl_boxplot(d, kw_args)
|
||||
function gl_boxplot(plotattributes, kw_args)
|
||||
kwbox = copy(kw_args)
|
||||
range = 1.5; notch = false
|
||||
x, y = d[:x], d[:y]
|
||||
x, y = plotattributes[:x], plotattributes[:y]
|
||||
glabels = sort(collect(unique(x)))
|
||||
warning = false
|
||||
outliers_x, outliers_y = zeros(0), zeros(0)
|
||||
@@ -854,18 +880,18 @@ function gl_boxplot(d, kw_args)
|
||||
# filter y
|
||||
values = y[filter(i -> _cycle(x,i) == glabel, 1:length(y))]
|
||||
# compute quantiles
|
||||
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||
q1,q2,q3,q4,q5 = quantile(values, range(0, stop=1, length=5))
|
||||
# notch
|
||||
n = Plots.notch_width(q2, q4, length(values))
|
||||
# warn on inverted notches?
|
||||
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||
warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
@warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
warning = true # Show the warning only one time
|
||||
end
|
||||
|
||||
# make the shape
|
||||
center = Plots.discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
hw = d[:bar_width] == nothing ? Plots._box_halfwidth*2 : _cycle(d[:bar_width], i)
|
||||
center = Plots.discrete_value!(plotattributes[:subplot][:xaxis], glabel)[1]
|
||||
hw = plotattributes[:bar_width] == nothing ? Plots._box_halfwidth*2 : _cycle(plotattributes[:bar_width], i)
|
||||
l, m, r = center - hw/2, center, center + hw/2
|
||||
|
||||
# internal nodes for notches
|
||||
@@ -904,17 +930,17 @@ function gl_boxplot(d, kw_args)
|
||||
:model => kw_args[:model],
|
||||
:offset => Vec2f0(0),
|
||||
)
|
||||
extract_marker(d, kw_args)
|
||||
extract_marker(plotattributes, kw_args)
|
||||
outlier_kw = Dict(
|
||||
:model => kw_args[:model],
|
||||
:color => scalar_color(d, :fill),
|
||||
:stroke_width => Float32(d[:markerstrokewidth]),
|
||||
:stroke_color => scalar_color(d, :markerstroke),
|
||||
:color => scalar_color(plotattributes, :fill),
|
||||
:stroke_width => Float32(plotattributes[:markerstrokewidth]),
|
||||
:stroke_color => scalar_color(plotattributes, :markerstroke),
|
||||
)
|
||||
lines_kw = Dict(
|
||||
:model => kw_args[:model],
|
||||
:stroke_width => d[:linewidth],
|
||||
:stroke_color => scalar_color(d, :fill),
|
||||
:stroke_width => plotattributes[:linewidth],
|
||||
:stroke_color => scalar_color(plotattributes, :fill),
|
||||
)
|
||||
vis1 = GLVisualize.visualize((GLVisualize.RECTANGLE, box_pos), Style(:default), kwbox)
|
||||
vis2 = GLVisualize.visualize((GLVisualize.CIRCLE, outliers), Style(:default), outlier_kw)
|
||||
@@ -958,11 +984,11 @@ end
|
||||
|
||||
function scale_for_annotations!(series::Series, scaletype::Symbol = :pixels)
|
||||
anns = series[:series_annotations]
|
||||
if anns != nothing && !isnull(anns.baseshape)
|
||||
if anns != nothing && anns.baseshape != nothing
|
||||
# we use baseshape to overwrite the markershape attribute
|
||||
# with a list of custom shapes for each
|
||||
msw, msh = anns.scalefactor
|
||||
offsets = Array{Vec2f0}(length(anns.strs))
|
||||
offsets = Array{Vec2f0}(undef, length(anns.strs))
|
||||
series[:markersize] = map(1:length(anns.strs)) do i
|
||||
str = _cycle(anns.strs, i)
|
||||
# get the width and height of the string (in mm)
|
||||
@@ -1040,8 +1066,8 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||
end
|
||||
for series in Plots.series_list(sp)
|
||||
|
||||
d = series.d
|
||||
st = d[:seriestype]; kw_args = KW() # exctract kw
|
||||
plotattributes = series.plotattributes
|
||||
st = plotattributes[:seriestype]; kw_args = KW() # exctract kw
|
||||
|
||||
kw_args[:model] = model_m # add transformation
|
||||
if !_3d # 3D is treated differently, since we need boundingboxes for camera
|
||||
@@ -1049,80 +1075,80 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||
end
|
||||
scale_for_annotations!(series)
|
||||
if st in (:surface, :wireframe)
|
||||
x, y, z = extract_surface(d)
|
||||
extract_gradient(d, kw_args, :fill)
|
||||
z = Plots.transpose_z(d, z, false)
|
||||
x, y, z = extract_surface(plotattributes)
|
||||
extract_gradient(plotattributes, kw_args, :fill)
|
||||
z = Plots.transpose_z(plotattributes, z, false)
|
||||
if isa(x, AbstractMatrix) && isa(y, AbstractMatrix)
|
||||
x, y = Plots.transpose_z(d, x, false), Plots.transpose_z(d, y, false)
|
||||
x, y = Plots.transpose_z(plotattributes, x, false), Plots.transpose_z(plotattributes, y, false)
|
||||
end
|
||||
if st == :wireframe
|
||||
kw_args[:wireframe] = true
|
||||
kw_args[:stroke_color] = d[:linecolor]
|
||||
kw_args[:stroke_width] = Float32(d[:linewidth]/100f0)
|
||||
kw_args[:stroke_color] = plotattributes[:linecolor]
|
||||
kw_args[:stroke_width] = Float32(plotattributes[:linewidth]/100f0)
|
||||
end
|
||||
vis = GL.gl_surface(x, y, z, kw_args)
|
||||
elseif (st in (:path, :path3d)) && d[:linewidth] > 0
|
||||
elseif (st in (:path, :path3d, :straightline)) && plotattributes[:linewidth] > 0
|
||||
kw = copy(kw_args)
|
||||
points = Plots.extract_points(d)
|
||||
extract_linestyle(d, kw)
|
||||
points = Plots.extract_points(plotattributes)
|
||||
extract_linestyle(plotattributes, kw)
|
||||
vis = GL.gl_lines(points, kw)
|
||||
if d[:markershape] != :none
|
||||
if plotattributes[:markershape] != :none
|
||||
kw = copy(kw_args)
|
||||
extract_stroke(d, kw)
|
||||
extract_marker(d, kw)
|
||||
extract_stroke(plotattributes, kw)
|
||||
extract_marker(plotattributes, kw)
|
||||
vis2 = GL.gl_scatter(copy(points), kw)
|
||||
vis = [vis; vis2]
|
||||
end
|
||||
if d[:fillrange] != nothing
|
||||
if plotattributes[:fillrange] != nothing
|
||||
kw = copy(kw_args)
|
||||
fr = d[:fillrange]
|
||||
ps = if all(x-> x >= 0, diff(d[:x])) # if is monotonic
|
||||
fr = plotattributes[:fillrange]
|
||||
ps = if all(x-> x >= 0, diff(plotattributes[:x])) # if is monotonic
|
||||
vcat(points, Point2f0[(points[i][1], _cycle(fr, i)) for i=length(points):-1:1])
|
||||
else
|
||||
points
|
||||
end
|
||||
extract_c(d, kw, :fill)
|
||||
extract_c(plotattributes, kw, :fill)
|
||||
vis = [GL.gl_poly(ps, kw), vis]
|
||||
end
|
||||
elseif st in (:scatter, :scatter3d) #|| d[:markershape] != :none
|
||||
extract_marker(d, kw_args)
|
||||
points = extract_points(d)
|
||||
elseif st in (:scatter, :scatter3d) #|| plotattributes[:markershape] != :none
|
||||
extract_marker(plotattributes, kw_args)
|
||||
points = extract_points(plotattributes)
|
||||
vis = GL.gl_scatter(points, kw_args)
|
||||
elseif st == :shape
|
||||
extract_c(d, kw_args, :fill)
|
||||
vis = GL.gl_shape(d, kw_args)
|
||||
extract_c(plotattributes, kw_args, :fill)
|
||||
vis = GL.gl_shape(plotattributes, kw_args)
|
||||
elseif st == :contour
|
||||
x,y,z = extract_surface(d)
|
||||
z = transpose_z(d, z, false)
|
||||
extract_extrema(d, kw_args)
|
||||
extract_gradient(d, kw_args, :fill)
|
||||
kw_args[:fillrange] = d[:fillrange]
|
||||
kw_args[:levels] = d[:levels]
|
||||
x,y,z = extract_surface(plotattributes)
|
||||
z = transpose_z(plotattributes, z, false)
|
||||
extract_extrema(plotattributes, kw_args)
|
||||
extract_gradient(plotattributes, kw_args, :fill)
|
||||
kw_args[:fillrange] = plotattributes[:fillrange]
|
||||
kw_args[:levels] = plotattributes[:levels]
|
||||
|
||||
vis = GL.gl_contour(x,y,z, kw_args)
|
||||
elseif st == :heatmap
|
||||
x,y,z = extract_surface(d)
|
||||
extract_gradient(d, kw_args, :fill)
|
||||
extract_extrema(d, kw_args)
|
||||
extract_limits(sp, d, kw_args)
|
||||
x,y,z = extract_surface(plotattributes)
|
||||
extract_gradient(plotattributes, kw_args, :fill)
|
||||
extract_extrema(plotattributes, kw_args)
|
||||
extract_limits(sp, plotattributes, kw_args)
|
||||
vis = GL.gl_heatmap(x,y,z, kw_args)
|
||||
elseif st == :bar
|
||||
extract_c(d, kw_args, :fill)
|
||||
extract_stroke(d, kw_args, :marker)
|
||||
vis = gl_bar(d, kw_args)
|
||||
extract_c(plotattributes, kw_args, :fill)
|
||||
extract_stroke(plotattributes, kw_args, :marker)
|
||||
vis = gl_bar(plotattributes, kw_args)
|
||||
elseif st == :image
|
||||
extract_extrema(d, kw_args)
|
||||
vis = GL.gl_image(d[:z].surf, kw_args)
|
||||
extract_extrema(plotattributes, kw_args)
|
||||
vis = GL.gl_image(plotattributes[:z].surf, kw_args)
|
||||
elseif st == :boxplot
|
||||
extract_c(d, kw_args, :fill)
|
||||
vis = gl_boxplot(d, kw_args)
|
||||
extract_c(plotattributes, kw_args, :fill)
|
||||
vis = gl_boxplot(plotattributes, kw_args)
|
||||
elseif st == :volume
|
||||
volume = d[:y]
|
||||
_d = copy(d)
|
||||
_d[:y] = 0:1
|
||||
_d[:x] = 0:1
|
||||
volume = plotattributes[:y]
|
||||
_plotattributes = copy(plotattributes)
|
||||
_plotattributes[:y] = 0:1
|
||||
_plotattributes[:x] = 0:1
|
||||
kw_args = KW()
|
||||
extract_gradient(_d, kw_args, :fill)
|
||||
extract_gradient(_plotattributes, kw_args, :fill)
|
||||
vis = visualize(volume.v, Style(:default), kw_args)
|
||||
else
|
||||
error("failed to display plot type $st")
|
||||
@@ -1131,15 +1157,15 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||
isa(vis, Array) && isempty(vis) && continue # nothing to see here
|
||||
|
||||
GLVisualize._view(vis, sp_screen, camera=:perspective)
|
||||
if haskey(d, :hover) && !(d[:hover] in (false, :none, nothing))
|
||||
hover(vis, d[:hover], sp_screen)
|
||||
if haskey(plotattributes, :hover) && !(plotattributes[:hover] in (false, :none, nothing))
|
||||
hover(vis, plotattributes[:hover], sp_screen)
|
||||
end
|
||||
if isdefined(:GLPlot) && isdefined(Main.GLPlot, :(register_plot!))
|
||||
del_signal = Main.GLPlot.register_plot!(vis, sp_screen, create_gizmo=false)
|
||||
append!(_glplot_deletes, del_signal)
|
||||
end
|
||||
anns = series[:series_annotations]
|
||||
for (x, y, str, font) in EachAnn(anns, d[:x], d[:y])
|
||||
for (x, y, str, font) in EachAnn(anns, plotattributes[:x], plotattributes[:y])
|
||||
txt_args = Dict{Symbol, Any}(:model => eye(GLAbstraction.Mat4f0))
|
||||
x, y = Reactive.value(model_m) * GeometryTypes.Vec{4, Float32}(x, y, 0, 1)
|
||||
extract_font(font, txt_args)
|
||||
@@ -1174,11 +1200,11 @@ end
|
||||
|
||||
function gl_image(img, kw_args)
|
||||
rect = kw_args[:primitive]
|
||||
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(rect.x, rect.y, rect.h, rect.w) # seems to be flipped
|
||||
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(rect.x, rect.y, rect.w, rect.h)
|
||||
visualize(img, Style(:default), kw_args)
|
||||
end
|
||||
|
||||
function handle_segment{P}(lines, line_segments, points::Vector{P}, segment)
|
||||
function handle_segment(lines, line_segments, points::Vector{P}, segment) where P
|
||||
(isempty(segment) || length(segment) < 2) && return
|
||||
if length(segment) == 2
|
||||
append!(line_segments, view(points, segment))
|
||||
@@ -1215,10 +1241,10 @@ function gl_lines(points, kw_args)
|
||||
return result
|
||||
end
|
||||
|
||||
function gl_shape(d, kw_args)
|
||||
points = Plots.extract_points(d)
|
||||
function gl_shape(plotattributes, kw_args)
|
||||
points = Plots.extract_points(plotattributes)
|
||||
result = []
|
||||
for rng in iter_segments(d[:x], d[:y])
|
||||
for rng in iter_segments(plotattributes[:x], plotattributes[:y])
|
||||
ps = points[rng]
|
||||
meshes = gl_poly(ps, kw_args)
|
||||
append!(result, meshes)
|
||||
@@ -1274,7 +1300,7 @@ function gl_poly(points, kw_args)
|
||||
if !isempty(GeometryTypes.faces(mesh)) # check if polygonation has any faces
|
||||
push!(result, GLVisualize.visualize(mesh, Style(:default), kw_args))
|
||||
else
|
||||
warn("Couldn't draw the polygon: $points")
|
||||
@warn("Couldn't draw the polygon: $points")
|
||||
end
|
||||
end
|
||||
result
|
||||
@@ -1284,7 +1310,7 @@ end
|
||||
|
||||
|
||||
function gl_surface(x,y,z, kw_args)
|
||||
if isa(x, Range) && isa(y, Range)
|
||||
if isa(x, AbstractRange) && isa(y, AbstractRange)
|
||||
main = z
|
||||
kw_args[:ranges] = (x, y)
|
||||
else
|
||||
@@ -1300,7 +1326,7 @@ function gl_surface(x,y,z, kw_args)
|
||||
if get(kw_args, :wireframe, false)
|
||||
points = map(Point3f0, zip(vec(x), vec(y), vec(z)))
|
||||
faces = Cuint[]
|
||||
idx = (i,j) -> sub2ind(size(z), i, j) - 1
|
||||
idx = (i,j) -> CartesianIndices(size(z), i, j) - 1
|
||||
for i=1:size(z,1), j=1:size(z,2)
|
||||
|
||||
i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j))
|
||||
@@ -1309,7 +1335,7 @@ function gl_surface(x,y,z, kw_args)
|
||||
end
|
||||
color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
|
||||
kw_args[:color] = color
|
||||
kw_args[:thickness] = get(kw_args, :stroke_width, 1f0)
|
||||
kw_args[:thickness] = Float32(get(kw_args, :stroke_width, 1f0))
|
||||
kw_args[:indices] = faces
|
||||
delete!(kw_args, :stroke_color)
|
||||
delete!(kw_args, :stroke_width)
|
||||
@@ -1372,10 +1398,10 @@ end
|
||||
"""
|
||||
Ugh, so much special casing (╯°□°)╯︵ ┻━┻
|
||||
"""
|
||||
function label_scatter(d, w, ho)
|
||||
function label_scatter(plotattributes, w, ho)
|
||||
kw = KW()
|
||||
extract_stroke(d, kw)
|
||||
extract_marker(d, kw)
|
||||
extract_stroke(plotattributes, kw)
|
||||
extract_marker(plotattributes, kw)
|
||||
kw[:scale] = Vec2f0(w/2)
|
||||
kw[:offset] = Vec2f0(-w/4)
|
||||
if haskey(kw, :intensity)
|
||||
@@ -1416,21 +1442,21 @@ function make_label(sp, series, i)
|
||||
GL = Plots
|
||||
w, gap, ho = 20f0, 5f0, 5
|
||||
result = []
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
plotattributes = series.plotattributes
|
||||
st = plotattributes[:seriestype]
|
||||
kw_args = KW()
|
||||
if (st in (:path, :path3d)) && d[:linewidth] > 0
|
||||
if (st in (:path, :path3d, :straightline)) && plotattributes[:linewidth] > 0
|
||||
points = Point2f0[(0, ho), (w, ho)]
|
||||
kw = KW()
|
||||
extract_linestyle(d, kw)
|
||||
extract_linestyle(plotattributes, kw)
|
||||
append!(result, GL.gl_lines(points, kw))
|
||||
if d[:markershape] != :none
|
||||
push!(result, label_scatter(d, w, ho))
|
||||
if plotattributes[:markershape] != :none
|
||||
push!(result, label_scatter(plotattributes, w, ho))
|
||||
end
|
||||
elseif st in (:scatter, :scatter3d) #|| d[:markershape] != :none
|
||||
push!(result, label_scatter(d, w, ho))
|
||||
elseif st in (:scatter, :scatter3d) #|| plotattributes[:markershape] != :none
|
||||
push!(result, label_scatter(plotattributes, w, ho))
|
||||
else
|
||||
extract_c(d, kw_args, :fill)
|
||||
extract_c(plotattributes, kw_args, :fill)
|
||||
if isa(kw_args[:color], AbstractVector)
|
||||
kw_args[:color] = first(kw_args[:color])
|
||||
end
|
||||
@@ -1445,9 +1471,8 @@ function make_label(sp, series, i)
|
||||
else
|
||||
series[:label]
|
||||
end
|
||||
color = sp[:foreground_color_legend]
|
||||
ft = sp[:legendfont]
|
||||
font = Plots.Font(ft.family, ft.pointsize, :left, :bottom, 0.0, color)
|
||||
ft = legendfont(sp)
|
||||
font = Plots.Font(ft.family, ft.pointsize, :left, :bottom, 0.0, ft.color)
|
||||
xy = Point2f0(w+gap, 0.0)
|
||||
kw = Dict(:model => text_model(font, xy), :scale_primitive=>false)
|
||||
extract_font(font, kw)
|
||||
|
||||
+384
-225
File diff suppressed because it is too large
Load Diff
+84
-90
@@ -28,20 +28,20 @@ Read from .hdf5 file using:
|
||||
- Should be reliable for archival purposes.
|
||||
==#
|
||||
|
||||
|
||||
import FixedPointNumbers: N0f8 #In core Julia
|
||||
|
||||
#Dispatch types:
|
||||
immutable HDF5PlotNative; end #Indentifies a data element that can natively be handled by HDF5
|
||||
immutable HDF5CTuple; end #Identifies a "complex" tuple structure
|
||||
struct HDF5PlotNative; end #Indentifies a data element that can natively be handled by HDF5
|
||||
struct HDF5CTuple; end #Identifies a "complex" tuple structure
|
||||
|
||||
type HDF5Plot_PlotRef
|
||||
ref::Union{Plot, Void}
|
||||
mutable struct HDF5Plot_PlotRef
|
||||
ref::Union{Plot, Nothing}
|
||||
end
|
||||
|
||||
|
||||
#==Useful constants
|
||||
===============================================================================#
|
||||
const _hdf5_nullable{T} = Union{T, Nothing}
|
||||
const _hdf5_plotroot = "plot"
|
||||
const _hdf5_dataroot = "data" #TODO: Eventually move data to different root (easier to locate)?
|
||||
const _hdf5plot_datatypeid = "TYPE" #Attribute identifying type
|
||||
@@ -94,7 +94,7 @@ const _hdf5_attr = merge_with_base_supported([
|
||||
:colorbar_title,
|
||||
])
|
||||
const _hdf5_seriestype = [
|
||||
:path, :steppre, :steppost, :shape,
|
||||
:path, :steppre, :steppost, :shape, :straightline,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
@@ -105,12 +105,35 @@ const _hdf5_marker = vcat(_allMarkers, :pixel)
|
||||
const _hdf5_scale = [:identity, :ln, :log2, :log10]
|
||||
is_marker_supported(::HDF5Backend, shape::Shape) = true
|
||||
|
||||
function add_backend_string(::HDF5Backend)
|
||||
"""
|
||||
if !Plots.is_installed("HDF5")
|
||||
Pkg.add("HDF5")
|
||||
end
|
||||
"""
|
||||
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
#Possible element types of high-level data types:
|
||||
telem2str = Dict{String, Type}(
|
||||
"NATIVE" => HDF5PlotNative,
|
||||
"VOID" => Nothing,
|
||||
"BOOL" => Bool,
|
||||
"SYMBOL" => Symbol,
|
||||
"TUPLE" => Tuple,
|
||||
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
|
||||
"RGBA" => ARGB{N0f8},
|
||||
"EXTREMA" => Extrema,
|
||||
"LENGTH" => Length,
|
||||
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
||||
|
||||
#Sub-structure types:
|
||||
"FONT" => Font,
|
||||
"BOUNDINGBOX" => BoundingBox,
|
||||
"GRIDLAYOUT" => GridLayout,
|
||||
"ROOTLAYOUT" => RootLayout,
|
||||
"SERIESANNOTATIONS" => SeriesAnnotations,
|
||||
# "PLOTTEXT" => PlotText,
|
||||
"COLORGRADIENT" => ColorGradient,
|
||||
"AXIS" => Axis,
|
||||
"SURFACE" => Surface,
|
||||
"SUBPLOT" => Subplot,
|
||||
"NULLABLE" => _hdf5_nullable,
|
||||
)
|
||||
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
|
||||
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
|
||||
end
|
||||
|
||||
|
||||
@@ -125,10 +148,10 @@ _hdf5_map_str2telem(v::Vector) = HDF5PLOT_MAP_STR2TELEM[v[1]]
|
||||
function _hdf5_merge!(dest::Dict, src::Dict)
|
||||
for (k, v) in src
|
||||
if isa(v, Axis)
|
||||
_hdf5_merge!(dest[k].d, v.d)
|
||||
_hdf5_merge!(dest[k].plotattributes, v.plotattributes)
|
||||
else
|
||||
dest[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
@@ -137,43 +160,6 @@ end
|
||||
#==
|
||||
===============================================================================#
|
||||
|
||||
function _initialize_backend(::HDF5Backend)
|
||||
@eval begin
|
||||
import HDF5
|
||||
export HDF5
|
||||
if length(HDF5PLOT_MAP_TELEM2STR) < 1
|
||||
#Possible element types of high-level data types:
|
||||
const telem2str = Dict{String, Type}(
|
||||
"NATIVE" => HDF5PlotNative,
|
||||
"VOID" => Void,
|
||||
"BOOL" => Bool,
|
||||
"SYMBOL" => Symbol,
|
||||
"TUPLE" => Tuple,
|
||||
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
|
||||
"RGBA" => ARGB{N0f8},
|
||||
"EXTREMA" => Extrema,
|
||||
"LENGTH" => Length,
|
||||
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
|
||||
|
||||
#Sub-structure types:
|
||||
"FONT" => Font,
|
||||
"BOUNDINGBOX" => BoundingBox,
|
||||
"GRIDLAYOUT" => GridLayout,
|
||||
"ROOTLAYOUT" => RootLayout,
|
||||
"SERIESANNOTATIONS" => SeriesAnnotations,
|
||||
# "PLOTTEXT" => PlotText,
|
||||
"COLORGRADIENT" => ColorGradient,
|
||||
"AXIS" => Axis,
|
||||
"SUBPLOT" => Subplot,
|
||||
"NULLABLE" => Nullable,
|
||||
)
|
||||
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
|
||||
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Create the window/figure for this backend.
|
||||
function _create_backend_figure(plt::Plot{HDF5Backend})
|
||||
@@ -229,22 +215,18 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{HDF5Backend})
|
||||
#Do nothing
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{HDF5Backend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{HDF5Backend})
|
||||
msg = "HDF5 interface does not support `display()` function."
|
||||
msg *= "\nUse `Plots.hdf5plot_write(::String)` method to write to .HDF5 \"plot\" file instead."
|
||||
warn(msg)
|
||||
@warn(msg)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -276,11 +258,11 @@ function _hdf5plot_overwritetype(grp, T::Type) #Write directly to group
|
||||
HDF5.a_delete(grp, _hdf5plot_datatypeid)
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writetype{T<:Any}(grp, ::Type{Array{T}})
|
||||
function _hdf5plot_writetype(grp, ::Type{Array{T}}) where T<:Any
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[Array] #ANY
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
function _hdf5plot_writetype{T<:BoundingBox}(grp, ::Type{T})
|
||||
function _hdf5plot_writetype(grp, ::Type{T}) where T<:BoundingBox
|
||||
tstr = HDF5PLOT_MAP_TELEM2STR[BoundingBox]
|
||||
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
|
||||
end
|
||||
@@ -289,7 +271,7 @@ function _hdf5plot_writecount(grp, n::Int) #Write directly to group
|
||||
end
|
||||
function _hdf5plot_gwritefields(grp, k::String, v)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
for _k in fieldnames(v)
|
||||
for _k in fieldnames(typeof(v))
|
||||
_v = getfield(v, _k)
|
||||
kstr = string(_k)
|
||||
_hdf5plot_gwrite(grp, kstr, _v)
|
||||
@@ -305,19 +287,19 @@ function _hdf5plot_gwrite(grp, k::String, v) #Default
|
||||
grp[k] = v
|
||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||
end
|
||||
function _hdf5plot_gwrite{T<:Number}(grp, k::String, v::Array{T}) #Default for arrays
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Array{T}) where T<:Number #Default for arrays
|
||||
grp[k] = v
|
||||
_hdf5plot_writetype(grp, k, HDF5PlotNative)
|
||||
end
|
||||
#=
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
|
||||
# @show grp, k
|
||||
warn("Cannot write Array: $k=$v")
|
||||
@warn("Cannot write Array: $k=$v")
|
||||
end
|
||||
=#
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Void)
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Nothing)
|
||||
grp[k] = 0
|
||||
_hdf5plot_writetype(grp, k, Void)
|
||||
_hdf5plot_writetype(grp, k, Nothing)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Bool)
|
||||
grp[k] = Int(v)
|
||||
@@ -341,10 +323,10 @@ function _hdf5plot_gwrite(grp, k::String, v::Tuple)
|
||||
end
|
||||
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, d::Dict)
|
||||
# warn("Cannot write dict: $k=$d")
|
||||
function _hdf5plot_gwrite(grp, k::String, plotattributes::Dict)
|
||||
# @warn("Cannot write dict: $k=$plotattributes")
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Range)
|
||||
function _hdf5plot_gwrite(grp, k::String, v::AbstractRange)
|
||||
_hdf5plot_gwrite(grp, k, collect(v)) #For now
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::ARGB{N0f8})
|
||||
@@ -355,7 +337,7 @@ function _hdf5plot_gwrite(grp, k::String, v::Colorant)
|
||||
_hdf5plot_gwrite(grp, k, ARGB{N0f8}(v))
|
||||
end
|
||||
#Custom vector (when not using simple numeric type):
|
||||
function _hdf5plot_gwritearray{T}(grp, k::String, v::Array{T})
|
||||
function _hdf5plot_gwritearray(grp, k::String, v::Array{T}) where T
|
||||
if "annotations" == k;
|
||||
return #Hack. Does not yet support annotations.
|
||||
end
|
||||
@@ -363,9 +345,10 @@ function _hdf5plot_gwritearray{T}(grp, k::String, v::Array{T})
|
||||
vgrp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_writetype(vgrp, Array) #ANY
|
||||
sz = size(v)
|
||||
lidx = LinearIndices(sz)
|
||||
|
||||
for iter in eachindex(v)
|
||||
coord = ind2sub(sz, iter)
|
||||
coord = lidx[iter]
|
||||
elem = v[iter]
|
||||
idxstr = join(coord, "_")
|
||||
_hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
|
||||
@@ -380,7 +363,7 @@ function _hdf5plot_gwrite(grp, k::String, v::Extrema)
|
||||
grp[k] = [v.emin, v.emax]
|
||||
_hdf5plot_writetype(grp, k, Extrema)
|
||||
end
|
||||
function _hdf5plot_gwrite{T}(grp, k::String, v::Length{T})
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Length{T}) where T
|
||||
grp[k] = v.value
|
||||
_hdf5plot_writetype(grp, k, [HDF5PLOT_MAP_TELEM2STR[Length], string(T)])
|
||||
end
|
||||
@@ -397,22 +380,27 @@ function _hdf5plot_gwrite(grp, k::String, v::HDF5PLOT_SIMPLESUBSTRUCT)
|
||||
end
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Axis)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
for (_k, _v) in v.d
|
||||
for (_k, _v) in v.plotattributes
|
||||
kstr = string(_k)
|
||||
_hdf5plot_gwrite(grp, kstr, _v)
|
||||
end
|
||||
_hdf5plot_writetype(grp, Axis)
|
||||
return
|
||||
end
|
||||
#TODO: "Properly" support Nullable using _hdf5plot_writetype?
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Nullable)
|
||||
if isnull(v)
|
||||
_hdf5plot_gwrite(grp, k, nothing)
|
||||
else
|
||||
_hdf5plot_gwrite(grp, k, v.value)
|
||||
end
|
||||
return
|
||||
function _hdf5plot_gwrite(grp, k::String, v::Surface)
|
||||
grp = HDF5.g_create(grp, k)
|
||||
_hdf5plot_gwrite(grp, "data2d", v.surf)
|
||||
_hdf5plot_writetype(grp, Surface)
|
||||
end
|
||||
# #TODO: "Properly" support Nullable using _hdf5plot_writetype?
|
||||
# function _hdf5plot_gwrite(grp, k::String, v::_hdf5_nullable)
|
||||
# if isnull(v)
|
||||
# _hdf5plot_gwrite(grp, k, nothing)
|
||||
# else
|
||||
# _hdf5plot_gwrite(grp, k, v.value)
|
||||
# end
|
||||
# return
|
||||
# end
|
||||
|
||||
function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
|
||||
#Currently no support for SeriesAnnotations
|
||||
@@ -424,8 +412,8 @@ function _hdf5plot_gwrite(grp, k::String, v::Subplot)
|
||||
_hdf5plot_writetype(grp, Subplot)
|
||||
return
|
||||
end
|
||||
function _hdf5plot_write(grp, d::Dict)
|
||||
for (k, v) in d
|
||||
function _hdf5plot_write(grp, plotattributes::Dict)
|
||||
for (k, v) in plotattributes
|
||||
kstr = string(k)
|
||||
_hdf5plot_gwrite(grp, kstr, v)
|
||||
end
|
||||
@@ -443,7 +431,7 @@ function _hdf5plot_write(sp::Subplot{HDF5Backend}, subpath::String, f)
|
||||
_hdf5plot_writecount(grp, length(sp.series_list))
|
||||
for (i, series) in enumerate(sp.series_list)
|
||||
grp = HDF5.g_create(f, _hdf5_plotelempath("$subpath/series_list/series$i"))
|
||||
_hdf5plot_write(grp, series.d)
|
||||
_hdf5plot_write(grp, series.plotattributes)
|
||||
end
|
||||
|
||||
return
|
||||
@@ -480,7 +468,7 @@ function _hdf5plot_readcount(grp) #Read directly from group
|
||||
end
|
||||
|
||||
_hdf5plot_convert(T::Type{HDF5PlotNative}, v) = v
|
||||
_hdf5plot_convert(T::Type{Void}, v) = nothing
|
||||
_hdf5plot_convert(T::Type{Nothing}, v) = nothing
|
||||
_hdf5plot_convert(T::Type{Bool}, v) = (v!=0)
|
||||
_hdf5plot_convert(T::Type{Symbol}, v) = Symbol(v)
|
||||
_hdf5plot_convert(T::Type{Tuple}, v) = tuple(v...) #With Vector{T<:Number}
|
||||
@@ -522,10 +510,11 @@ function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
|
||||
sz = _hdf5plot_read(grp, "dim")
|
||||
if [0] == sz; return []; end
|
||||
sz = tuple(sz...)
|
||||
result = Array{Any}(sz)
|
||||
result = Array{Any}(undef, sz)
|
||||
lidx = LinearIndices(sz)
|
||||
|
||||
for iter in eachindex(result)
|
||||
coord = ind2sub(sz, iter)
|
||||
coord = lidx[iter]
|
||||
idxstr = join(coord, "_")
|
||||
result[iter] = _hdf5plot_read(grp, "v$idxstr")
|
||||
end
|
||||
@@ -574,6 +563,11 @@ function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
return Axis([], kwlist)
|
||||
end
|
||||
function _hdf5plot_read(grp, k::String, T::Type{Surface}, dtid)
|
||||
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)
|
||||
grp = HDF5.g_open(grp, k)
|
||||
idx = _hdf5plot_read(grp, "index")
|
||||
@@ -585,17 +579,17 @@ function _hdf5plot_read(grp, k::String)
|
||||
return _hdf5plot_read(grp, k, T, dtid)
|
||||
end
|
||||
|
||||
#Read in values in group to populate d:
|
||||
function _hdf5plot_read(grp, d::Dict)
|
||||
#Read in values in group to populate plotattributes:
|
||||
function _hdf5plot_read(grp, plotattributes::Dict)
|
||||
gnames = names(grp)
|
||||
for k in gnames
|
||||
try
|
||||
v = _hdf5plot_read(grp, k)
|
||||
d[Symbol(k)] = v
|
||||
plotattributes[Symbol(k)] = v
|
||||
catch e
|
||||
@show e
|
||||
@show grp
|
||||
warn("Could not read field $k")
|
||||
@warn("Could not read field $k")
|
||||
end
|
||||
end
|
||||
return
|
||||
@@ -620,7 +614,7 @@ function _hdf5plot_read(sp::Subplot, subpath::String, f)
|
||||
kwlist = KW()
|
||||
_hdf5plot_read(grp, kwlist)
|
||||
plot!(sp, kwlist[:x], kwlist[:y]) #Add data & create data structures
|
||||
_hdf5_merge!(sp.series_list[end].d, kwlist)
|
||||
_hdf5_merge!(sp.series_list[end].plotattributes, kwlist)
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
+53
-60
@@ -28,10 +28,13 @@ const _inspectdr_attr = merge_with_base_supported([
|
||||
:markerstrokestyle, #Causes warning not to have it... what is this?
|
||||
:fillcolor, :fillalpha, #:fillrange,
|
||||
# :bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:title, :title_location,
|
||||
:window_title,
|
||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:titlefontfamily, :titlefontsize, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :legend, #:colorbar,
|
||||
# :marker_z,
|
||||
# :line_z,
|
||||
@@ -50,7 +53,7 @@ const _inspectdr_attr = merge_with_base_supported([
|
||||
])
|
||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _inspectdr_seriestype = [
|
||||
:path, :scatter, :shape #, :steppre, :steppost
|
||||
:path, :scatter, :shape, :straightline, #, :steppre, :steppost
|
||||
]
|
||||
#see: _allMarkers, _shape_keys
|
||||
const _inspectdr_marker = Symbol[
|
||||
@@ -83,24 +86,24 @@ end
|
||||
|
||||
# py_marker(markers::AVec) = map(py_marker, markers)
|
||||
function _inspectdr_mapglyph(markers::AVec)
|
||||
warn("Vectors of markers are currently unsupported in InspectDR.")
|
||||
@warn("Vectors of markers are currently unsupported in InspectDR.")
|
||||
_inspectdr_mapglyph(markers[1])
|
||||
end
|
||||
|
||||
_inspectdr_mapglyphsize(v::Real) = v
|
||||
function _inspectdr_mapglyphsize(v::Vector)
|
||||
warn("Vectors of marker sizes are currently unsupported in InspectDR.")
|
||||
@warn("Vectors of marker sizes are currently unsupported in InspectDR.")
|
||||
_inspectdr_mapglyphsize(v[1])
|
||||
end
|
||||
|
||||
_inspectdr_mapcolor(v::Colorant) = v
|
||||
function _inspectdr_mapcolor(g::PlotUtils.ColorGradient)
|
||||
warn("Color gradients are currently unsupported in InspectDR.")
|
||||
@warn("Color gradients are currently unsupported in InspectDR.")
|
||||
#Pick middle color:
|
||||
_inspectdr_mapcolor(g.colors[div(1+end,2)])
|
||||
end
|
||||
function _inspectdr_mapcolor(v::AVec)
|
||||
warn("Vectors of colors are currently unsupported in InspectDR.")
|
||||
@warn("Vectors of colors are currently unsupported in InspectDR.")
|
||||
#Pick middle color:
|
||||
_inspectdr_mapcolor(v[div(1+end,2)])
|
||||
end
|
||||
@@ -131,7 +134,7 @@ end
|
||||
|
||||
function _inspectdr_getscale(s::Symbol, yaxis::Bool)
|
||||
#TODO: Support :asinh, :sqrt
|
||||
kwargs = yaxis? (:tgtmajor=>8, :tgtminor=>2): () #More grid lines on y-axis
|
||||
kwargs = yaxis ? (:tgtmajor=>8, :tgtminor=>2) : () #More grid lines on y-axis
|
||||
if :log2 == s
|
||||
return InspectDR.AxisScale(:log2; kwargs...)
|
||||
elseif :log10 == s
|
||||
@@ -145,37 +148,23 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::InspectDRBackend)
|
||||
"""
|
||||
if !Plots.is_installed("InspectDR")
|
||||
Pkg.add("InspectDR")
|
||||
end
|
||||
"""
|
||||
#Glyph used when plotting "Shape"s:
|
||||
INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
|
||||
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
|
||||
)
|
||||
|
||||
mutable struct InspecDRPlotRef
|
||||
mplot::Union{Nothing, InspectDR.Multiplot}
|
||||
gui::Union{Nothing, InspectDR.GtkPlot}
|
||||
end
|
||||
|
||||
function _initialize_backend(::InspectDRBackend; kw...)
|
||||
@eval begin
|
||||
import InspectDR
|
||||
export InspectDR
|
||||
_inspectdr_getmplot(::Any) = nothing
|
||||
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
|
||||
|
||||
#Glyph used when plotting "Shape"s:
|
||||
const INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
|
||||
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
|
||||
)
|
||||
|
||||
type InspecDRPlotRef
|
||||
mplot::Union{Void, InspectDR.Multiplot}
|
||||
gui::Union{Void, InspectDR.GtkPlot}
|
||||
end
|
||||
|
||||
_inspectdr_getmplot(::Any) = nothing
|
||||
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
|
||||
|
||||
_inspectdr_getgui(::Any) = nothing
|
||||
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed? nothing: gplot)
|
||||
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
|
||||
end
|
||||
end
|
||||
_inspectdr_getgui(::Any) = nothing
|
||||
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed ? nothing : gplot)
|
||||
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
|
||||
push!(_initialized_backends, :inspectdr)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -235,13 +224,17 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series)
|
||||
#Don't do anything without a "subplot" object: Will process later.
|
||||
if nothing == plot; return; end
|
||||
|
||||
_vectorize(v) = isa(v, Vector)? v: collect(v) #InspectDR only supports vectors
|
||||
x = _vectorize(series[:x]); y = _vectorize(series[:y])
|
||||
_vectorize(v) = isa(v, Vector) ? v : collect(v) #InspectDR only supports vectors
|
||||
x, y = if st == :straightline
|
||||
straightline_data(series)
|
||||
else
|
||||
_vectorize(series[:x]), _vectorize(series[:y])
|
||||
end
|
||||
|
||||
#No support for polar grid... but can still perform polar transformation:
|
||||
if ispolar(sp)
|
||||
Θ = x; r = y
|
||||
x = r.*cos(Θ); y = r.*sin(Θ)
|
||||
x = r.*cos.(Θ); y = r.*sin.(Θ)
|
||||
end
|
||||
|
||||
# doesn't handle mismatched x/y - wrap data (pyplot behaviour):
|
||||
@@ -261,6 +254,7 @@ For st in :shape:
|
||||
=#
|
||||
|
||||
if st in (:shape,)
|
||||
x, y = shape_data(series)
|
||||
nmax = 0
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
nmax = i
|
||||
@@ -278,7 +272,7 @@ For st in :shape:
|
||||
end
|
||||
end
|
||||
|
||||
i = (nmax >= 2? div(nmax, 2): nmax) #Must pick one set of colors for legend
|
||||
i = (nmax >= 2 ? div(nmax, 2) : nmax) #Must pick one set of colors for legend
|
||||
if i > 1 #Add dummy waveform for legend entry:
|
||||
linewidth = series[:linewidth]
|
||||
linecolor = _inspectdr_mapcolor(_cycle(series[:linecolor], i))
|
||||
@@ -292,11 +286,11 @@ For st in :shape:
|
||||
color = linecolor, fillcolor = fillcolor
|
||||
)
|
||||
end
|
||||
elseif st in (:path, :scatter) #, :steppre, :steppost)
|
||||
elseif st in (:path, :scatter, :straightline) #, :steppre, :steppost)
|
||||
#NOTE: In Plots.jl, :scatter plots have 0-linewidths (I think).
|
||||
linewidth = series[:linewidth]
|
||||
#More efficient & allows some support for markerstrokewidth:
|
||||
_style = (0==linewidth? :none: series[:linestyle])
|
||||
_style = (0==linewidth ? :none : series[:linestyle])
|
||||
wfrm = InspectDR.add(plot, x, y, id=series[:label])
|
||||
wfrm.line = InspectDR.line(
|
||||
style = _style,
|
||||
@@ -335,8 +329,8 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
const plot = sp.o
|
||||
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
plot = sp.o
|
||||
strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
|
||||
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
|
||||
xgrid_show = xaxis[:grid]
|
||||
@@ -369,24 +363,24 @@ function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
l[:frame_canvas].fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
|
||||
l[:frame_data].fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
|
||||
l[:frame_data].line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
|
||||
l[:font_title] = InspectDR.Font(sp[:titlefont].family,
|
||||
_inspectdr_mapptsize(sp[:titlefont].pointsize),
|
||||
color = _inspectdr_mapcolor(sp[:foreground_color_title])
|
||||
l[:font_title] = InspectDR.Font(sp[:titlefontfamily],
|
||||
_inspectdr_mapptsize(sp[:titlefontsize]),
|
||||
color = _inspectdr_mapcolor(sp[:titlefontcolor])
|
||||
)
|
||||
#Cannot independently control fonts of axes with InspectDR:
|
||||
l[:font_axislabel] = InspectDR.Font(xaxis[:guidefont].family,
|
||||
_inspectdr_mapptsize(xaxis[:guidefont].pointsize),
|
||||
color = _inspectdr_mapcolor(xaxis[:foreground_color_guide])
|
||||
l[:font_axislabel] = InspectDR.Font(xaxis[:guidefontfamily],
|
||||
_inspectdr_mapptsize(xaxis[:guidefontsize]),
|
||||
color = _inspectdr_mapcolor(xaxis[:guidefontcolor])
|
||||
)
|
||||
l[:font_ticklabel] = InspectDR.Font(xaxis[:tickfont].family,
|
||||
_inspectdr_mapptsize(xaxis[:tickfont].pointsize),
|
||||
color = _inspectdr_mapcolor(xaxis[:foreground_color_text])
|
||||
l[:font_ticklabel] = InspectDR.Font(xaxis[:tickfontfamily],
|
||||
_inspectdr_mapptsize(xaxis[:tickfontsize]),
|
||||
color = _inspectdr_mapcolor(xaxis[:tickfontcolor])
|
||||
)
|
||||
l[:enable_legend] = (sp[:legend] != :none)
|
||||
#l[:halloc_legend] = 150 #TODO: compute???
|
||||
l[:font_legend] = InspectDR.Font(sp[:legendfont].family,
|
||||
_inspectdr_mapptsize(sp[:legendfont].pointsize),
|
||||
color = _inspectdr_mapcolor(sp[:foreground_color_legend])
|
||||
l[:font_legend] = InspectDR.Font(sp[:legendfontfamily],
|
||||
_inspectdr_mapptsize(sp[:legendfontsize]),
|
||||
color = _inspectdr_mapcolor(sp[:legendfontcolor])
|
||||
)
|
||||
l[:frame_legend].fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
|
||||
end
|
||||
@@ -394,7 +388,7 @@ end
|
||||
# called just before updating layout bounding boxes... in case you need to prep
|
||||
# for the calcs
|
||||
function _before_layout_calcs(plt::Plot{InspectDRBackend})
|
||||
const mplot = _inspectdr_getmplot(plt.o)
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
|
||||
mplot.title = plt[:plot_title]
|
||||
@@ -463,7 +457,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
@@ -494,7 +488,7 @@ const _inspectdr_mimeformats_nodpi = Dict(
|
||||
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
|
||||
"application/pdf" => "pdf"
|
||||
)
|
||||
_inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
|
||||
_inspectdr_show(io::IO, mime::MIME, ::Nothing, w, h) =
|
||||
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
|
||||
function _inspectdr_show(io::IO, mime::MIME, mplot, w, h)
|
||||
InspectDR._show(io, mime, mplot, Float64(w), Float64(h))
|
||||
@@ -511,7 +505,6 @@ for (mime, fmt) in _inspectdr_mimeformats_nodpi
|
||||
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
|
||||
end
|
||||
end
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
+282
-101
@@ -4,11 +4,12 @@
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
# :background_color_legend,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
# :foreground_color_legend,
|
||||
:foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
@@ -22,8 +23,8 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
:colorbar,
|
||||
:marker_z, #:levels,
|
||||
:colorbar, :colorbar_title,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
@@ -31,30 +32,17 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
# :normalize, :weights, :contours,
|
||||
:aspect_ratio,
|
||||
# :match_dimensions,
|
||||
:tick_direction,
|
||||
:framestyle,
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape]
|
||||
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] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon, :hline] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::PGFPlotsBackend)
|
||||
"""
|
||||
Pkg.add("PGFPlots")
|
||||
Pkg.build("PGFPlots")
|
||||
"""
|
||||
end
|
||||
|
||||
function _initialize_backend(::PGFPlotsBackend; kw...)
|
||||
@eval begin
|
||||
import PGFPlots
|
||||
export PGFPlots
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
const _pgfplots_linestyles = KW(
|
||||
@@ -79,6 +67,7 @@ const _pgfplots_markers = KW(
|
||||
:star6 => "asterisk",
|
||||
:diamond => "diamond*",
|
||||
:pentagon => "pentagon*",
|
||||
:hline => "-"
|
||||
)
|
||||
|
||||
const _pgfplots_legend_pos = KW(
|
||||
@@ -86,6 +75,7 @@ const _pgfplots_legend_pos = KW(
|
||||
:bottomright => "south east",
|
||||
:topright => "north east",
|
||||
:topleft => "north west",
|
||||
:outertopright => "outer north east",
|
||||
)
|
||||
|
||||
|
||||
@@ -106,6 +96,18 @@ const _pgf_annotation_halign = KW(
|
||||
:right => "left"
|
||||
)
|
||||
|
||||
const _pgf_framestyles = [:box, :axes, :origin, :zerolines, :grid, :none]
|
||||
const _pgf_framestyle_defaults = Dict(:semi => :box)
|
||||
function pgf_framestyle(style::Symbol)
|
||||
if style in _pgf_framestyles
|
||||
return style
|
||||
else
|
||||
default_style = get(_pgf_framestyle_defaults, style, :axes)
|
||||
@warn("Framestyle :$style is not (yet) supported by the PGFPlots backend. :$default_style was cosen instead.")
|
||||
default_style
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# takes in color,alpha, and returns color and alpha appropriate for pgf style
|
||||
@@ -127,81 +129,92 @@ function pgf_colormap(grad::ColorGradient)
|
||||
end,", ")
|
||||
end
|
||||
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor])
|
||||
pgf_thickness_scaling(plt::Plot) = plt[:thickness_scaling]
|
||||
pgf_thickness_scaling(sp::Subplot) = pgf_thickness_scaling(sp.plt)
|
||||
pgf_thickness_scaling(series) = pgf_thickness_scaling(series[:subplot])
|
||||
|
||||
function pgf_fillstyle(plotattributes, i = 1)
|
||||
cstr,a = pgf_color(get_fillcolor(plotattributes, i))
|
||||
fa = get_fillalpha(plotattributes, i)
|
||||
if fa != nothing
|
||||
a = fa
|
||||
end
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
end
|
||||
|
||||
function pgf_linestyle(d::KW)
|
||||
cstr,a = pgf_color(d[:linecolor])
|
||||
function pgf_linestyle(linewidth::Real, color, α = 1, linestyle = "solid")
|
||||
cstr, a = pgf_color(plot_color(color, α))
|
||||
"""
|
||||
color = $cstr,
|
||||
draw opacity=$a,
|
||||
line width=$(d[:linewidth]),
|
||||
$(get(_pgfplots_linestyles, d[:linestyle], "solid"))"""
|
||||
draw opacity = $a,
|
||||
line width = $linewidth,
|
||||
$(get(_pgfplots_linestyles, linestyle, "solid"))"""
|
||||
end
|
||||
|
||||
function pgf_marker(d::KW)
|
||||
shape = d[:markershape]
|
||||
cstr, a = pgf_color(d[:markercolor])
|
||||
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor])
|
||||
function pgf_linestyle(plotattributes, i = 1)
|
||||
lw = pgf_thickness_scaling(plotattributes) * get_linewidth(plotattributes, i)
|
||||
lc = get_linecolor(plotattributes, i)
|
||||
la = get_linealpha(plotattributes, i)
|
||||
ls = get_linestyle(plotattributes, i)
|
||||
return pgf_linestyle(lw, lc, la, ls)
|
||||
end
|
||||
|
||||
function pgf_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
|
||||
fs = fontsize * thickness_scaling
|
||||
return string("{\\fontsize{", fs, " pt}{", 1.3fs, " pt}", font, "}")
|
||||
end
|
||||
|
||||
function pgf_marker(plotattributes, i = 1)
|
||||
shape = _cycle(plotattributes[:markershape], i)
|
||||
cstr, a = pgf_color(plot_color(get_markercolor(plotattributes, i), get_markeralpha(plotattributes, i)))
|
||||
cstr_stroke, a_stroke = pgf_color(plot_color(get_markerstrokecolor(plotattributes, i), get_markerstrokealpha(plotattributes, i)))
|
||||
"""
|
||||
mark = $(get(_pgfplots_markers, shape, "*")),
|
||||
mark size = $(0.5 * d[:markersize]),
|
||||
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 = $(d[:markerstrokewidth]),
|
||||
line width = $(pgf_thickness_scaling(plotattributes) * _cycle(plotattributes[:markerstrokewidth], i)),
|
||||
rotate = $(shape == :dtriangle ? 180 : 0),
|
||||
$(get(_pgfplots_linestyles, d[:markerstrokestyle], "solid"))
|
||||
$(get(_pgfplots_linestyles, _cycle(plotattributes[:markerstrokestyle], i), "solid"))
|
||||
}"""
|
||||
end
|
||||
|
||||
function pgf_add_annotation!(o,x,y,val)
|
||||
function pgf_add_annotation!(o, x, y, val, thickness_scaling = 1)
|
||||
# Construct the style string.
|
||||
# Currently supports color and orientation
|
||||
cstr,a = pgf_color(val.font.color)
|
||||
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation)
|
||||
"""))
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation),
|
||||
font=$(pgf_font(val.font.pointsize, thickness_scaling))
|
||||
"""))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function pgf_series(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
|
||||
if d[:fillrange] != nothing || st in (:shape,)
|
||||
push!(style, pgf_fillstyle(d))
|
||||
end
|
||||
|
||||
# add to legend?
|
||||
if sp[:legend] != :none && should_add_to_legend(series)
|
||||
kw[:legendentry] = d[:label]
|
||||
else
|
||||
push!(style, "forget plot")
|
||||
end
|
||||
plotattributes = series.plotattributes
|
||||
st = plotattributes[:seriestype]
|
||||
series_collection = PGFPlots.Plot[]
|
||||
|
||||
# function args
|
||||
args = if st == :contour
|
||||
d[:z].surf, d[:x], d[:y]
|
||||
args = if st == :contour
|
||||
plotattributes[:z].surf, plotattributes[:x], plotattributes[:y]
|
||||
elseif is3d(st)
|
||||
d[:x], d[:y], d[:z]
|
||||
elseif d[:marker_z] != nothing
|
||||
# If a marker_z is used pass it as third coordinate to a 2D plot.
|
||||
# See "Scatter Plots" in PGFPlots documentation
|
||||
d[:x], d[:y], d[:marker_z]
|
||||
plotattributes[:x], plotattributes[:y], plotattributes[:z]
|
||||
elseif st == :straightline
|
||||
straightline_data(series)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
elseif ispolar(sp)
|
||||
theta, r = filter_radial_data(plotattributes[:x], plotattributes[:y], axis_limits(sp[:yaxis]))
|
||||
rad2deg.(theta), r
|
||||
else
|
||||
d[:x], d[:y]
|
||||
plotattributes[:x], plotattributes[:y]
|
||||
end
|
||||
|
||||
# PGFPlots can't handle non-Vector?
|
||||
@@ -210,34 +223,131 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
else
|
||||
a
|
||||
end, args)
|
||||
# for (i,a) in enumerate(args)
|
||||
# if typeof(a) <: AbstractVector && typeof(a) != Vector
|
||||
# args[i] = collect(a)
|
||||
# end
|
||||
# end
|
||||
|
||||
# include additional style, then add to the kw
|
||||
if st in (:contour, :histogram2d)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(plotattributes))
|
||||
push!(style, pgf_marker(plotattributes))
|
||||
push!(style, "forget plot")
|
||||
|
||||
kw[:style] = join(style, ',')
|
||||
func = if st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
else
|
||||
kw[:labels] = series[:contour_labels]
|
||||
kw[:levels] = series[:levels]
|
||||
PGFPlots.Contour
|
||||
end
|
||||
push!(series_collection, func(args...; kw...))
|
||||
|
||||
else
|
||||
# series segments
|
||||
segments = iter_segments(series)
|
||||
for (i, rng) in enumerate(segments)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(plotattributes, i))
|
||||
push!(style, pgf_marker(plotattributes, i))
|
||||
|
||||
if st == :shape
|
||||
push!(style, pgf_fillstyle(plotattributes, i))
|
||||
end
|
||||
|
||||
# add to legend?
|
||||
if i == 1 && sp[:legend] != :none && should_add_to_legend(series)
|
||||
if plotattributes[:fillrange] != nothing
|
||||
push!(style, "forget plot")
|
||||
push!(series_collection, pgf_fill_legend_hack(plotattributes, args))
|
||||
else
|
||||
kw[:legendentry] = plotattributes[:label]
|
||||
if st == :shape # || plotattributes[:fillrange] != nothing
|
||||
push!(style, "area legend")
|
||||
end
|
||||
end
|
||||
else
|
||||
push!(style, "forget plot")
|
||||
end
|
||||
|
||||
seg_args = (arg[rng] for arg in args)
|
||||
|
||||
# include additional style, then add to the kw
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
|
||||
# add fillrange
|
||||
if series[:fillrange] != nothing && st != :shape
|
||||
push!(series_collection, pgf_fillrange_series(series, i, _cycle(series[:fillrange], rng), seg_args...))
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
push!(series_collection, func(seg_args...; kw...))
|
||||
end
|
||||
end
|
||||
series_collection
|
||||
end
|
||||
|
||||
function pgf_fillrange_series(series, i, fillrange, args...)
|
||||
st = series[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, "line width = 0")
|
||||
push!(style, "draw opacity = 0")
|
||||
push!(style, pgf_fillstyle(series, i))
|
||||
push!(style, pgf_marker(series, i))
|
||||
push!(style, "forget plot")
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
func = is3d(series) ? PGFPlots.Linear3 : PGFPlots.Linear
|
||||
return func(pgf_fillrange_args(fillrange, args...)...; kw...)
|
||||
end
|
||||
|
||||
# build/return the series object
|
||||
function pgf_fillrange_args(fillrange, x, y)
|
||||
n = length(x)
|
||||
x_fill = [x; x[n:-1:1]; x[1]]
|
||||
y_fill = [y; _cycle(fillrange, n:-1:1); y[1]]
|
||||
return x_fill, y_fill
|
||||
end
|
||||
|
||||
function pgf_fillrange_args(fillrange, x, y, z)
|
||||
n = length(x)
|
||||
x_fill = [x; x[n:-1:1]; x[1]]
|
||||
y_fill = [y; y[n:-1:1]; x[1]]
|
||||
z_fill = [z; _cycle(fillrange, n:-1:1); z[1]]
|
||||
return x_fill, y_fill, z_fill
|
||||
end
|
||||
|
||||
function pgf_fill_legend_hack(plotattributes, args)
|
||||
style = []
|
||||
kw = KW()
|
||||
push!(style, pgf_linestyle(plotattributes, 1))
|
||||
push!(style, pgf_marker(plotattributes, 1))
|
||||
push!(style, pgf_fillstyle(plotattributes, 1))
|
||||
push!(style, "area legend")
|
||||
kw[:legendentry] = plotattributes[:label]
|
||||
kw[:style] = join(style, ',')
|
||||
st = plotattributes[:seriestype]
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
elseif st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
elseif st == :contour
|
||||
PGFPlots.Contour
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
func(args...; kw...)
|
||||
return func(([arg[1]] for arg in args)...; kw...)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function pgf_axis(sp::Subplot, letter)
|
||||
@@ -245,11 +355,18 @@ function pgf_axis(sp::Subplot, letter)
|
||||
style = []
|
||||
kw = KW()
|
||||
|
||||
# turn off scaled ticks
|
||||
push!(style, "scaled $(letter) ticks = false")
|
||||
|
||||
# set to supported framestyle
|
||||
framestyle = pgf_framestyle(sp[:framestyle])
|
||||
|
||||
# axis guide
|
||||
kw[Symbol(letter,:label)] = axis[:guide]
|
||||
|
||||
# Add ticklabel rotations
|
||||
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
|
||||
# Add label font
|
||||
cstr, α = pgf_color(plot_color(axis[:guidefontcolor]))
|
||||
push!(style, string(letter, "label style = {font = ", pgf_font(axis[:guidefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:guidefontrotation], "}"))
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
@@ -262,27 +379,75 @@ function pgf_axis(sp::Subplot, letter)
|
||||
end
|
||||
|
||||
# ticks on or off
|
||||
if axis[:ticks] in (nothing, false)
|
||||
if axis[:ticks] in (nothing, false, :none) || framestyle == :none
|
||||
push!(style, "$(letter)majorticks=false")
|
||||
end
|
||||
|
||||
# grid on or off
|
||||
if axis[:grid]
|
||||
if axis[:grid] && framestyle != :none
|
||||
push!(style, "$(letter)majorgrids = true")
|
||||
else
|
||||
push!(style, "$(letter)majorgrids = false")
|
||||
end
|
||||
|
||||
# limits
|
||||
# TODO: support zlims
|
||||
if letter != :z
|
||||
lims = axis_limits(axis)
|
||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(axis)) : axis_limits(axis)
|
||||
kw[Symbol(letter,:min)] = lims[1]
|
||||
kw[Symbol(letter,:max)] = lims[2]
|
||||
end
|
||||
|
||||
if !(axis[:ticks] in (nothing, false, :none, :auto))
|
||||
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
||||
ticks = get_ticks(axis)
|
||||
push!(style, string(letter, "tick = {", join(ticks[1],","), "}"))
|
||||
push!(style, string(letter, "ticklabels = {", join(ticks[2],","), "}"))
|
||||
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
||||
tick_values = ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
||||
push!(style, string(letter, "tick = {", join(tick_values,","), "}"))
|
||||
if axis[:showaxis] && axis[:scale] in (:ln, :log2, :log10) && axis[:ticks] == :auto
|
||||
# wrap the power part of label with }
|
||||
tick_labels = Vector{String}(length(ticks[2]))
|
||||
for (i, label) in enumerate(ticks[2])
|
||||
base, power = split(label, "^")
|
||||
power = string("{", power, "}")
|
||||
tick_labels[i] = string(base, "^", power)
|
||||
end
|
||||
push!(style, string(letter, "ticklabels = {\$", join(tick_labels,"\$,\$"), "\$}"))
|
||||
elseif axis[:showaxis]
|
||||
tick_labels = ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
if axis[:formatter] in (:scientific, :auto)
|
||||
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
|
||||
tick_labels = replace.(tick_labels, "×", "\\times")
|
||||
end
|
||||
push!(style, string(letter, "ticklabels = {", join(tick_labels,","), "}"))
|
||||
else
|
||||
push!(style, string(letter, "ticklabels = {}"))
|
||||
end
|
||||
push!(style, string(letter, "tick align = ", (axis[:tick_direction] == :out ? "outside" : "inside")))
|
||||
cstr, α = pgf_color(plot_color(axis[:tickfontcolor]))
|
||||
push!(style, string(letter, "ticklabel style = {font = ", pgf_font(axis[:tickfontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", axis[:tickfontrotation], "}"))
|
||||
push!(style, string(letter, " grid style = {", pgf_linestyle(pgf_thickness_scaling(sp) * axis[:gridlinewidth], axis[:foreground_color_grid], axis[:gridalpha], axis[:gridstyle]), "}"))
|
||||
end
|
||||
|
||||
# framestyle
|
||||
if framestyle in (:axes, :origin)
|
||||
axispos = framestyle == :axes ? "left" : "middle"
|
||||
# the * after lines disables the arrows at the axes
|
||||
push!(style, string("axis lines* = ", axispos))
|
||||
end
|
||||
|
||||
if framestyle == :zerolines
|
||||
push!(style, string("extra ", letter, " ticks = 0"))
|
||||
push!(style, string("extra ", letter, " tick labels = "))
|
||||
push!(style, string("extra ", letter, " tick style = {grid = major, major grid style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}}"))
|
||||
end
|
||||
|
||||
if !axis[:showaxis]
|
||||
push!(style, "separate axis lines")
|
||||
end
|
||||
if !axis[:showaxis] || framestyle in (:zerolines, :grid, :none)
|
||||
push!(style, string(letter, " axis line style = {draw opacity = 0}"))
|
||||
else
|
||||
push!(style, string(letter, " axis line style = {", pgf_linestyle(pgf_thickness_scaling(sp), axis[:foreground_color_axis], 1.0), "}"))
|
||||
end
|
||||
|
||||
# return the style list and KW args
|
||||
@@ -319,7 +484,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
bb = bbox(sp)
|
||||
push!(style, """
|
||||
xshift = $(left(bb).value)mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value,2))mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value, digits=2))mm,
|
||||
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
|
||||
""")
|
||||
kw[:width] = "$(width(bb).value)mm"
|
||||
@@ -327,6 +492,8 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
if sp[:title] != ""
|
||||
kw[:title] = "$(sp[:title])"
|
||||
cstr, α = pgf_color(plot_color(sp[:titlefontcolor]))
|
||||
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
|
||||
end
|
||||
|
||||
if sp[:aspect_ratio] in (1, :equal)
|
||||
@@ -337,10 +504,23 @@ 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)), "}"))
|
||||
|
||||
if any(s[:seriestype] == :contour for s in series_list(sp))
|
||||
kw[:view] = "{0}{90}"
|
||||
kw[:colorbar] = !(sp[:colorbar] in (:none, :off, :hide, false))
|
||||
elseif is3d(sp)
|
||||
azim, elev = sp[:camera]
|
||||
kw[:view] = "{$(azim)}{$(elev)}"
|
||||
end
|
||||
|
||||
axisf = PGFPlots.Axis
|
||||
if sp[:projection] == :polar
|
||||
axisf = PGFPlots.PolarAxis
|
||||
#make radial axis vertical
|
||||
kw[:xmin] = 90
|
||||
kw[:xmax] = 450
|
||||
end
|
||||
|
||||
# Search series for any gradient. In case one series uses a gradient set
|
||||
@@ -354,9 +534,9 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
# As it is likely that all series within the same axis use the same
|
||||
# colormap this should not cause any problem.
|
||||
for series in series_list(sp)
|
||||
for col in (:markercolor, :fillcolor)
|
||||
if typeof(series.d[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.d[col]))}")
|
||||
for col in (:markercolor, :fillcolor, :linecolor)
|
||||
if typeof(series.plotattributes[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.plotattributes[col]))}")
|
||||
|
||||
if sp[:colorbar] == :none
|
||||
kw[:colorbar] = "false"
|
||||
@@ -370,22 +550,23 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
end
|
||||
@label colorbar_end
|
||||
|
||||
o = axisf(; style = style, kw...)
|
||||
push!(style, "colorbar style={title=$(sp[:colorbar_title])}")
|
||||
o = axisf(; style = join(style, ","), kw...)
|
||||
|
||||
# add the series object to the PGFPlots.Axis
|
||||
for series in series_list(sp)
|
||||
push!(o, pgf_series(sp, series))
|
||||
push!.(o, pgf_series(sp, series))
|
||||
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt), pgf_thickness_scaling(series))
|
||||
end
|
||||
end
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o,ann...)
|
||||
pgf_add_annotation!(o, locate_annotation(sp, ann...)..., pgf_thickness_scaling(sp))
|
||||
end
|
||||
|
||||
|
||||
@@ -407,7 +588,7 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
|
||||
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
|
||||
|
||||
# read it into io
|
||||
write(io, readstring(open(fn)))
|
||||
write(io, read(open(fn), String))
|
||||
|
||||
# cleanup
|
||||
PGFPlots.cleanup(plt.o)
|
||||
@@ -416,7 +597,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)
|
||||
write(io, readstring(open(fn)))
|
||||
write(io, read(open(fn), String))
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
+473
-278
File diff suppressed because it is too large
Load Diff
+10
-32
@@ -10,35 +10,6 @@ const _plotlyjs_scale = _plotly_scale
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function add_backend_string(::PlotlyJSBackend)
|
||||
"""
|
||||
if !Plots.is_installed("PlotlyJS")
|
||||
Pkg.add("PlotlyJS")
|
||||
end
|
||||
if !Plots.is_installed("Rsvg")
|
||||
Pkg.add("Rsvg")
|
||||
end
|
||||
import Blink
|
||||
Blink.AtomShell.install()
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
function _initialize_backend(::PlotlyJSBackend; kw...)
|
||||
@eval begin
|
||||
import PlotlyJS
|
||||
export PlotlyJS
|
||||
end
|
||||
|
||||
# # override IJulia inline display
|
||||
# if isijulia()
|
||||
# IJulia.display_dict(plt::AbstractPlot{PlotlyJSBackend}) = IJulia.display_dict(plt.o)
|
||||
# end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
|
||||
@@ -60,14 +31,14 @@ end
|
||||
|
||||
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
|
||||
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
||||
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(plt.series_list, series),
|
||||
findfirst(isequal(series), plt.series_list),
|
||||
kw
|
||||
)
|
||||
end
|
||||
@@ -85,7 +56,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot{PlotlyJSBackend})
|
||||
if isijulia() && !_use_remote[]
|
||||
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
|
||||
else
|
||||
@@ -98,6 +69,7 @@ function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
|
||||
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")
|
||||
@@ -116,6 +88,12 @@ function _display(plt::Plot{PlotlyJSBackend})
|
||||
end
|
||||
end
|
||||
|
||||
@require WebIO = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" begin
|
||||
function WebIO.render(plt::Plot{PlotlyJSBackend})
|
||||
prepare_output(plt)
|
||||
WebIO.render(plt.o)
|
||||
end
|
||||
end
|
||||
|
||||
function closeall(::PlotlyJSBackend)
|
||||
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
|
||||
|
||||
+350
-309
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,9 @@
|
||||
|
||||
# [ADD BACKEND WEBSITE]
|
||||
|
||||
function _initialize_backend(::[PkgName]Backend; kw...)
|
||||
@eval begin
|
||||
import [PkgName]
|
||||
export [PkgName]
|
||||
# todo: other initialization that needs to be eval-ed
|
||||
end
|
||||
# todo: other initialization
|
||||
end
|
||||
import [PkgName]
|
||||
export [PkgName]
|
||||
push!(_initialized_backends, [pgkname]::Symbol)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -54,7 +49,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (plotattributes[:annotations])
|
||||
function _update_plot_object(plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const _unicodeplots_attr = merge_with_base_supported([
|
||||
:guide, :lims,
|
||||
])
|
||||
const _unicodeplots_seriestype = [
|
||||
:path, :scatter,
|
||||
:path, :scatter, :straightline,
|
||||
# :bar,
|
||||
:shape,
|
||||
:histogram2d,
|
||||
@@ -25,26 +25,10 @@ const _unicodeplots_scale = [:identity]
|
||||
|
||||
|
||||
# don't warn on unsupported... there's just too many warnings!!
|
||||
warnOnUnsupported_args(::UnicodePlotsBackend, d::KW) = nothing
|
||||
warnOnUnsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::UnicodePlotsBackend)
|
||||
"""
|
||||
Pkg.add("UnicodePlots")
|
||||
Pkg.build("UnicodePlots")
|
||||
"""
|
||||
end
|
||||
|
||||
function _initialize_backend(::UnicodePlotsBackend; kw...)
|
||||
@eval begin
|
||||
import UnicodePlots
|
||||
export UnicodePlots
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
const _canvas_type = Ref(:auto)
|
||||
|
||||
function _canvas_map()
|
||||
@@ -120,7 +104,7 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
|
||||
# now use the ! functions to add to the plot
|
||||
for series in series_list(sp)
|
||||
addUnicodeSeries!(o, series.d, sp[:legend] != :none, xlim, ylim)
|
||||
addUnicodeSeries!(o, series.plotattributes, sp[:legend] != :none, xlim, ylim)
|
||||
end
|
||||
|
||||
# save the object
|
||||
@@ -130,17 +114,17 @@ end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
function addUnicodeSeries!(o, plotattributes::KW, addlegend::Bool, xlim, ylim)
|
||||
# get the function, or special handling for step/bar/hist
|
||||
st = d[:seriestype]
|
||||
st = plotattributes[:seriestype]
|
||||
if st == :histogram2d
|
||||
UnicodePlots.densityplot!(o, d[:x], d[:y])
|
||||
UnicodePlots.densityplot!(o, plotattributes[:x], plotattributes[:y])
|
||||
return
|
||||
end
|
||||
|
||||
if st == :path
|
||||
if st in (:path, :straightline)
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif st == :scatter || d[:markershape] != :none
|
||||
elseif st == :scatter || plotattributes[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
# elseif st == :bar
|
||||
# func = UnicodePlots.barplot!
|
||||
@@ -151,14 +135,20 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
end
|
||||
|
||||
# get the series data and label
|
||||
x, y = [collect(float(d[s])) for s in (:x, :y)]
|
||||
label = addlegend ? d[:label] : ""
|
||||
x, y = if st == :straightline
|
||||
straightline_data(plotattributes)
|
||||
elseif st == :shape
|
||||
shape_data(series)
|
||||
else
|
||||
[collect(float(plotattributes[s])) for s in (:x, :y)]
|
||||
end
|
||||
label = addlegend ? plotattributes[:label] : ""
|
||||
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
|
||||
color = plotattributes[:linecolor] in UnicodePlots.color_cycle ? plotattributes[:linecolor] : :auto
|
||||
|
||||
# add the series
|
||||
x, y = Plots.unzip(collect(filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
|
||||
x, y = Plots.unzip(collect(Base.Iterators.filter(xy->isfinite(xy[1])&&isfinite(xy[2]), zip(x,y))))
|
||||
func(o, x, y; color = color, name = label)
|
||||
end
|
||||
|
||||
@@ -173,7 +163,7 @@ function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
|
||||
gui(plt)
|
||||
|
||||
# @osx_only begin
|
||||
@static if is_apple()
|
||||
@static if Sys.isapple()
|
||||
# BEGIN HACK
|
||||
|
||||
# wait while the plot gets drawn
|
||||
@@ -202,7 +192,7 @@ end
|
||||
|
||||
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
|
||||
unicodeplots_rebuild(plt)
|
||||
map(show, plt.o)
|
||||
foreach(x -> show(io, x), plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
+5
-5
@@ -3,13 +3,13 @@
|
||||
|
||||
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
|
||||
|
||||
|
||||
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
|
||||
"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>$title</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
$(html_head(plt))
|
||||
</head>
|
||||
<body>
|
||||
@@ -20,16 +20,16 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr
|
||||
end
|
||||
|
||||
function open_browser_window(filename::AbstractString)
|
||||
@static if is_apple()
|
||||
@static if Sys.isapple()
|
||||
return run(`open $(filename)`)
|
||||
end
|
||||
@static if is_linux() || is_bsd() # is_bsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
|
||||
@static if Sys.islinux() || Sys.isbsd() # Sys.isbsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
|
||||
return run(`xdg-open $(filename)`)
|
||||
end
|
||||
@static if is_windows()
|
||||
@static if Sys.iswindows()
|
||||
return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
|
||||
end
|
||||
warn("Unknown OS... cannot open browser window.")
|
||||
@warn("Unknown OS... cannot open browser window.")
|
||||
end
|
||||
|
||||
function write_temp_html(plt::AbstractPlot)
|
||||
|
||||
+104
-48
@@ -7,11 +7,11 @@ nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
|
||||
nanpush!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); append!(a, b))
|
||||
compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : angle)
|
||||
compute_angle(v::P2) = (angle = atan(v[2], v[1]); angle < 0 ? 2π - angle : angle)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
immutable Shape
|
||||
struct Shape
|
||||
x::Vector{Float64}
|
||||
y::Vector{Float64}
|
||||
# function Shape(x::AVec, y::AVec)
|
||||
@@ -58,7 +58,7 @@ end
|
||||
|
||||
"get an array of tuples of points on a circle with radius `r`"
|
||||
function partialcircle(start_θ, end_θ, n = 20, r=1)
|
||||
Tuple{Float64,Float64}[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
|
||||
Tuple{Float64,Float64}[(r*cos(u),r*sin(u)) for u in range(start_θ, stop=end_θ, length=n)]
|
||||
end
|
||||
|
||||
"interleave 2 vectors into each other (like a zipper's teeth)"
|
||||
@@ -68,7 +68,8 @@ function weave(x,y; ordering = Vector[x,y])
|
||||
while !done
|
||||
for o in ordering
|
||||
try
|
||||
push!(ret, shift!(o))
|
||||
push!(ret, popfirst!(o))
|
||||
catch
|
||||
end
|
||||
end
|
||||
done = isempty(x) && isempty(y)
|
||||
@@ -246,7 +247,7 @@ end
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
type Font
|
||||
mutable struct Font
|
||||
family::AbstractString
|
||||
pointsize::Int
|
||||
halign::Symbol
|
||||
@@ -296,7 +297,7 @@ function font(args...)
|
||||
elseif typeof(arg) <: Real
|
||||
rotation = convert(Float64, arg)
|
||||
else
|
||||
warn("Unused font arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused font arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -305,7 +306,7 @@ end
|
||||
|
||||
function scalefontsize(k::Symbol, factor::Number)
|
||||
f = default(k)
|
||||
f.pointsize = round(Int, factor * f.pointsize)
|
||||
f = round(Int, factor * f)
|
||||
default(k, f)
|
||||
end
|
||||
|
||||
@@ -315,7 +316,7 @@ end
|
||||
Scales all **current** font sizes by `factor`. For example `scalefontsizes(1.1)` increases all current font sizes by 10%. To reset to initial sizes, use `scalefontsizes()`
|
||||
"""
|
||||
function scalefontsizes(factor::Number)
|
||||
for k in (:titlefont, :guidefont, :tickfont, :legendfont)
|
||||
for k in (:titlefontsize, :guidefontsize, :tickfontsize, :legendfontsize)
|
||||
scalefontsize(k, factor)
|
||||
end
|
||||
end
|
||||
@@ -326,17 +327,17 @@ end
|
||||
Resets font sizes to initial default values.
|
||||
"""
|
||||
function scalefontsizes()
|
||||
for k in (:titlefont, :guidefont, :tickfont, :legendfont)
|
||||
for k in (:titlefontsize, :guidefontsize, :tickfontsize, :legendfontsize)
|
||||
f = default(k)
|
||||
if k in keys(_initial_fontsizes)
|
||||
factor = f.pointsize / _initial_fontsizes[k]
|
||||
factor = f / _initial_fontsizes[k]
|
||||
scalefontsize(k, 1.0/factor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"Wrap a string with font info"
|
||||
immutable PlotText
|
||||
struct PlotText
|
||||
str::AbstractString
|
||||
font::Font
|
||||
end
|
||||
@@ -348,6 +349,7 @@ PlotText(str) = PlotText(string(str), font())
|
||||
Create a PlotText object wrapping a string with font info, for plot annotations
|
||||
"""
|
||||
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...))
|
||||
@@ -359,7 +361,7 @@ Base.length(t::PlotText) = length(t.str)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
immutable Stroke
|
||||
struct Stroke
|
||||
width
|
||||
color
|
||||
alpha
|
||||
@@ -387,13 +389,14 @@ function stroke(args...; alpha = nothing)
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
catch
|
||||
end
|
||||
elseif allAlphas(arg)
|
||||
alpha = arg
|
||||
elseif allReals(arg)
|
||||
width = arg
|
||||
else
|
||||
warn("Unused stroke arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused stroke arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -401,7 +404,7 @@ function stroke(args...; alpha = nothing)
|
||||
end
|
||||
|
||||
|
||||
immutable Brush
|
||||
struct Brush
|
||||
size # fillrange, markersize, or any other sizey attribute
|
||||
color
|
||||
alpha
|
||||
@@ -419,13 +422,14 @@ function brush(args...; alpha = nothing)
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
catch
|
||||
end
|
||||
elseif allAlphas(arg)
|
||||
alpha = arg
|
||||
elseif allReals(arg)
|
||||
size = arg
|
||||
else
|
||||
warn("Unused brush arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused brush arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -434,19 +438,19 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type SeriesAnnotations
|
||||
mutable struct SeriesAnnotations
|
||||
strs::AbstractVector # the labels/names
|
||||
font::Font
|
||||
baseshape::Nullable
|
||||
baseshape::Union{Shape, AbstractVector{Shape}, Nothing}
|
||||
scalefactor::Tuple
|
||||
end
|
||||
function series_annotations(strs::AbstractVector, args...)
|
||||
fnt = font()
|
||||
shp = Nullable{Any}()
|
||||
shp = nothing
|
||||
scalefactor = (1,1)
|
||||
for arg in args
|
||||
if isa(arg, Shape) || (isa(arg, AbstractVector) && eltype(arg) == Shape)
|
||||
shp = Nullable(arg)
|
||||
shp = arg
|
||||
elseif isa(arg, Font)
|
||||
fnt = arg
|
||||
elseif isa(arg, Symbol) && haskey(_shapes, arg)
|
||||
@@ -456,7 +460,7 @@ function series_annotations(strs::AbstractVector, args...)
|
||||
elseif is_2tuple(arg)
|
||||
scalefactor = arg
|
||||
else
|
||||
warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
|
||||
@warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
|
||||
end
|
||||
end
|
||||
# if scalefactor != 1
|
||||
@@ -467,7 +471,7 @@ function series_annotations(strs::AbstractVector, args...)
|
||||
SeriesAnnotations(strs, fnt, shp, scalefactor)
|
||||
end
|
||||
series_annotations(anns::SeriesAnnotations) = anns
|
||||
series_annotations(::Void) = nothing
|
||||
series_annotations(::Nothing) = nothing
|
||||
|
||||
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
anns = series[:series_annotations]
|
||||
@@ -482,12 +486,13 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
# end
|
||||
|
||||
# @show msw msh
|
||||
if anns != nothing && !isnull(anns.baseshape)
|
||||
if anns != nothing && anns.baseshape != nothing
|
||||
# we use baseshape to overwrite the markershape attribute
|
||||
# with a list of custom shapes for each
|
||||
msw,msh = anns.scalefactor
|
||||
msize = Float64[]
|
||||
shapes = Shape[begin
|
||||
shapes = Vector{Shape}(length(anns.strs))
|
||||
for i in eachindex(anns.strs)
|
||||
str = _cycle(anns.strs,i)
|
||||
|
||||
# get the width and height of the string (in mm)
|
||||
@@ -505,22 +510,25 @@ function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||
maxscale = max(xscale, yscale)
|
||||
push!(msize, maxscale)
|
||||
baseshape = _cycle(get(anns.baseshape),i)
|
||||
shape = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
||||
end for i=1:length(anns.strs)]
|
||||
shapes[i] = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
||||
end
|
||||
series[:markershape] = shapes
|
||||
series[:markersize] = msize
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
type EachAnn
|
||||
mutable struct EachAnn
|
||||
anns
|
||||
x
|
||||
y
|
||||
end
|
||||
Base.start(ea::EachAnn) = 1
|
||||
Base.done(ea::EachAnn, i) = ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
function Base.next(ea::EachAnn, i)
|
||||
|
||||
function Base.iterate(ea::EachAnn, i = 1)
|
||||
if ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||
return nothing
|
||||
end
|
||||
|
||||
tmp = _cycle(ea.anns.strs,i)
|
||||
str,fnt = if isa(tmp, PlotText)
|
||||
tmp.str, tmp.font
|
||||
@@ -530,20 +538,68 @@ function Base.next(ea::EachAnn, i)
|
||||
((_cycle(ea.x,i), _cycle(ea.y,i), str, fnt), i+1)
|
||||
end
|
||||
|
||||
annotations(::Void) = []
|
||||
annotations(::Nothing) = []
|
||||
annotations(anns::AVec) = anns
|
||||
annotations(anns) = Any[anns]
|
||||
annotations(sa::SeriesAnnotations) = sa
|
||||
|
||||
# Expand arrays of coordinates, positions and labels into induvidual annotations
|
||||
# and make sure labels are of type PlotText
|
||||
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)
|
||||
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)))
|
||||
end
|
||||
end
|
||||
anns
|
||||
end
|
||||
function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol}, labs, font = font())
|
||||
anns = []
|
||||
positions, labs = makevec(positions), makevec(labs)
|
||||
for i in 1:max(length(positions), length(labs))
|
||||
pos, lab = _cycle(positions, i), _cycle(labs, i)
|
||||
pos = get(_positionAliases, pos, pos)
|
||||
if lab == :auto
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font)))
|
||||
else
|
||||
push!(anns, (pos, isa(lab, PlotText) ? lab : text(lab, font)))
|
||||
end
|
||||
end
|
||||
anns
|
||||
end
|
||||
|
||||
# Give each annotation coordinates based on specified position
|
||||
function locate_annotation(sp::Subplot, pos::Symbol, lab::PlotText)
|
||||
position_multiplier = Dict{Symbol, Tuple{Float64,Float64}}(
|
||||
:topleft => (0.1, 0.9),
|
||||
:topcenter => (0.5, 0.9),
|
||||
:topright => (0.9, 0.9),
|
||||
:bottomleft => (0.1, 0.1),
|
||||
:bottomcenter => (0.5, 0.1),
|
||||
:bottomright => (0.9, 0.1),
|
||||
)
|
||||
xmin, xmax = ignorenan_extrema(sp[:xaxis])
|
||||
ymin, ymax = ignorenan_extrema(sp[:yaxis])
|
||||
x, y = (xmin, ymin).+ position_multiplier[pos].* (xmax - xmin, ymax - ymin)
|
||||
(x, y, lab)
|
||||
end
|
||||
locate_annotation(sp::Subplot, x, y, label::PlotText) = (x, y, label)
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"type which represents z-values for colors and sizes (and anything else that might come up)"
|
||||
immutable ZValues
|
||||
struct ZValues
|
||||
values::Vector{Float64}
|
||||
zrange::Tuple{Float64,Float64}
|
||||
end
|
||||
|
||||
function zvalues{T<:Real}(values::AVec{T}, zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values)))
|
||||
function zvalues(values::AVec{T}, zrange::Tuple{T,T} = (ignorenan_minimum(values), ignorenan_maximum(values))) where T<:Real
|
||||
ZValues(collect(float(values)), map(Float64, zrange))
|
||||
end
|
||||
|
||||
@@ -552,7 +608,7 @@ end
|
||||
abstract type AbstractSurface end
|
||||
|
||||
"represents a contour or surface mesh"
|
||||
immutable Surface{M<:AMat} <: AbstractSurface
|
||||
struct Surface{M<:AMat} <: AbstractSurface
|
||||
surf::M
|
||||
end
|
||||
|
||||
@@ -564,7 +620,7 @@ for f in (:length, :size)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
end
|
||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||
Base.eltype{T}(surf::Surface{T}) = eltype(T)
|
||||
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
|
||||
|
||||
function expand_extrema!(a::Axis, surf::Surface)
|
||||
ex = a[:extrema]
|
||||
@@ -575,7 +631,7 @@ function expand_extrema!(a::Axis, surf::Surface)
|
||||
end
|
||||
|
||||
"For the case of representing a surface as a function of x/y... can possibly avoid allocations."
|
||||
immutable SurfaceFunction <: AbstractSurface
|
||||
struct SurfaceFunction <: AbstractSurface
|
||||
f::Function
|
||||
end
|
||||
|
||||
@@ -585,19 +641,19 @@ end
|
||||
# # I don't want to clash with ValidatedNumerics, but this would be nice:
|
||||
# ..(a::T, b::T) = (a,b)
|
||||
|
||||
immutable Volume{T}
|
||||
struct Volume{T}
|
||||
v::Array{T,3}
|
||||
x_extents::Tuple{T,T}
|
||||
y_extents::Tuple{T,T}
|
||||
z_extents::Tuple{T,T}
|
||||
end
|
||||
|
||||
default_extents{T}(::Type{T}) = (zero(T), one(T))
|
||||
default_extents(::Type{T}) where {T} = (zero(T), one(T))
|
||||
|
||||
function Volume{T}(v::Array{T,3},
|
||||
x_extents = default_extents(T),
|
||||
y_extents = default_extents(T),
|
||||
z_extents = default_extents(T))
|
||||
function Volume(v::Array{T,3},
|
||||
x_extents = default_extents(T),
|
||||
y_extents = default_extents(T),
|
||||
z_extents = default_extents(T)) where T
|
||||
Volume(v, x_extents, y_extents, z_extents)
|
||||
end
|
||||
|
||||
@@ -605,13 +661,13 @@ Base.Array(vol::Volume) = vol.v
|
||||
for f in (:length, :size)
|
||||
@eval Base.$f(vol::Volume, args...) = $f(vol.v, args...)
|
||||
end
|
||||
Base.copy{T}(vol::Volume{T}) = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
|
||||
Base.eltype{T}(vol::Volume{T}) = T
|
||||
Base.copy(vol::Volume{T}) where {T} = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
|
||||
Base.eltype(vol::Volume{T}) where {T} = T
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# style is :open or :closed (for now)
|
||||
immutable Arrow
|
||||
struct Arrow
|
||||
style::Symbol
|
||||
side::Symbol # :head (default), :tail, or :both
|
||||
headlength::Float64
|
||||
@@ -648,7 +704,7 @@ function arrow(args...)
|
||||
elseif T <: Tuple && length(arg) == 2
|
||||
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
|
||||
else
|
||||
warn("Skipped arrow arg $arg")
|
||||
@warn("Skipped arrow arg $arg")
|
||||
end
|
||||
end
|
||||
Arrow(style, side, headlength, headwidth)
|
||||
@@ -673,14 +729,14 @@ end
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
"Represents data values with formatting that should apply to the tick labels."
|
||||
immutable Formatted{T}
|
||||
struct Formatted{T}
|
||||
data::T
|
||||
formatter::Function
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
"create a BezierCurve for plotting"
|
||||
type BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
mutable struct BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
control_points::Vector{T}
|
||||
end
|
||||
|
||||
@@ -698,7 +754,7 @@ end
|
||||
|
||||
@deprecate curve_points coords
|
||||
|
||||
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, range(range..., stop=n, length=50))
|
||||
|
||||
# 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]
|
||||
|
||||
@@ -72,7 +72,7 @@ is_subplot_supported(::BokehBackend) = false
|
||||
|
||||
function _initialize_backend(::BokehBackend; kw...)
|
||||
@eval begin
|
||||
warn("Bokeh is no longer supported... many features will likely be broken.")
|
||||
@warn("Bokeh is no longer supported... many features will likely be broken.")
|
||||
import Bokeh
|
||||
export Bokeh
|
||||
end
|
||||
@@ -95,9 +95,9 @@ const _glyphtypes = KW(
|
||||
)
|
||||
|
||||
|
||||
function bokeh_glyph_type(d::KW)
|
||||
st = d[:seriestype]
|
||||
mt = d[:markershape]
|
||||
function bokeh_glyph_type(plotattributes::KW)
|
||||
st = plotattributes[:seriestype]
|
||||
mt = plotattributes[:markershape]
|
||||
if st == :scatter && mt == :none
|
||||
mt = :circle
|
||||
end
|
||||
@@ -125,7 +125,7 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# function _create_plot(pkg::BokehBackend, d::KW)
|
||||
# function _create_plot(pkg::BokehBackend, plotattributes::KW)
|
||||
function _create_backend_figure(plt::Plot{BokehBackend})
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
@@ -142,34 +142,34 @@ function _create_backend_figure(plt::Plot{BokehBackend})
|
||||
extra_args = KW() # TODO: we'll put extra settings (xlim, etc) here
|
||||
Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
|
||||
|
||||
# Plot(bplt, pkg, 0, d, KW[])
|
||||
# Plot(bplt, pkg, 0, plotattributes, KW[])
|
||||
end
|
||||
|
||||
|
||||
# function _series_added(::BokehBackend, plt::Plot, d::KW)
|
||||
# function _series_added(::BokehBackend, plt::Plot, plotattributes::KW)
|
||||
function _series_added(plt::Plot{BokehBackend}, series::Series)
|
||||
bdata = Dict{Symbol, Vector}(:x => collect(series.d[:x]), :y => collect(series.d[:y]))
|
||||
bdata = Dict{Symbol, Vector}(:x => collect(series.plotattributes[:x]), :y => collect(series.plotattributes[:y]))
|
||||
|
||||
glyph = Bokeh.Bokehjs.Glyph(
|
||||
glyphtype = bokeh_glyph_type(d),
|
||||
linecolor = webcolor(d[:linecolor]), # shape's stroke or line color
|
||||
linewidth = d[:linewidth], # shape's stroke width or line width
|
||||
fillcolor = webcolor(d[:markercolor]),
|
||||
size = ceil(Int, d[:markersize] * 2.5), # magic number 2.5 to keep in same scale as other backends
|
||||
dash = get_stroke_vector(d[:linestyle])
|
||||
glyphtype = bokeh_glyph_type(plotattributes),
|
||||
linecolor = webcolor(plotattributes[:linecolor]), # shape's stroke or line color
|
||||
linewidth = plotattributes[:linewidth], # shape's stroke width or line width
|
||||
fillcolor = webcolor(plotattributes[:markercolor]),
|
||||
size = ceil(Int, plotattributes[:markersize] * 2.5), # magic number 2.5 to keep in same scale as other backends
|
||||
dash = get_stroke_vector(plotattributes[:linestyle])
|
||||
)
|
||||
|
||||
legend = nothing # TODO
|
||||
push!(plt.o.datacolumns, Bokeh.BokehDataSet(bdata, glyph, legend))
|
||||
|
||||
# push!(plt.seriesargs, d)
|
||||
# push!(plt.seriesargs, plotattributes)
|
||||
# plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot_object(plt::Plot{BokehBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{BokehBackend}, plotattributes::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
@@ -196,7 +196,7 @@ end
|
||||
|
||||
function Base.show(io::IO, ::MIME"image/png", plt::AbstractPlot{BokehBackend})
|
||||
# TODO: write a png to io
|
||||
warn("mime png not implemented")
|
||||
@warn("mime png not implemented")
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
|
||||
|
||||
+114
-114
@@ -51,32 +51,32 @@ end
|
||||
# Base.size(v::MissingVec) = (1,)
|
||||
# Base.getindex(v::MissingVec, i::Integer) = 0.0
|
||||
|
||||
function createGadflyPlotObject(d::KW)
|
||||
function createGadflyPlotObject(plotattributes::KW)
|
||||
gplt = Gadfly.Plot()
|
||||
gplt.mapping = Dict()
|
||||
gplt.data_source = Gadfly.DataFrames.DataFrame()
|
||||
# gplt.layers = gplt.layers[1:0]
|
||||
gplt.layers = [Gadfly.layer(Gadfly.Geom.point(tag=:remove), x=zeros(1), y=zeros(1));] # x=MissingVec(), y=MissingVec());]
|
||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xguide]),
|
||||
Gadfly.Guide.ylabel(d[:yguide]),
|
||||
Gadfly.Guide.title(d[:title])]
|
||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(plotattributes[:xguide]),
|
||||
Gadfly.Guide.ylabel(plotattributes[:yguide]),
|
||||
Gadfly.Guide.title(plotattributes[:title])]
|
||||
gplt
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getLineGeom(d::KW)
|
||||
st = d[:seriestype]
|
||||
xbins, ybins = maketuple(d[:bins])
|
||||
function getLineGeom(plotattributes::KW)
|
||||
st = plotattributes[:seriestype]
|
||||
xbins, ybins = maketuple(plotattributes[:bins])
|
||||
if st == :hexb
|
||||
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
|
||||
elseif st == :histogram2d
|
||||
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
|
||||
elseif st == :histogram
|
||||
Gadfly.Geom.histogram(bincount = xbins,
|
||||
orientation = isvertical(d) ? :vertical : :horizontal,
|
||||
position = d[:bar_position] == :stack ? :stack : :dodge)
|
||||
orientation = isvertical(plotattributes) ? :vertical : :horizontal,
|
||||
position = plotattributes[:bar_position] == :stack ? :stack : :dodge)
|
||||
elseif st == :path
|
||||
Gadfly.Geom.path
|
||||
elseif st in (:bar, :sticks)
|
||||
@@ -90,7 +90,7 @@ function getLineGeom(d::KW)
|
||||
elseif st == :vline
|
||||
Gadfly.Geom.vline
|
||||
elseif st == :contour
|
||||
Gadfly.Geom.contour(levels = d[:levels])
|
||||
Gadfly.Geom.contour(levels = plotattributes[:levels])
|
||||
# elseif st == :shape
|
||||
# Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
else
|
||||
@@ -98,11 +98,11 @@ function getLineGeom(d::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function get_extra_theme_args(d::KW, k::Symbol)
|
||||
function get_extra_theme_args(plotattributes::KW, k::Symbol)
|
||||
# gracefully handles old Gadfly versions
|
||||
extra_theme_args = KW()
|
||||
try
|
||||
extra_theme_args[:line_style] = Gadfly.get_stroke_vector(d[k])
|
||||
extra_theme_args[:line_style] = Gadfly.get_stroke_vector(plotattributes[k])
|
||||
catch err
|
||||
if string(err) == "UndefVarError(:get_stroke_vector)"
|
||||
Base.warn_once("Gadfly.get_stroke_vector failed... do you have an old version of Gadfly?")
|
||||
@@ -113,53 +113,53 @@ function get_extra_theme_args(d::KW, k::Symbol)
|
||||
extra_theme_args
|
||||
end
|
||||
|
||||
function getGadflyLineTheme(d::KW)
|
||||
st = d[:seriestype]
|
||||
lc = convertColor(getColor(d[:linecolor]), d[:linealpha])
|
||||
fc = convertColor(getColor(d[:fillcolor]), d[:fillalpha])
|
||||
function getGadflyLineTheme(plotattributes::KW)
|
||||
st = plotattributes[:seriestype]
|
||||
lc = convertColor(getColor(plotattributes[:linecolor]), plotattributes[:linealpha])
|
||||
fc = convertColor(getColor(plotattributes[:fillcolor]), plotattributes[:fillalpha])
|
||||
|
||||
Gadfly.Theme(;
|
||||
default_color = (st in (:histogram,:histogram2d,:hexbin,:bar,:sticks) ? fc : lc),
|
||||
line_width = (st == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
|
||||
# line_style = Gadfly.get_stroke_vector(d[:linestyle]),
|
||||
line_width = (st == :sticks ? 1 : plotattributes[:linewidth]) * Gadfly.px,
|
||||
# line_style = Gadfly.get_stroke_vector(plotattributes[:linestyle]),
|
||||
lowlight_color = x->RGB(fc), # fill/ribbon
|
||||
lowlight_opacity = alpha(fc), # fill/ribbon
|
||||
bar_highlight = RGB(lc), # bars
|
||||
get_extra_theme_args(d, :linestyle)...
|
||||
get_extra_theme_args(plotattributes, :linestyle)...
|
||||
)
|
||||
end
|
||||
|
||||
# add a line as a new layer
|
||||
function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
|
||||
function addGadflyLine!(plt::Plot, numlayers::Int, plotattributes::KW, geoms...)
|
||||
gplt = getGadflyContext(plt)
|
||||
gfargs = vcat(geoms..., getGadflyLineTheme(d))
|
||||
gfargs = vcat(geoms..., getGadflyLineTheme(plotattributes))
|
||||
kwargs = KW()
|
||||
st = d[:seriestype]
|
||||
st = plotattributes[:seriestype]
|
||||
|
||||
# add a fill?
|
||||
if d[:fillrange] != nothing && st != :contour
|
||||
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
|
||||
if plotattributes[:fillrange] != nothing && st != :contour
|
||||
fillmin, fillmax = map(makevec, maketuple(plotattributes[:fillrange]))
|
||||
nmin, nmax = length(fillmin), length(fillmax)
|
||||
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(plotattributes[:y])]
|
||||
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(plotattributes[:y])]
|
||||
push!(gfargs, Gadfly.Geom.ribbon)
|
||||
end
|
||||
|
||||
if st in (:hline, :vline)
|
||||
kwargs[st == :hline ? :yintercept : :xintercept] = d[:y]
|
||||
kwargs[st == :hline ? :yintercept : :xintercept] = plotattributes[:y]
|
||||
|
||||
else
|
||||
if st == :sticks
|
||||
w = 0.01 * mean(diff(d[:x]))
|
||||
kwargs[:xmin] = d[:x] - w
|
||||
kwargs[:xmax] = d[:x] + w
|
||||
w = 0.01 * mean(diff(plotattributes[:x]))
|
||||
kwargs[:xmin] = plotattributes[:x] - w
|
||||
kwargs[:xmax] = plotattributes[:x] + w
|
||||
elseif st == :contour
|
||||
kwargs[:z] = d[:z].surf
|
||||
addGadflyContColorScale(plt, d[:linecolor])
|
||||
kwargs[:z] = plotattributes[:z].surf
|
||||
addGadflyContColorScale(plt, plotattributes[:linecolor])
|
||||
end
|
||||
|
||||
kwargs[:x] = d[st == :histogram ? :y : :x]
|
||||
kwargs[:y] = d[:y]
|
||||
kwargs[:x] = plotattributes[st == :histogram ? :y : :x]
|
||||
kwargs[:y] = plotattributes[:y]
|
||||
|
||||
end
|
||||
|
||||
@@ -180,24 +180,24 @@ getMarkerGeom(other) = gadflyshape(get_shape(other))
|
||||
# getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
||||
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
|
||||
function getMarkerGeom(d::KW)
|
||||
if d[:seriestype] == :shape
|
||||
function getMarkerGeom(plotattributes::KW)
|
||||
if plotattributes[:seriestype] == :shape
|
||||
Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
else
|
||||
getMarkerGeom(d[:markershape])
|
||||
getMarkerGeom(plotattributes[:markershape])
|
||||
end
|
||||
end
|
||||
|
||||
function getGadflyMarkerTheme(d::KW, attr::KW)
|
||||
c = getColor(d[:markercolor])
|
||||
α = d[:markeralpha]
|
||||
function getGadflyMarkerTheme(plotattributes::KW, attr::KW)
|
||||
c = getColor(plotattributes[:markercolor])
|
||||
α = plotattributes[:markeralpha]
|
||||
if α != nothing
|
||||
c = RGBA(RGB(c), α)
|
||||
end
|
||||
|
||||
ms = d[:markersize]
|
||||
ms = plotattributes[:markersize]
|
||||
ms = if typeof(ms) <: AVec
|
||||
warn("Gadfly doesn't support variable marker sizes... using the average: $(mean(ms))")
|
||||
@warn("Gadfly doesn't support variable marker sizes... using the average: $(mean(ms))")
|
||||
mean(ms) * Gadfly.px
|
||||
else
|
||||
ms * Gadfly.px
|
||||
@@ -206,10 +206,10 @@ function getGadflyMarkerTheme(d::KW, attr::KW)
|
||||
Gadfly.Theme(;
|
||||
default_color = c,
|
||||
default_point_size = ms,
|
||||
discrete_highlight_color = c -> RGB(getColor(d[:markerstrokecolor])),
|
||||
highlight_width = d[:markerstrokewidth] * Gadfly.px,
|
||||
line_width = d[:markerstrokewidth] * Gadfly.px,
|
||||
# get_extra_theme_args(d, :markerstrokestyle)...
|
||||
discrete_highlight_color = c -> RGB(getColor(plotattributes[:markerstrokecolor])),
|
||||
highlight_width = plotattributes[:markerstrokewidth] * Gadfly.px,
|
||||
line_width = plotattributes[:markerstrokewidth] * Gadfly.px,
|
||||
# get_extra_theme_args(plotattributes, :markerstrokestyle)...
|
||||
)
|
||||
end
|
||||
|
||||
@@ -221,30 +221,30 @@ function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
|
||||
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
|
||||
end
|
||||
|
||||
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, attr::KW, geoms...)
|
||||
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, attr), getMarkerGeom(d))
|
||||
function addGadflyMarker!(plt::Plot, numlayers::Int, plotattributes::KW, attr::KW, geoms...)
|
||||
gfargs = vcat(geoms..., getGadflyMarkerTheme(plotattributes, attr), getMarkerGeom(plotattributes))
|
||||
kwargs = KW()
|
||||
|
||||
# handle continuous color scales for the markers
|
||||
zcolor = d[:marker_z]
|
||||
zcolor = plotattributes[:marker_z]
|
||||
if zcolor != nothing && typeof(zcolor) <: AVec
|
||||
kwargs[:color] = zcolor
|
||||
addGadflyContColorScale(plt, d[:markercolor])
|
||||
addGadflyContColorScale(plt, plotattributes[:markercolor])
|
||||
end
|
||||
|
||||
Gadfly.layer(gfargs...; x = d[:x], y = d[:y], order=numlayers, kwargs...)
|
||||
Gadfly.layer(gfargs...; x = plotattributes[:x], y = plotattributes[:y], order=numlayers, kwargs...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function addToGadflyLegend(plt::Plot, d::KW)
|
||||
if plt.attr[:legend] != :none && d[:label] != ""
|
||||
function addToGadflyLegend(plt::Plot, plotattributes::KW)
|
||||
if plt.attr[:legend] != :none && plotattributes[:label] != ""
|
||||
gplt = getGadflyContext(plt)
|
||||
|
||||
# add the legend if needed
|
||||
if all(g -> !isa(g, Gadfly.Guide.ManualColorKey), gplt.guides)
|
||||
unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
|
||||
pushfirst!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
|
||||
end
|
||||
|
||||
# now add the series to the legend
|
||||
@@ -254,20 +254,20 @@ function addToGadflyLegend(plt::Plot, d::KW)
|
||||
# since gadfly will call unique(colors), but doesn't also merge the rows that match
|
||||
# Should ensure from this side that colors which are the same are merged together
|
||||
|
||||
c = getColor(d[d[:markershape] == :none ? :linecolor : :markercolor])
|
||||
c = getColor(plotattributes[plotattributes[:markershape] == :none ? :linecolor : :markercolor])
|
||||
foundit = false
|
||||
|
||||
# extend the label if we found this color
|
||||
for i in 1:length(guide.colors)
|
||||
if RGB(c) == guide.colors[i]
|
||||
guide.labels[i] *= ", " * d[:label]
|
||||
guide.labels[i] *= ", " * plotattributes[:label]
|
||||
foundit = true
|
||||
end
|
||||
end
|
||||
|
||||
# didn't find the color, so add a new entry into the legend
|
||||
if !foundit
|
||||
push!(guide.labels, d[:label])
|
||||
push!(guide.labels, plotattributes[:label])
|
||||
push!(guide.colors, c)
|
||||
end
|
||||
end
|
||||
@@ -279,40 +279,40 @@ getGadflySmoothing(smooth::Bool) = smooth ? [Gadfly.Geom.smooth(method=:lm)] : A
|
||||
getGadflySmoothing(smooth::Real) = [Gadfly.Geom.smooth(method=:loess, smoothing=float(smooth))]
|
||||
|
||||
|
||||
function addGadflySeries!(plt::Plot, d::KW)
|
||||
function addGadflySeries!(plt::Plot, plotattributes::KW)
|
||||
layers = Gadfly.Layer[]
|
||||
gplt = getGadflyContext(plt)
|
||||
|
||||
# add a regression line?
|
||||
# TODO: make more flexible
|
||||
smooth = getGadflySmoothing(d[:smooth])
|
||||
smooth = getGadflySmoothing(plotattributes[:smooth])
|
||||
|
||||
# lines
|
||||
geom = getLineGeom(d)
|
||||
geom = getLineGeom(plotattributes)
|
||||
if geom != nothing
|
||||
prepend!(layers, addGadflyLine!(plt, length(gplt.layers), d, geom, smooth...))
|
||||
prepend!(layers, addGadflyLine!(plt, length(gplt.layers), plotattributes, geom, smooth...))
|
||||
smooth = Any[] # don't add a regression for markers too
|
||||
end
|
||||
|
||||
# special handling for ohlc and scatter
|
||||
st = d[:seriestype]
|
||||
st = plotattributes[:seriestype]
|
||||
# if st == :ohlc
|
||||
# error("Haven't re-implemented after refactoring")
|
||||
if st in (:histogram2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
|
||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
|
||||
elseif st == :scatter && d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
if st in (:histogram2d, :hexbin) && (isa(plotattributes[:fillcolor], ColorGradient) || isa(plotattributes[:fillcolor], ColorFunction))
|
||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(plotattributes[:fillcolor], p))))
|
||||
elseif st == :scatter && plotattributes[:markershape] == :none
|
||||
plotattributes[:markershape] = :circle
|
||||
end
|
||||
|
||||
# markers
|
||||
if d[:markershape] != :none || st == :shape
|
||||
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.attr, smooth...))
|
||||
if plotattributes[:markershape] != :none || st == :shape
|
||||
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), plotattributes, plt.attr, smooth...))
|
||||
end
|
||||
|
||||
st in (:histogram2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
|
||||
st in (:histogram2d, :hexbin, :contour) || addToGadflyLegend(plt, plotattributes)
|
||||
|
||||
# now save the layers that apply to this series
|
||||
d[:gadflylayers] = layers
|
||||
plotattributes[:gadflylayers] = layers
|
||||
prepend!(gplt.layers, layers)
|
||||
end
|
||||
|
||||
@@ -322,10 +322,10 @@ end
|
||||
# NOTE: I'm leaving this here and commented out just in case I want to implement again... it was hacky code to create multi-colored line segments
|
||||
|
||||
# # colorgroup
|
||||
# z = d[:z]
|
||||
# z = plotattributes[:z]
|
||||
|
||||
# # handle line segments of different colors
|
||||
# cscheme = d[:linecolor]
|
||||
# cscheme = plotattributes[:linecolor]
|
||||
# if isa(cscheme, ColorVector)
|
||||
# # create a color scale, and set the color group to the index of the color
|
||||
# push!(gplt.scales, Gadfly.Scale.color_discrete_manual(cscheme.v...))
|
||||
@@ -333,10 +333,10 @@ end
|
||||
# # this is super weird, but... oh well... for some reason this creates n separate line segments...
|
||||
# # create a list of vertices that go: [x1,x2,x2,x3,x3, ... ,xi,xi, ... xn,xn] (same for y)
|
||||
# # then the vector passed to the "color" keyword should be a vector: [1,1,2,2,3,3,4,4, ..., i,i, ... , n,n]
|
||||
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(d[:y])]
|
||||
# cs = collect(repmat(csindices', 2, 1))[1:end-1]
|
||||
# grp = collect(repmat((1:length(d[:y]))', 2, 1))[1:end-1]
|
||||
# d[:x], d[:y] = map(createSegments, (d[:x], d[:y]))
|
||||
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(plotattributes[:y])]
|
||||
# cs = collect(repeat(csindices', 2, 1))[1:end-1]
|
||||
# grp = collect(repeat((1:length(plotattributes[:y]))', 2, 1))[1:end-1]
|
||||
# plotattributes[:x], plotattributes[:y] = map(createSegments, (plotattributes[:x], plotattributes[:y]))
|
||||
# colorgroup = [(:linecolor, cs), (:group, grp)]
|
||||
|
||||
|
||||
@@ -388,11 +388,11 @@ continuousAndSameAxis(scale, isx::Bool) = isa(scale, Gadfly.Scale.ContinuousScal
|
||||
filterGadflyScale(gplt, isx::Bool) = filter!(scale -> !continuousAndSameAxis(scale, isx), gplt.scales)
|
||||
|
||||
|
||||
function getGadflyScaleFunction(d::KW, isx::Bool)
|
||||
function getGadflyScaleFunction(plotattributes::KW, isx::Bool)
|
||||
scalekey = isx ? :xscale : :yscale
|
||||
hasScaleKey = haskey(d, scalekey)
|
||||
hasScaleKey = haskey(plotattributes, scalekey)
|
||||
if hasScaleKey
|
||||
scale = d[scalekey]
|
||||
scale = plotattributes[scalekey]
|
||||
scale == :ln && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey, log
|
||||
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey, log2
|
||||
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey, log10
|
||||
@@ -403,15 +403,15 @@ function getGadflyScaleFunction(d::KW, isx::Bool)
|
||||
end
|
||||
|
||||
|
||||
function addGadflyLimitsScale(gplt, d::KW, isx::Bool)
|
||||
gfunc, hasScaleKey, func = getGadflyScaleFunction(d, isx)
|
||||
function addGadflyLimitsScale(gplt, plotattributes::KW, isx::Bool)
|
||||
gfunc, hasScaleKey, func = getGadflyScaleFunction(plotattributes, isx)
|
||||
|
||||
# do we want to add min/max limits for the axis?
|
||||
limsym = isx ? :xlims : :ylims
|
||||
limargs = Any[]
|
||||
|
||||
# map :auto to nothing, otherwise add to limargs
|
||||
lims = get(d, limsym, :auto)
|
||||
lims = get(plotattributes, limsym, :auto)
|
||||
if lims == :auto
|
||||
lims = nothing
|
||||
else
|
||||
@@ -432,7 +432,7 @@ function addGadflyLimitsScale(gplt, d::KW, isx::Bool)
|
||||
lims, func
|
||||
end
|
||||
|
||||
function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
|
||||
function updateGadflyAxisFlips(gplt, plotattributes::KW, xlims, ylims, xfunc, yfunc)
|
||||
if isa(gplt.coord, Gadfly.Coord.Cartesian)
|
||||
gplt.coord = Gadfly.Coord.cartesian(
|
||||
gplt.coord.xvars,
|
||||
@@ -441,16 +441,16 @@ function updateGadflyAxisFlips(gplt, d::KW, xlims, ylims, xfunc, yfunc)
|
||||
xmax = xlims == nothing ? gplt.coord.xmax : xfunc(maximum(xlims)),
|
||||
ymin = ylims == nothing ? gplt.coord.ymin : yfunc(minimum(ylims)),
|
||||
ymax = ylims == nothing ? gplt.coord.ymax : yfunc(maximum(ylims)),
|
||||
xflip = get(d, :xflip, gplt.coord.xflip),
|
||||
yflip = get(d, :yflip, gplt.coord.yflip),
|
||||
xflip = get(plotattributes, :xflip, gplt.coord.xflip),
|
||||
yflip = get(plotattributes, :yflip, gplt.coord.yflip),
|
||||
fixed = gplt.coord.fixed,
|
||||
aspect_ratio = gplt.coord.aspect_ratio,
|
||||
raster = gplt.coord.raster
|
||||
)
|
||||
else
|
||||
gplt.coord = Gadfly.Coord.Cartesian(
|
||||
xflip = get(d, :xflip, false),
|
||||
yflip = get(d, :yflip, false)
|
||||
xflip = get(plotattributes, :xflip, false),
|
||||
yflip = get(plotattributes, :yflip, false)
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -464,37 +464,37 @@ function findGuideAndSet(gplt, t::DataType, args...; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
function updateGadflyGuides(plt::Plot, d::KW)
|
||||
function updateGadflyGuides(plt::Plot, plotattributes::KW)
|
||||
gplt = getGadflyContext(plt)
|
||||
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
|
||||
haskey(d, :xguide) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xguide]))
|
||||
haskey(d, :yguide) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:yguide]))
|
||||
haskey(plotattributes, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(plotattributes[:title]))
|
||||
haskey(plotattributes, :xguide) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(plotattributes[:xguide]))
|
||||
haskey(plotattributes, :yguide) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(plotattributes[:yguide]))
|
||||
|
||||
xlims, xfunc = addGadflyLimitsScale(gplt, d, true)
|
||||
ylims, yfunc = addGadflyLimitsScale(gplt, d, false)
|
||||
xlims, xfunc = addGadflyLimitsScale(gplt, plotattributes, true)
|
||||
ylims, yfunc = addGadflyLimitsScale(gplt, plotattributes, false)
|
||||
|
||||
ticks = get(d, :xticks, :auto)
|
||||
ticks = get(plotattributes, :xticks, :auto)
|
||||
if ticks == :none
|
||||
_remove_axis(plt, true)
|
||||
else
|
||||
addGadflyTicksGuide(gplt, ticks, true)
|
||||
end
|
||||
ticks = get(d, :yticks, :auto)
|
||||
ticks = get(plotattributes, :yticks, :auto)
|
||||
if ticks == :none
|
||||
_remove_axis(plt, false)
|
||||
else
|
||||
addGadflyTicksGuide(gplt, ticks, false)
|
||||
end
|
||||
|
||||
updateGadflyAxisFlips(gplt, d, xlims, ylims, xfunc, yfunc)
|
||||
updateGadflyAxisFlips(gplt, plotattributes, xlims, ylims, xfunc, yfunc)
|
||||
end
|
||||
|
||||
function updateGadflyPlotTheme(plt::Plot, d::KW)
|
||||
function updateGadflyPlotTheme(plt::Plot, plotattributes::KW)
|
||||
kwargs = KW()
|
||||
|
||||
# colors
|
||||
insidecolor, gridcolor, textcolor, guidecolor, legendcolor =
|
||||
map(s -> getColor(d[s]), (
|
||||
map(s -> getColor(plotattributes[s]), (
|
||||
:background_color_inside,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_text,
|
||||
@@ -503,17 +503,17 @@ function updateGadflyPlotTheme(plt::Plot, d::KW)
|
||||
))
|
||||
|
||||
# # hide the legend?
|
||||
leg = d[d[:legend] == :none ? :colorbar : :legend]
|
||||
leg = plotattributes[plotattributes[:legend] == :none ? :colorbar : :legend]
|
||||
if leg != :best
|
||||
kwargs[:key_position] = leg == :inside ? :right : leg
|
||||
end
|
||||
|
||||
if !get(d, :grid, true)
|
||||
if !get(plotattributes, :grid, true)
|
||||
kwargs[:grid_color] = gridcolor
|
||||
end
|
||||
|
||||
# fonts
|
||||
tfont, gfont, lfont = d[:tickfont], d[:guidefont], d[:legendfont]
|
||||
tfont, gfont, lfont = plotattributes[:tickfont], plotattributes[:guidefont], plotattributes[:legendfont]
|
||||
|
||||
getGadflyContext(plt).theme = Gadfly.Theme(;
|
||||
background_color = insidecolor,
|
||||
@@ -558,7 +558,7 @@ function createGadflyAnnotationObject(x, y, txt::PlotText)
|
||||
))
|
||||
end
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{GadflyBackend}, anns::AVec{Tuple{X,Y,V}})
|
||||
function _add_annotations(plt::Plot{GadflyBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
|
||||
end
|
||||
@@ -568,9 +568,9 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
# function _create_plot(pkg::GadflyBackend, d::KW)
|
||||
# gplt = createGadflyPlotObject(d)
|
||||
# Plot(gplt, pkg, 0, d, KW[])
|
||||
# function _create_plot(pkg::GadflyBackend, plotattributes::KW)
|
||||
# gplt = createGadflyPlotObject(plotattributes)
|
||||
# Plot(gplt, pkg, 0, plotattributes, KW[])
|
||||
# end
|
||||
function _create_backend_figure(plt::Plot{GadflyBackend})
|
||||
createGadflyPlotObject(plt.attr)
|
||||
@@ -578,7 +578,7 @@ end
|
||||
|
||||
|
||||
# plot one data series
|
||||
# function _series_added(::GadflyBackend, plt::Plot, d::KW)
|
||||
# function _series_added(::GadflyBackend, plt::Plot, plotattributes::KW)
|
||||
function _series_added(plt::Plot{GadflyBackend}, series::Series)
|
||||
# first clear out the temporary layer
|
||||
gplt = getGadflyContext(plt)
|
||||
@@ -586,16 +586,16 @@ function _series_added(plt::Plot{GadflyBackend}, series::Series)
|
||||
gplt.layers = gplt.layers[2:end]
|
||||
end
|
||||
|
||||
addGadflySeries!(plt, series.d)
|
||||
# push!(plt.seriesargs, d)
|
||||
addGadflySeries!(plt, series.plotattributes)
|
||||
# push!(plt.seriesargs, plotattributes)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{GadflyBackend}, d::KW)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
function _update_plot_object(plt::Plot{GadflyBackend}, plotattributes::KW)
|
||||
updateGadflyGuides(plt, plotattributes)
|
||||
updateGadflyPlotTheme(plt, plotattributes)
|
||||
end
|
||||
|
||||
|
||||
@@ -614,7 +614,7 @@ function getxy(plt::Plot{GadflyBackend}, i::Integer)
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
function setxy!(plt::Plot{GadflyBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
@@ -677,7 +677,7 @@ setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
function doshow{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, func, plt::AbstractPlot{P})
|
||||
function doshow(io::IO, func, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
|
||||
gplt = getGadflyContext(plt)
|
||||
setGadflyDisplaySize(plt)
|
||||
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
|
||||
@@ -692,7 +692,7 @@ getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF
|
||||
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
|
||||
|
||||
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex")
|
||||
@eval function Base.show{P<:Union{GadflyBackend,ImmerseBackend}}(io::IO, ::$mime, plt::AbstractPlot{P})
|
||||
@eval function Base.show(io::IO, ::$mime, plt::AbstractPlot{P}) where P<:Union{GadflyBackend,ImmerseBackend}
|
||||
func = getGadflyWriteFunc($mime())
|
||||
doshow(io, func, plt)
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Geometry which displays arbitrary shapes at given (x, y) positions.
|
||||
# note: vertices is a list of shapes
|
||||
immutable ShapeGeometry <: Gadfly.GeometryElement
|
||||
struct ShapeGeometry <: Gadfly.GeometryElement
|
||||
vertices::AbstractVector #{Tuple{Float64,Float64}}
|
||||
tag::Symbol
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ function _initialize_backend(::ImmerseBackend; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
function createImmerseFigure(d::KW)
|
||||
w,h = d[:size]
|
||||
figidx = Immerse.figure(; name = d[:window_title], width = w, height = h)
|
||||
function createImmerseFigure(plotattributes::KW)
|
||||
w,h = plotattributes[:size]
|
||||
figidx = Immerse.figure(; name = plotattributes[:window_title], width = w, height = h)
|
||||
Immerse.Figure(figidx)
|
||||
end
|
||||
|
||||
@@ -28,12 +28,12 @@ end
|
||||
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
# function _create_plot(pkg::ImmerseBackend, d::KW)
|
||||
# function _create_plot(pkg::ImmerseBackend, plotattributes::KW)
|
||||
# # create the underlying Gadfly.Plot object
|
||||
# gplt = createGadflyPlotObject(d)
|
||||
# gplt = createGadflyPlotObject(plotattributes)
|
||||
#
|
||||
# # save both the Immerse.Figure and the Gadfly.Plot
|
||||
# Plot((nothing,gplt), pkg, 0, d, KW[])
|
||||
# Plot((nothing,gplt), pkg, 0, plotattributes, KW[])
|
||||
# end
|
||||
function _create_backend_figure(plt::Plot{ImmerseBackend})
|
||||
(nothing, createGadflyPlotObject(plt.attr))
|
||||
@@ -41,27 +41,27 @@ end
|
||||
|
||||
|
||||
# # plot one data series
|
||||
# function _series_added(::ImmerseBackend, plt::Plot, d::KW)
|
||||
# addGadflySeries!(plt, d)
|
||||
# push!(plt.seriesargs, d)
|
||||
# function _series_added(::ImmerseBackend, plt::Plot, plotattributes::KW)
|
||||
# addGadflySeries!(plt, plotattributes)
|
||||
# push!(plt.seriesargs, plotattributes)
|
||||
# plt
|
||||
# end
|
||||
|
||||
function _series_added(plt::Plot{ImmerseBackend}, series::Series)
|
||||
addGadflySeries!(plt, series.d)
|
||||
addGadflySeries!(plt, series.plotattributes)
|
||||
end
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{ImmerseBackend}, d::KW)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
function _update_plot_object(plt::Plot{ImmerseBackend}, plotattributes::KW)
|
||||
updateGadflyGuides(plt, plotattributes)
|
||||
updateGadflyPlotTheme(plt, plotattributes)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{ImmerseBackend}, anns::AVec{Tuple{X,Y,V}})
|
||||
function _add_annotations(plt::Plot{ImmerseBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
|
||||
end
|
||||
@@ -76,7 +76,7 @@ function getxy(plt::Plot{ImmerseBackend}, i::Integer)
|
||||
mapping[:x], mapping[:y]
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
function setxy!(plt::Plot{ImmerseBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
for mapping in getGadflyMappings(plt, i)
|
||||
mapping[:x], mapping[:y] = xy
|
||||
end
|
||||
@@ -94,10 +94,10 @@ end
|
||||
#
|
||||
# function showSubplotObject(subplt::Subplot{ImmerseBackend})
|
||||
# # create the Gtk window with vertical box vsep
|
||||
# d = getattr(subplt,1)
|
||||
# w,h = d[:size]
|
||||
# plotattributes = getattr(subplt,1)
|
||||
# w,h = plotattributes[:size]
|
||||
# vsep = Gtk.GtkBoxLeaf(:v)
|
||||
# win = Gtk.GtkWindowLeaf(vsep, d[:window_title], w, h)
|
||||
# win = Gtk.GtkWindowLeaf(vsep, plotattributes[:window_title], w, h)
|
||||
#
|
||||
# figindices = []
|
||||
# row = Gtk.GtkBoxLeaf(:h)
|
||||
|
||||
@@ -31,7 +31,7 @@ is_subplot_supported(::QwtBackend) = true
|
||||
|
||||
function _initialize_backend(::QwtBackend; kw...)
|
||||
@eval begin
|
||||
warn("Qwt is no longer supported... many features will likely be broken.")
|
||||
@warn("Qwt is no longer supported... many features will likely be broken.")
|
||||
import Qwt
|
||||
export Qwt
|
||||
end
|
||||
@@ -52,84 +52,84 @@ const _qwtAliases = KW(
|
||||
:star8 => :star2,
|
||||
)
|
||||
|
||||
function fixcolors(d::KW)
|
||||
for (k,v) in d
|
||||
function fixcolors(plotattributes::KW)
|
||||
for (k,v) in plotattributes
|
||||
if typeof(v) <: ColorScheme
|
||||
d[k] = getColor(v)
|
||||
plotattributes[k] = getColor(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function replaceQwtAliases(d, s)
|
||||
if haskey(_qwtAliases, d[s])
|
||||
d[s] = _qwtAliases[d[s]]
|
||||
function replaceQwtAliases(plotattributes, s)
|
||||
if haskey(_qwtAliases, plotattributes[s])
|
||||
plotattributes[s] = _qwtAliases[plotattributes[s]]
|
||||
end
|
||||
end
|
||||
|
||||
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
|
||||
d = KW(kw)
|
||||
st = d[:seriestype]
|
||||
plotattributes = KW(kw)
|
||||
st = plotattributes[:seriestype]
|
||||
if st == :scatter
|
||||
d[:seriestype] = :none
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
plotattributes[:seriestype] = :none
|
||||
if plotattributes[:markershape] == :none
|
||||
plotattributes[:markershape] = :circle
|
||||
end
|
||||
|
||||
elseif st in (:hline, :vline)
|
||||
addLineMarker(plt, d)
|
||||
d[:seriestype] = :none
|
||||
d[:markershape] = :circle
|
||||
d[:markersize] = 1
|
||||
addLineMarker(plt, plotattributes)
|
||||
plotattributes[:seriestype] = :none
|
||||
plotattributes[:markershape] = :circle
|
||||
plotattributes[:markersize] = 1
|
||||
if st == :vline
|
||||
d[:x], d[:y] = d[:y], d[:x]
|
||||
plotattributes[:x], plotattributes[:y] = plotattributes[:y], plotattributes[:x]
|
||||
end
|
||||
|
||||
elseif !iscreating && st == :bar
|
||||
d = barHack(; kw...)
|
||||
plotattributes = barHack(; kw...)
|
||||
elseif !iscreating && st == :histogram
|
||||
d = barHack(; histogramHack(; kw...)...)
|
||||
plotattributes = barHack(; histogramHack(; kw...)...)
|
||||
end
|
||||
|
||||
replaceQwtAliases(d, :seriestype)
|
||||
replaceQwtAliases(d, :markershape)
|
||||
replaceQwtAliases(plotattributes, :seriestype)
|
||||
replaceQwtAliases(plotattributes, :markershape)
|
||||
|
||||
for k in keys(d)
|
||||
for k in keys(plotattributes)
|
||||
if haskey(_qwtAliases, k)
|
||||
d[_qwtAliases[k]] = d[k]
|
||||
plotattributes[_qwtAliases[k]] = plotattributes[k]
|
||||
end
|
||||
end
|
||||
|
||||
d[:x] = collect(d[:x])
|
||||
d[:y] = collect(d[:y])
|
||||
plotattributes[:x] = collect(plotattributes[:x])
|
||||
plotattributes[:y] = collect(plotattributes[:y])
|
||||
|
||||
d
|
||||
plotattributes
|
||||
end
|
||||
|
||||
# function _create_plot(pkg::QwtBackend, d::KW)
|
||||
# function _create_plot(pkg::QwtBackend, plotattributes::KW)
|
||||
function _create_backend_figure(plt::Plot{QwtBackend})
|
||||
fixcolors(plt.attr)
|
||||
dumpdict(plt.attr,"\n\n!!! plot")
|
||||
o = Qwt.plot(zeros(0,0); plt.attr..., show=false)
|
||||
# plt = Plot(o, pkg, 0, d, KW[])
|
||||
# plt = Plot(o, pkg, 0, plotattributes, KW[])
|
||||
# plt
|
||||
end
|
||||
|
||||
# function _series_added(::QwtBackend, plt::Plot, d::KW)
|
||||
# function _series_added(::QwtBackend, plt::Plot, plotattributes::KW)
|
||||
function _series_added(plt::Plot{QwtBackend}, series::Series)
|
||||
d = adjustQwtKeywords(plt, false; series.d...)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot!")
|
||||
Qwt.oplot(plt.o; d...)
|
||||
# push!(plt.seriesargs, d)
|
||||
plotattributes = adjustQwtKeywords(plt, false; series.plotattributes...)
|
||||
fixcolors(plotattributes)
|
||||
dumpdict(plotattributes,"\n\n!!! plot!")
|
||||
Qwt.oplot(plt.o; plotattributes...)
|
||||
# push!(plt.seriesargs, plotattributes)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
|
||||
lims = get(d, isx ? :xlims : :ylims, nothing)
|
||||
ticks = get(d, isx ? :xticks : :yticks, nothing)
|
||||
function updateLimsAndTicks(plt::Plot{QwtBackend}, plotattributes::KW, isx::Bool)
|
||||
lims = get(plotattributes, isx ? :xlims : :ylims, nothing)
|
||||
ticks = get(plotattributes, isx ? :xticks : :yticks, nothing)
|
||||
w = plt.o.widget
|
||||
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
|
||||
|
||||
@@ -142,7 +142,7 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
|
||||
w[:setAxisScale](axisid, lims...)
|
||||
end
|
||||
|
||||
if typeof(ticks) <: Range
|
||||
if typeof(ticks) <: AbstractRange
|
||||
if isx
|
||||
plt.o.autoscale_x = false
|
||||
else
|
||||
@@ -150,49 +150,49 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
|
||||
end
|
||||
w[:setAxisScale](axisid, float(minimum(ticks)), float(maximum(ticks)), float(step(ticks)))
|
||||
elseif !(ticks in (nothing, :none, :auto))
|
||||
warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
|
||||
@warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
|
||||
end
|
||||
|
||||
# change the scale
|
||||
scalesym = isx ? :xscale : :yscale
|
||||
if haskey(d, scalesym)
|
||||
scaletype = d[scalesym]
|
||||
if haskey(plotattributes, scalesym)
|
||||
scaletype = plotattributes[scalesym]
|
||||
scaletype == :identity && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLinearScaleEngine())
|
||||
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
|
||||
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
|
||||
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
|
||||
scaletype in supported_scales() || warn("Unsupported scale type: ", scaletype)
|
||||
scaletype in supported_scales() || @warn("Unsupported scale type: ", scaletype)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function _update_plot_object(plt::Plot{QwtBackend}, d::KW)
|
||||
haskey(d, :title) && Qwt.title(plt.o, d[:title])
|
||||
haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide])
|
||||
haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide])
|
||||
updateLimsAndTicks(plt, d, true)
|
||||
updateLimsAndTicks(plt, d, false)
|
||||
function _update_plot_object(plt::Plot{QwtBackend}, plotattributes::KW)
|
||||
haskey(plotattributes, :title) && Qwt.title(plt.o, plotattributes[:title])
|
||||
haskey(plotattributes, :xguide) && Qwt.xlabel(plt.o, plotattributes[:xguide])
|
||||
haskey(plotattributes, :yguide) && Qwt.ylabel(plt.o, plotattributes[:yguide])
|
||||
updateLimsAndTicks(plt, plotattributes, true)
|
||||
updateLimsAndTicks(plt, plotattributes, false)
|
||||
end
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{QwtBackend}, d::KW)
|
||||
haskey(d, :size) && Qwt.resizewidget(plt.o, d[:size]...)
|
||||
haskey(d, :pos) && Qwt.movewidget(plt.o, d[:pos]...)
|
||||
function _update_plot_pos_size(plt::AbstractPlot{QwtBackend}, plotattributes::KW)
|
||||
haskey(plotattributes, :size) && Qwt.resizewidget(plt.o, plotattributes[:size]...)
|
||||
haskey(plotattributes, :pos) && Qwt.movewidget(plt.o, plotattributes[:pos]...)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
|
||||
function addLineMarker(plt::Plot{QwtBackend}, d::KW)
|
||||
for yi in d[:y]
|
||||
function addLineMarker(plt::Plot{QwtBackend}, plotattributes::KW)
|
||||
for yi in plotattributes[:y]
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
ishorizontal = (d[:seriestype] == :hline)
|
||||
ishorizontal = (plotattributes[:seriestype] == :hline)
|
||||
marker[:setLineStyle](ishorizontal ? 1 : 2)
|
||||
marker[ishorizontal ? :setYValue : :setXValue](yi)
|
||||
qcolor = Qwt.convertRGBToQColor(getColor(d[:linecolor]))
|
||||
linestyle = plt.o.widget[:getLineStyle](string(d[:linestyle]))
|
||||
marker[:setLinePen](Qwt.QT.QPen(qcolor, d[:linewidth], linestyle))
|
||||
qcolor = Qwt.convertRGBToQColor(getColor(plotattributes[:linecolor]))
|
||||
linestyle = plt.o.widget[:getLineStyle](string(plotattributes[:linestyle]))
|
||||
marker[:setLinePen](Qwt.QT.QPen(qcolor, plotattributes[:linewidth], linestyle))
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
@@ -218,7 +218,7 @@ function createQwtAnnotation(plt::Plot, x, y, val::AbstractString)
|
||||
marker[:attach](plt.o.widget)
|
||||
end
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{QwtBackend}, anns::AVec{Tuple{X,Y,V}})
|
||||
function _add_annotations(plt::Plot{QwtBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
createQwtAnnotation(plt, ann...)
|
||||
end
|
||||
@@ -233,7 +233,7 @@ function getxy(plt::Plot{QwtBackend}, i::Int)
|
||||
series.x, series.y
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
function setxy!(plt::Plot{QwtBackend}, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
series = plt.o.lines[i]
|
||||
series.x, series.y = xy
|
||||
plt
|
||||
|
||||
@@ -34,7 +34,7 @@ is_subplot_supported(::WinstonBackend) = false
|
||||
function _initialize_backend(::WinstonBackend; kw...)
|
||||
@eval begin
|
||||
# ENV["WINSTON_OUTPUT"] = "gtk"
|
||||
warn("Winston is no longer supported... many features will likely be broken.")
|
||||
@warn("Winston is no longer supported... many features will likely be broken.")
|
||||
import Winston, Gtk
|
||||
export Winston, Gtk
|
||||
end
|
||||
@@ -75,10 +75,10 @@ function _create_backend_figure(plt::Plot{WinstonBackend})
|
||||
)
|
||||
end
|
||||
|
||||
copy_remove(d::KW, s::Symbol) = delete!(copy(d), s)
|
||||
copy_remove(plotattributes::KW, s::Symbol) = delete!(copy(plotattributes), s)
|
||||
|
||||
function addRegressionLineWinston(d::KW, wplt)
|
||||
xs, ys = regressionXY(d[:x], d[:y])
|
||||
function addRegressionLineWinston(plotattributes::KW, wplt)
|
||||
xs, ys = regressionXY(plotattributes[:x], plotattributes[:y])
|
||||
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
|
||||
end
|
||||
|
||||
@@ -93,22 +93,22 @@ function getWinstonItems(plt::Plot)
|
||||
end
|
||||
|
||||
function _series_added(plt::Plot{WinstonBackend}, series::Series)
|
||||
d = series.d
|
||||
plotattributes = series.plotattributes
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
# until we call it normally, do the hack
|
||||
if d[:seriestype] == :bar
|
||||
d = barHack(;d...)
|
||||
if plotattributes[:seriestype] == :bar
|
||||
plotattributes = barHack(;plotattributes...)
|
||||
end
|
||||
|
||||
|
||||
e = KW()
|
||||
e[:color] = getColor(d[:linecolor])
|
||||
e[:linewidth] = d[:linewidth]
|
||||
e[:kind] = winston_linestyle[d[:linestyle]]
|
||||
e[:symbolkind] = winston_marker[d[:markershape]]
|
||||
e[:color] = getColor(plotattributes[:linecolor])
|
||||
e[:linewidth] = plotattributes[:linewidth]
|
||||
e[:kind] = winston_linestyle[plotattributes[:linestyle]]
|
||||
e[:symbolkind] = winston_marker[plotattributes[:markershape]]
|
||||
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
|
||||
e[:symbolsize] = d[:markersize] / 5
|
||||
e[:symbolsize] = plotattributes[:markersize] / 5
|
||||
|
||||
# pos # (Int,Int), move the enclosing window to this position
|
||||
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
|
||||
@@ -116,69 +116,69 @@ function _series_added(plt::Plot{WinstonBackend}, series::Series)
|
||||
|
||||
|
||||
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :histogram2d, :hexbin, :histogram, :bar
|
||||
if d[:seriestype] == :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
if plotattributes[:seriestype] == :none
|
||||
Winston.add(wplt, Winston.Points(plotattributes[:x], plotattributes[:y]; copy_remove(e, :kind)..., color=getColor(plotattributes[:markercolor])))
|
||||
|
||||
elseif d[:seriestype] == :path
|
||||
x, y = d[:x], d[:y]
|
||||
elseif plotattributes[:seriestype] == :path
|
||||
x, y = plotattributes[:x], plotattributes[:y]
|
||||
Winston.add(wplt, Winston.Curve(x, y; e...))
|
||||
|
||||
fillrange = d[:fillrange]
|
||||
fillrange = plotattributes[:fillrange]
|
||||
if fillrange != nothing
|
||||
if isa(fillrange, AbstractVector)
|
||||
y2 = fillrange
|
||||
else
|
||||
y2 = Float64[fillrange for yi in y]
|
||||
end
|
||||
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
|
||||
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(plotattributes[:fillcolor])))
|
||||
end
|
||||
|
||||
elseif d[:seriestype] == :scatter
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
elseif plotattributes[:seriestype] == :scatter
|
||||
if plotattributes[:markershape] == :none
|
||||
plotattributes[:markershape] = :circle
|
||||
end
|
||||
|
||||
# elseif d[:seriestype] == :step
|
||||
# elseif plotattributes[:seriestype] == :step
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:seriestype] == :stepinverted
|
||||
# elseif plotattributes[:seriestype] == :stepinverted
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:seriestype] == :sticks
|
||||
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
|
||||
elseif plotattributes[:seriestype] == :sticks
|
||||
Winston.add(wplt, Winston.Stems(plotattributes[:x], plotattributes[:y]; e...))
|
||||
|
||||
# elseif d[:seriestype] == :dots
|
||||
# elseif plotattributes[:seriestype] == :dots
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:seriestype] == :histogram2d
|
||||
# elseif plotattributes[:seriestype] == :histogram2d
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:seriestype] == :hexbin
|
||||
# elseif plotattributes[:seriestype] == :hexbin
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:seriestype] == :histogram
|
||||
hst = hist(d[:y], d[:bins])
|
||||
elseif plotattributes[:seriestype] == :histogram
|
||||
hst = hist(plotattributes[:y], plotattributes[:bins])
|
||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
|
||||
|
||||
# elseif d[:seriestype] == :bar
|
||||
# elseif plotattributes[:seriestype] == :bar
|
||||
# # fn = Winston.XXX
|
||||
|
||||
else
|
||||
error("seriestype $(d[:seriestype]) not supported by Winston.")
|
||||
error("seriestype $(plotattributes[:seriestype]) not supported by Winston.")
|
||||
|
||||
end
|
||||
|
||||
|
||||
# markershape
|
||||
if d[:markershape] != :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
if plotattributes[:markershape] != :none
|
||||
Winston.add(wplt, Winston.Points(plotattributes[:x], plotattributes[:y]; copy_remove(e, :kind)..., color=getColor(plotattributes[:markercolor])))
|
||||
end
|
||||
|
||||
|
||||
# optionally add a regression line
|
||||
d[:smooth] && d[:seriestype] != :histogram && addRegressionLineWinston(d, wplt)
|
||||
plotattributes[:smooth] && plotattributes[:seriestype] != :histogram && addRegressionLineWinston(plotattributes, wplt)
|
||||
|
||||
# push!(plt.seriesargs, d)
|
||||
# push!(plt.seriesargs, plotattributes)
|
||||
# plt
|
||||
end
|
||||
|
||||
@@ -192,17 +192,17 @@ const _winstonNames = KW(
|
||||
:yscale => :ylog,
|
||||
)
|
||||
|
||||
function _update_plot_object(plt::Plot{WinstonBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{WinstonBackend}, plotattributes::KW)
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
for k in (:xguide, :yguide, :title, :xlims, :ylims)
|
||||
if haskey(d, k)
|
||||
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
|
||||
if haskey(plotattributes, k)
|
||||
Winston.setattr(wplt, string(get(_winstonNames, k, k)), plotattributes[k])
|
||||
end
|
||||
end
|
||||
|
||||
for k in (:xscale, :yscale)
|
||||
if haskey(d, k)
|
||||
islogscale = d[k] == :log10
|
||||
if haskey(plotattributes, k)
|
||||
islogscale = plotattributes[k] == :log10
|
||||
Winston.setattr(wplt, (k == :xscale ? :xlog : :ylog), islogscale)
|
||||
end
|
||||
end
|
||||
@@ -217,7 +217,7 @@ function createWinstonAnnotationObject(plt::Plot{WinstonBackend}, x, y, val::Abs
|
||||
Winston.text(x, y, val)
|
||||
end
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{WinstonBackend}, anns::AVec{Tuple{X,Y,V}})
|
||||
function _add_annotations(plt::Plot{WinstonBackend}, anns::AVec{Tuple{X,Y,V}}) where {X,Y,V}
|
||||
for ann in anns
|
||||
createWinstonAnnotationObject(plt, ann...)
|
||||
end
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
function sample_evenly(v::AVec, n::Integer = length(v))
|
||||
idx = Int[round(Int, x) for x in linspace(1, length(v), n)]
|
||||
idx = Int[round(Int, x) for x in range(1, stop=length(v), length=n)]
|
||||
v[idx]
|
||||
end
|
||||
|
||||
|
||||
+26
-26
@@ -13,15 +13,15 @@ function cgrad(arg, values = nothing; alpha = nothing, scale = :identity)
|
||||
values = if values != nothing
|
||||
values
|
||||
elseif scale in (:log, :log10)
|
||||
log10(linspace(1,10,30))
|
||||
log10(range(1, stop=10, length=30))
|
||||
elseif scale == :log2
|
||||
log2(linspace(1,2,30))
|
||||
log2(range(1, stop=2, length=30))
|
||||
elseif scale == :ln
|
||||
log(linspace(1,pi,30))
|
||||
log(range(1, stop=pi, length=30))
|
||||
elseif scale in (:exp, :exp10)
|
||||
(exp10(linspace(0,1,30)) - 1) / 9
|
||||
(exp10(range(0, stop=1, length=30)) - 1) / 9
|
||||
else
|
||||
linspace(0, 1, length(colors))
|
||||
range(0, stop=1, length=length(colors))
|
||||
end
|
||||
ColorGradient(colors, values)
|
||||
end
|
||||
@@ -34,9 +34,9 @@ getColorVector(scheme::ColorScheme) = [getColor(scheme)]
|
||||
colorscheme(scheme::ColorScheme) = scheme
|
||||
colorscheme(s::AbstractString; kw...) = colorscheme(Symbol(s); kw...)
|
||||
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
|
||||
colorscheme{T<:Real}(s::Symbol, vals::AVec{T}; kw...) = ColorGradient(s, vals; kw...)
|
||||
colorscheme(s::Symbol, vals::AVec{T}; kw...) where {T<:Real} = ColorGradient(s, vals; kw...)
|
||||
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
|
||||
colorscheme{T<:Colorant}(cs::AVec{T}; kw...) = ColorGradient(cs; kw...)
|
||||
colorscheme(cs::AVec{T}; kw...) where {T<:Colorant} = ColorGradient(cs; kw...)
|
||||
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
|
||||
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
|
||||
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
|
||||
@@ -51,7 +51,7 @@ convertColor(c::Symbol) = parse(Colorant, string(c))
|
||||
convertColor(c::Colorant) = c
|
||||
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
|
||||
convertColor(c::ColorScheme) = c
|
||||
convertColor(v::Void) = RGBA(0,0,0,0)
|
||||
convertColor(v::Nothing) = RGBA(0,0,0,0)
|
||||
convertColor(b::Bool) = b ? RGBA(0,0,0,1) : RGBA(0,0,0,0)
|
||||
|
||||
function convertColor(c, α::Real)
|
||||
@@ -59,7 +59,7 @@ function convertColor(c, α::Real)
|
||||
RGBA(RGB(getColor(c)), α)
|
||||
end
|
||||
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
|
||||
convertColor(c, α::Void) = convertColor(c)
|
||||
convertColor(c, α::Nothing) = convertColor(c)
|
||||
|
||||
# backup... try to convert
|
||||
getColor(c) = convertColor(c)
|
||||
@@ -98,7 +98,7 @@ const _gradients = KW(
|
||||
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
||||
)
|
||||
|
||||
function register_gradient_colors{C<:Colorant}(name::Symbol, colors::AVec{C})
|
||||
function register_gradient_colors(name::Symbol, colors::AVec{C}) where C<:Colorant
|
||||
_gradients[name] = colors
|
||||
end
|
||||
|
||||
@@ -109,24 +109,24 @@ default_gradient() = ColorGradient(:inferno)
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
|
||||
immutable ColorGradient <: ColorScheme
|
||||
struct ColorGradient <: ColorScheme
|
||||
colors::Vector
|
||||
values::Vector
|
||||
|
||||
function ColorGradient{S<:Real}(cs::AVec, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing)
|
||||
function ColorGradient(cs::AVec, vals::AVec{S} = range(0, stop=1, length=length(cs)); alpha = nothing) where S<:Real
|
||||
if length(cs) == length(vals)
|
||||
return new(convertColor(cs,alpha), collect(vals))
|
||||
end
|
||||
|
||||
# # otherwise interpolate evenly between the minval and maxval
|
||||
# minval, maxval = minimum(vals), maximum(vals)
|
||||
# vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
|
||||
# vs = Float64[interpolate(minval, maxval, w) for w in range(0, stop = 1, length = length(cs))]
|
||||
# new(convertColor(cs,alpha), vs)
|
||||
|
||||
# interpolate the colors for each value
|
||||
vals = merge(linspace(0, 1, length(cs)), vals)
|
||||
vals = merge(range(0, stop=1, length=length(cs)), vals)
|
||||
grad = ColorGradient(cs)
|
||||
cs = [getColorZ(grad, z) for z in linspace(0, 1, length(vals))]
|
||||
cs = [getColorZ(grad, z) for z in range(0, stop=1, length=length(vals))]
|
||||
new(convertColor(cs, alpha), vals)
|
||||
end
|
||||
end
|
||||
@@ -138,16 +138,16 @@ Base.getindex(cs::ColorGradient, z::Number) = getColorZ(cs, z)
|
||||
|
||||
|
||||
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
|
||||
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:0; kw...)
|
||||
function ColorGradient(s::Symbol, vals::AVec{T} = 0:0; kw...) where T<:Real
|
||||
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
|
||||
cs = _gradients[s]
|
||||
if vals == 0:0
|
||||
vals = linspace(0, 1, length(cs))
|
||||
vals = range(0, stop=1, length=length(cs))
|
||||
end
|
||||
ColorGradient(cs, vals; kw...)
|
||||
end
|
||||
|
||||
# function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = linspace(0, 1, length(cs)); kw...)
|
||||
# function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = range(0, stop = 1, length = length(cs)); kw...)
|
||||
# ColorGradient(map(convertColor, cs), vals; kw...)
|
||||
# end
|
||||
|
||||
@@ -208,7 +208,7 @@ end
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a function, taking an index and returning a Colorant"
|
||||
immutable ColorFunction <: ColorScheme
|
||||
struct ColorFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
@@ -217,7 +217,7 @@ getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a function, taking an z-value and returning a Colorant"
|
||||
immutable ColorZFunction <: ColorScheme
|
||||
struct ColorZFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
@@ -226,7 +226,7 @@ getColorZ(scheme::ColorZFunction, z::Real) = scheme.f(z)
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
|
||||
immutable ColorVector <: ColorScheme
|
||||
struct ColorVector <: ColorScheme
|
||||
v::Vector{Colorant}
|
||||
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
|
||||
end
|
||||
@@ -238,7 +238,7 @@ getColorVector(scheme::ColorVector) = scheme.v
|
||||
# --------------------------------------------------------------
|
||||
|
||||
"Wraps a single color"
|
||||
immutable ColorWrapper <: ColorScheme
|
||||
struct ColorWrapper <: ColorScheme
|
||||
c::RGBA
|
||||
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
|
||||
end
|
||||
@@ -247,7 +247,7 @@ ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Color
|
||||
|
||||
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
|
||||
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
|
||||
convertColor(c::ColorWrapper, α::Void) = c.c
|
||||
convertColor(c::ColorWrapper, α::Nothing) = c.c
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
@@ -332,7 +332,7 @@ function generate_colorgradient(bgcolor = colorant"white";
|
||||
seed_colors,
|
||||
lchoices=Float64[lightness],
|
||||
cchoices=Float64[chroma],
|
||||
hchoices=linspace(0, 340, 20)
|
||||
hchoices=range(0, stop=340, length=20)
|
||||
)[2:end]
|
||||
gradient_from_list(colors)
|
||||
end
|
||||
@@ -347,8 +347,8 @@ function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numco
|
||||
RGBA[getColorZ(grad, z) for z in zrng]
|
||||
end
|
||||
|
||||
function get_color_palette{C<:Colorant}(palette::Vector{C},
|
||||
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
|
||||
function get_color_palette(palette::Vector{C},
|
||||
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer) where C<:Colorant
|
||||
palette
|
||||
end
|
||||
|
||||
|
||||
@@ -5,24 +5,24 @@
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
const FuncOrFuncs = @compat(Union{Function, AVec{Function}})
|
||||
const FuncOrFuncs = Union{Function, AVec{Function}}
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
all3D(plotattributes::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(plotattributes, :seriestype, :none))
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
|
||||
convertToAnyVector(v::Nothing, plotattributes::KW) = Any[nothing], nothing
|
||||
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
convertToAnyVector(n::Integer, plotattributes::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
|
||||
# numeric vector
|
||||
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:Number} = Any[v], nothing
|
||||
|
||||
# string vector
|
||||
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
convertToAnyVector(v::AVec{T}, plotattributes::KW) where {T<:AbstractString} = Any[v], nothing
|
||||
|
||||
function convertToAnyVector(v::AMat, d::KW)
|
||||
if all3D(d)
|
||||
function convertToAnyVector(v::AMat, plotattributes::KW)
|
||||
if all3D(plotattributes)
|
||||
Any[Surface(v)]
|
||||
else
|
||||
Any[v[:,i] for i in 1:size(v,2)]
|
||||
@@ -30,30 +30,30 @@ function convertToAnyVector(v::AMat, d::KW)
|
||||
end
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
convertToAnyVector(f::Function, plotattributes::KW) = Any[f], nothing
|
||||
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
|
||||
convertToAnyVector(s::Surface, plotattributes::KW) = Any[s], nothing
|
||||
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
# convertToAnyVector(v::AVec{OHLC}, plotattributes::KW) = Any[v], nothing
|
||||
|
||||
# dates
|
||||
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||
convertToAnyVector(dts::AVec{D}, plotattributes::KW) where {D<:Union{Date,DateTime}} = Any[dts], nothing
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
function convertToAnyVector(v::AVec, d::KW)
|
||||
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
|
||||
else
|
||||
# something else... treat each element as an item
|
||||
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
|
||||
vcat(Any[convertToAnyVector(vi, plotattributes)[1] for vi in v]...), nothing
|
||||
# Any[vi for vi in v], nothing
|
||||
end
|
||||
end
|
||||
|
||||
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
|
||||
convertToAnyVector(t::Tuple, plotattributes::KW) = Any[t], nothing
|
||||
|
||||
|
||||
function convertToAnyVector(args...)
|
||||
@@ -65,19 +65,19 @@ end
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
# result in that array being shared. push!, etc will add too many items to that array
|
||||
|
||||
compute_x(x::Void, y::Void, z) = 1:size(z,1)
|
||||
compute_x(x::Void, y, z) = 1:size(y,1)
|
||||
compute_x(x::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)
|
||||
|
||||
# compute_y(x::Void, y::Function, z) = error()
|
||||
compute_y(x::Void, y::Void, z) = 1:size(z,2)
|
||||
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)
|
||||
|
||||
compute_z(x, y, z::Function) = map(z, x, y)
|
||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Void) = nothing
|
||||
compute_z(x, y, z::Nothing) = nothing
|
||||
compute_z(x, y, z) = copy(z)
|
||||
|
||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||
@@ -92,8 +92,8 @@ nobigs(v) = v
|
||||
end
|
||||
|
||||
# not allowed
|
||||
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
compute_xyz(x::Nothing, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
+162
-76
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
|
||||
"""
|
||||
type PlotExample
|
||||
mutable struct PlotExample
|
||||
header::AbstractString
|
||||
desc::AbstractString
|
||||
exprs::Vector{Expr}
|
||||
@@ -18,11 +18,18 @@ PlotExample("Lines",
|
||||
),
|
||||
|
||||
PlotExample("Functions, adding data, and animations",
|
||||
"Plot multiple functions. You can also put the function first, or use the form `plot(f, xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data: `x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with `push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the animation.",
|
||||
"""
|
||||
Plot multiple functions. You can also put the function first, or use the form `plot(f,
|
||||
xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data:
|
||||
`x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with
|
||||
`push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available
|
||||
to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the
|
||||
animation.
|
||||
""",
|
||||
[:(begin
|
||||
p = plot([sin,cos], zeros(0), leg=false)
|
||||
anim = Animation()
|
||||
for x in linspace(0, 10π, 100)
|
||||
for x in range(0, stop=10π, length=100)
|
||||
push!(p, x, Float64[sin(x), cos(x)])
|
||||
frame(anim)
|
||||
end
|
||||
@@ -37,23 +44,36 @@ PlotExample("Parametric plots",
|
||||
),
|
||||
|
||||
PlotExample("Colors",
|
||||
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
|
||||
"""
|
||||
Access predefined palettes (or build your own with the `colorscheme` method).
|
||||
Line/marker colors are auto-generated from the plot's palette, unless overridden. Set
|
||||
the `z` argument to turn on series gradients.
|
||||
""",
|
||||
[:(begin
|
||||
y = rand(100)
|
||||
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=0, α=0.6)
|
||||
scatter!(y, zcolor=abs.(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs.(y-0.5)+4, lab="grad")
|
||||
y = rand(100)
|
||||
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=0, α=0.6)
|
||||
scatter!(y, zcolor=abs.(y.-0.5), m=(:heat,0.8,Plots.stroke(1,:green)), ms=10*abs.(y.-0.5).+4,
|
||||
lab="grad")
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Global",
|
||||
"Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow you to pass a tuple or value which will be mapped to the relevant args automatically. The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`, `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`",
|
||||
"""
|
||||
Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow
|
||||
you to pass a tuple or value which will be mapped to the relevant args automatically.
|
||||
The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during
|
||||
the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`,
|
||||
`yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`
|
||||
""",
|
||||
[:(begin
|
||||
y = rand(20,3)
|
||||
plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)
|
||||
hline!(mean(y,1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))
|
||||
vline!([5,10])
|
||||
title!("TITLE")
|
||||
yaxis!("YLABEL", :log10)
|
||||
using Statistics
|
||||
y = rand(20,3)
|
||||
plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2),
|
||||
leg=false)
|
||||
hline!(mean(y, dims = 1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))
|
||||
vline!([5,10])
|
||||
title!("TITLE")
|
||||
yaxis!("YLABEL", :log10)
|
||||
end)]
|
||||
),
|
||||
|
||||
@@ -66,17 +86,24 @@ PlotExample("Global",
|
||||
PlotExample("Images",
|
||||
"Plot an image. y-axis is set to flipped",
|
||||
[:(begin
|
||||
import FileIO
|
||||
img = FileIO.load(Pkg.dir("PlotReferenceImages","Plots","pyplot","0.7.0","ref1.png"))
|
||||
import FileIO, PlotReferenceImages
|
||||
img = FileIO.load(joinpath(dirname(pathof(PlotReferenceImages)), "..", "Plots","pyplot","0.7.0","ref1.png"))
|
||||
plot(img)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Arguments",
|
||||
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors). Special arguments `line`, `marker`, and `fill` will automatically figure out what arguments to set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with `line`.) Note that we pass a matrix of colors, and this applies the colors to each series.",
|
||||
"""
|
||||
Plot multiple series with different numbers of points. Mix arguments that apply to all
|
||||
series (marker/markersize) with arguments unique to each series (colors). Special
|
||||
arguments `line`, `marker`, and `fill` will automatically figure out what arguments to
|
||||
set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with
|
||||
`line`.) Note that we pass a matrix of colors, and this applies the colors to each
|
||||
series.
|
||||
""",
|
||||
[:(begin
|
||||
ys = Vector[rand(10), rand(20)]
|
||||
plot(ys, color=[:black :orange], line=(:dot,4), marker=([:hex :d],12,0.8,stroke(3,:gray)))
|
||||
plot(ys, color=[:black :orange], line=(:dot,4), marker=([:hex :d],12,0.8,Plots.stroke(3,:gray)))
|
||||
end)]
|
||||
),
|
||||
|
||||
@@ -115,12 +142,13 @@ PlotExample("Line types",
|
||||
PlotExample("Line styles",
|
||||
"",
|
||||
[:(begin
|
||||
styles = filter(s -> s in Plots.supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])
|
||||
styles = reshape(styles, 1, length(styles)) # Julia 0.6 unfortunately gives an error when transposing symbol vectors
|
||||
n = length(styles)
|
||||
y = cumsum(randn(20,n),1)
|
||||
plot(y, line = (5, styles), label = map(string,styles), legendtitle = "linestyle")
|
||||
end)]
|
||||
styles = filter(s -> s in Plots.supported_styles(),
|
||||
[:solid, :dash, :dot, :dashdot, :dashdotdot])
|
||||
styles = reshape(styles, 1, length(styles)) # Julia 0.6 unfortunately gives an error when transposing symbol vectors
|
||||
n = length(styles)
|
||||
y = cumsum(randn(20,n), dims = 1)
|
||||
plot(y, line = (5, styles), label = map(string,styles), legendtitle = "linestyle")
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Marker types",
|
||||
@@ -129,8 +157,8 @@ PlotExample("Marker types",
|
||||
markers = filter(m -> m in Plots.supported_markers(), Plots._shape_keys)
|
||||
markers = reshape(markers, 1, length(markers))
|
||||
n = length(markers)
|
||||
x = linspace(0,10,n+2)[2:end-1]
|
||||
y = repmat(reshape(reverse(x),1,:), n, 1)
|
||||
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)]
|
||||
),
|
||||
@@ -145,79 +173,106 @@ PlotExample("Bar",
|
||||
PlotExample("Histogram",
|
||||
"",
|
||||
[:(begin
|
||||
histogram(randn(1000), nbins=20)
|
||||
histogram(randn(1000), bins = :scott, weights = repeat(1:5, outer = 200))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Subplots",
|
||||
"""
|
||||
Use the `layout` keyword, and optionally the convenient `@layout` macro to generate arbitrarily complex subplot layouts.
|
||||
""",
|
||||
"""
|
||||
Use the `layout` keyword, and optionally the convenient `@layout` macro to generate
|
||||
arbitrarily complex subplot layouts.
|
||||
""",
|
||||
[:(begin
|
||||
l = @layout([a{0.1h}; b [c;d e]])
|
||||
plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false, ticks=nothing, border=:none)
|
||||
l = @layout([a{0.1h}; b [c;d e]])
|
||||
plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false,
|
||||
ticks=nothing, border=:none)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Adding to subplots",
|
||||
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
|
||||
"""
|
||||
Note here the automatic grid layout, as well as the order in which new series are added
|
||||
to the plots.
|
||||
""",
|
||||
[:(begin
|
||||
plot(Plots.fakedata(100,10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
|
||||
plot(Plots.fakedata(100,10), layout=4, palette=[:grays :blues :heat :lightrainbow],
|
||||
bg_inside=[:orange :pink :darkblue :black])
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("",
|
||||
"",
|
||||
[:(begin
|
||||
srand(111)
|
||||
using Random
|
||||
Random.seed!(111)
|
||||
plot!(Plots.fakedata(100,10))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Open/High/Low/Close",
|
||||
"Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.",
|
||||
"""
|
||||
Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y`
|
||||
argument. This uses recipes to first convert the tuples to OHLC objects, and
|
||||
subsequently create a :path series with the appropriate line segments.
|
||||
""",
|
||||
[:(begin
|
||||
n=20
|
||||
hgt=rand(n)+1
|
||||
bot=randn(n)
|
||||
openpct=rand(n)
|
||||
closepct=rand(n)
|
||||
y = OHLC[(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n]
|
||||
ohlc(y)
|
||||
n=20
|
||||
hgt=rand(n).+1
|
||||
bot=randn(n)
|
||||
openpct=rand(n)
|
||||
closepct=rand(n)
|
||||
y = OHLC[(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i],
|
||||
closepct[i]*hgt[i]+bot[i]) for i in 1:n]
|
||||
ohlc(y)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Annotations",
|
||||
"The `annotations` keyword is used for text annotations in data-coordinates. Pass in a tuple (x,y,text) or a vector of annotations. `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`. Series annotations are used for annotating individual data points. They require only the annotation... x/y values are computed. A `PlotText` object can be build with the method `text(string, attr...)`, which wraps font and color attributes.",
|
||||
"""
|
||||
The `annotations` keyword is used for text annotations in data-coordinates. Pass in a
|
||||
tuple (x,y,text) or a vector of annotations. `annotate!(ann)` is shorthand for `plot!(;
|
||||
annotation=ann)`. Series annotations are used for annotating individual data points.
|
||||
They require only the annotation... x/y values are computed. A `PlotText` object can be
|
||||
build with the method `text(string, attr...)`, which wraps font and color attributes.
|
||||
""",
|
||||
[:(begin
|
||||
y = rand(10)
|
||||
plot(y, annotations = (3,y[3],text("this is #3",:left)), leg=false)
|
||||
annotate!([(5, y[5], text("this is #5",16,:red,:center)), (10, y[10], text("this is #10",:right,20,"courier"))])
|
||||
scatter!(linspace(2,8,6), rand(6), marker=(50,0.2,:orange), series_annotations = ["series","annotations","map","to","series",text("data",:green)])
|
||||
y = rand(10)
|
||||
plot(y, annotations = (3,y[3],text("this is #3",:left)), leg=false)
|
||||
annotate!([(5, y[5], text("this is #5",16,:red,:center)),
|
||||
(10, y[10], text("this is #10",:right,20,"courier"))])
|
||||
scatter!(range(2, stop=8, length=6), rand(6), marker=(50,0.2,:orange),
|
||||
series_annotations = ["series","annotations","map","to","series",
|
||||
text("data",:green)])
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Custom Markers",
|
||||
"A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.",
|
||||
"""A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported
|
||||
backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and
|
||||
the size is expected to be rougly the area of the unit circle.
|
||||
""",
|
||||
[:(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.7rand(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)
|
||||
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.7rand(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)]
|
||||
),
|
||||
|
||||
PlotExample("Contours",
|
||||
"Any value for fill works here. We first build a filled contour from a function, then an unfilled contour from a matrix.",
|
||||
"""
|
||||
Any value for fill works here. We first build a filled contour from a function, then an
|
||||
unfilled contour from a matrix.
|
||||
""",
|
||||
[:(begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x,y) = (3x+y^2)*abs(sin(x)+cos(y))
|
||||
X = repmat(reshape(x,1,:), length(y), 1)
|
||||
Y = repmat(y, 1, length(x))
|
||||
X = repeat(reshape(x,1,:), length(y), 1)
|
||||
Y = repeat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p1 = contour(x, y, f, fill=true)
|
||||
p2 = contour(x, y, Z)
|
||||
@@ -238,11 +293,11 @@ PlotExample("3D",
|
||||
"",
|
||||
[:(begin
|
||||
n = 100
|
||||
ts = linspace(0,8π,n)
|
||||
ts = range(0, stop=8π, length=n)
|
||||
x = ts .* map(cos,ts)
|
||||
y = 0.1ts .* map(sin,ts)
|
||||
z = 1:n
|
||||
plot(x, y, z, zcolor=reverse(z), m=(10,0.8,:blues,stroke(0)), leg=false, cbar=true, w=5)
|
||||
plot(x, y, z, zcolor=reverse(z), m=(10,0.8,:blues,Plots.stroke(0)), leg=false, cbar=true, w=5)
|
||||
plot!(zeros(n),zeros(n),1:n, w=10)
|
||||
end)]
|
||||
),
|
||||
@@ -252,7 +307,7 @@ PlotExample("DataFrames",
|
||||
[:(begin
|
||||
import RDatasets
|
||||
iris = RDatasets.dataset("datasets", "iris")
|
||||
scatter(iris, :SepalLength, :SepalWidth, group=:Species,
|
||||
@df iris scatter(:SepalLength, :SepalWidth, group=:Species,
|
||||
title = "My awesome plot", xlabel = "Length", ylabel = "Width",
|
||||
marker = (0.5, [:cross :hex :star7], 12), bg=RGB(.2,.2,.2))
|
||||
end)]
|
||||
@@ -262,14 +317,15 @@ PlotExample("Groups and Subplots",
|
||||
"",
|
||||
[:(begin
|
||||
group = rand(map(i->"group $i",1:4),100)
|
||||
plot(rand(100), layout=@layout([a b;c]), group=group, linetype=[:bar :scatter :steppre])
|
||||
plot(rand(100), layout=@layout([a b;c]), group=group,
|
||||
linetype=[:bar :scatter :steppre], linecolor = :match)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Polar Plots",
|
||||
"",
|
||||
[:(begin
|
||||
Θ = linspace(0,1.5π,100)
|
||||
Θ = range(0, stop=1.5π, length=100)
|
||||
r = abs.(0.1randn(100)+sin.(3Θ))
|
||||
plot(Θ, r, proj=:polar, m=2)
|
||||
end)]
|
||||
@@ -288,6 +344,7 @@ PlotExample("Heatmap, categorical axes, and aspect_ratio",
|
||||
PlotExample("Layouts, margins, label rotation, title location",
|
||||
"",
|
||||
[:(begin
|
||||
using Plots.PlotMeasures # for Measures, e.g. mm and px
|
||||
plot(rand(100,6),layout=@layout([a b; c]),title=["A" "B" "C"],
|
||||
title_location=:left, left_margin=[20mm 0mm],
|
||||
bottom_margin=10px, xrotation=60)
|
||||
@@ -299,8 +356,8 @@ PlotExample("Boxplot and Violin series recipes",
|
||||
[:(begin
|
||||
import RDatasets
|
||||
singers = RDatasets.dataset("lattice", "singer")
|
||||
violin(singers, :VoicePart, :Height, line = 0, fill = (0.2, :blue))
|
||||
boxplot!(singers, :VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
|
||||
@df singers violin(:VoicePart, :Height, line = 0, fill = (0.2, :blue))
|
||||
@df singers boxplot!(:VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
|
||||
end)]
|
||||
),
|
||||
|
||||
@@ -313,7 +370,7 @@ PlotExample("Animation with subplots",
|
||||
plot(log,1,xlims=(1,10π),ylims=(0,5),leg=false),layout=l)
|
||||
|
||||
anim = Animation()
|
||||
for x = linspace(1,10π,100)
|
||||
for x = range(1, stop=10π, length=100)
|
||||
plot(push!(p,x,Float64[sin(x),cos(x),atan(x),cos(x),log(x)]))
|
||||
frame(anim)
|
||||
end
|
||||
@@ -321,16 +378,23 @@ PlotExample("Animation with subplots",
|
||||
),
|
||||
|
||||
PlotExample("Spy",
|
||||
"For a matrix `mat` with unique nonzeros `spy(mat)` returns a colorless plot. If `mat` has various different nonzero values, a colorbar is added. The colorbar can be disabled with `legend = nothing`. As always, the marker shape and size can be changed with `spy(mat, markersize = 3, markershape = :star)`.",
|
||||
"""
|
||||
For a matrix `mat` with unique nonzeros `spy(mat)` returns a colorless plot. If `mat` has
|
||||
various different nonzero values, a colorbar is added. The colorbar can be disabled with
|
||||
`legend = nothing`.
|
||||
""",
|
||||
[:(begin
|
||||
a = spdiagm((ones(50), ones(49), ones(49), ones(40), ones(40)),(0, 1, -1, 10, -10))
|
||||
b = spdiagm((1:50, 1:49, 1:49, 1:40, 1:40),(0, 1, -1, 10, -10))
|
||||
plot(spy(a, markershape = :dtriangle), spy(b), markersize = 3, title = ["Unique nonzeros" "Different nonzeros"])
|
||||
using SparseArrays
|
||||
a = spdiagm(0 => ones(50), 1 => ones(49), -1 => ones(49), 10 => ones(40), -10 => ones(40))
|
||||
b = spdiagm(0 => 1:50, 1 => 1:49, -1 => 1:49, 10 => 1:40, -10 => 1:40)
|
||||
plot(spy(a), spy(b), title = ["Unique nonzeros" "Different nonzeros"])
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Magic grid argument",
|
||||
"The grid lines can be modified individually for each axis with the magic `grid` argument.",
|
||||
"""
|
||||
The grid lines can be modified individually for each axis with the magic `grid` argument.
|
||||
""",
|
||||
[:(begin
|
||||
x = rand(10)
|
||||
p1 = plot(x, title = "Default looks")
|
||||
@@ -342,10 +406,32 @@ PlotExample("Magic grid argument",
|
||||
),
|
||||
|
||||
PlotExample("Framestyle",
|
||||
"The style of the frame/axes of a (sub)plot can be changed with the `framestyle` attribute. The default framestyle is `:axes`.",
|
||||
"""
|
||||
The style of the frame/axes of a (sub)plot can be changed with the `framestyle`
|
||||
attribute. The default framestyle is `:axes`.
|
||||
""",
|
||||
[:(begin
|
||||
histogram(fill(randn(1000), 5), framestyle = [:box :semi :axes :grid :none],
|
||||
title = [":box" ":semi" ":axes" ":grid" ":none"], color = RowVector(1:5), layout = 5, label = "")
|
||||
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)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Lines and markers with varying colors",
|
||||
"""
|
||||
You can use the `line_z` and `marker_z` properties to associate a color with
|
||||
each line segment or marker in the plot.
|
||||
""",
|
||||
[:(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)->x+y, color=:bluesreds, legend=false)
|
||||
plot(p1, p2)
|
||||
end)]
|
||||
),
|
||||
|
||||
@@ -356,7 +442,7 @@ PlotExample("Framestyle",
|
||||
# make and display one plot
|
||||
function test_examples(pkgname::Symbol, idx::Int; debug = false, disp = true)
|
||||
Plots._debugMode.on = debug
|
||||
info("Testing plot: $pkgname:$idx:$(_examples[idx].header)")
|
||||
@info("Testing plot: $pkgname:$idx:$(_examples[idx].header)")
|
||||
backend(pkgname)
|
||||
backend()
|
||||
map(eval, _examples[idx].exprs)
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
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
|
||||
end
|
||||
pushdisplay(PlotsDisplay())
|
||||
|
||||
atreplinit(i -> begin
|
||||
if PlotDisplay() in Base.Multimedia.displays
|
||||
popdisplay(PlotsDisplay())
|
||||
end
|
||||
pushdisplay(PlotsDisplay())
|
||||
end)
|
||||
|
||||
include(joinpath(@__DIR__, "backends", "plotly.jl"))
|
||||
include(joinpath(@__DIR__, "backends", "gr.jl"))
|
||||
include(joinpath(@__DIR__, "backends", "web.jl"))
|
||||
|
||||
@require GLVisualize = "4086de5b-f4b6-55f3-abb0-b8c73827585f" include(joinpath(@__DIR__, "backends", "glvisualize.jl"))
|
||||
@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"))
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@require IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" begin
|
||||
if IJulia.inited
|
||||
|
||||
"""
|
||||
Add extra jupyter mimetypes to display_dict based on the plot backed.
|
||||
|
||||
The default is nothing, except for plotly based backends, where it
|
||||
adds data for `application/vnd.plotly.v1+json` that is used in
|
||||
frontends like jupyterlab and nteract.
|
||||
"""
|
||||
_extra_mime_info!(plt::Plot, out::Dict) = out
|
||||
function _extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = JSON.lower(plt.o)
|
||||
out
|
||||
end
|
||||
|
||||
function _extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
|
||||
out["application/vnd.plotly.v1+json"] = Dict(
|
||||
:data => plotly_series(plt),
|
||||
:layout => plotly_layout(plt)
|
||||
)
|
||||
out
|
||||
end
|
||||
|
||||
function IJulia.display_dict(plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
out = Dict()
|
||||
if output_type == :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"
|
||||
end
|
||||
end
|
||||
end
|
||||
+20
-24
@@ -1,23 +1,19 @@
|
||||
|
||||
# NOTE: (0,0) is the top-left !!!
|
||||
|
||||
# allow pixels and percentages
|
||||
const px = AbsoluteLength(0.254)
|
||||
const pct = Length{:pct, Float64}(1.0)
|
||||
|
||||
to_pixels(m::AbsoluteLength) = m.value / 0.254
|
||||
|
||||
const _cbar_width = 5mm
|
||||
|
||||
Base.broadcast(::typeof(Base.:.*), m::Measure, n::Number) = m * n
|
||||
Base.broadcast(::typeof(Base.:.*), m::Number, n::Measure) = m * n
|
||||
#Base.broadcast(::typeof(Base.:.*), m::Measure, n::Number) = m * n
|
||||
#Base.broadcast(::typeof(Base.:.*), m::Number, n::Measure) = m * n
|
||||
Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
|
||||
Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
|
||||
Base.zero(::Type{typeof(mm)}) = 0mm
|
||||
Base.one(::Type{typeof(mm)}) = 1mm
|
||||
Base.typemin(::typeof(mm)) = -Inf*mm
|
||||
Base.typemax(::typeof(mm)) = Inf*mm
|
||||
Base.convert{F<:AbstractFloat}(::Type{F}, l::AbsoluteLength) = convert(F, l.value)
|
||||
Base.convert(::Type{F}, l::AbsoluteLength) where {F<:AbstractFloat} = convert(F, l.value)
|
||||
|
||||
# TODO: these are unintuitive and may cause tricky bugs
|
||||
# Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
|
||||
@@ -99,7 +95,7 @@ end
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# points combined by x/y, pct, and length
|
||||
type MixedMeasures
|
||||
mutable struct MixedMeasures
|
||||
xy::Float64
|
||||
pct::Float64
|
||||
len::AbsoluteLength
|
||||
@@ -151,7 +147,7 @@ function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...)
|
||||
elseif oarg in (:top, :bottom, :vcenter)
|
||||
origver = oarg
|
||||
else
|
||||
warn("Unused origin arg in bbox construction: $oarg")
|
||||
@warn("Unused origin arg in bbox construction: $oarg")
|
||||
end
|
||||
end
|
||||
bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver)
|
||||
@@ -220,7 +216,7 @@ bottompad(layout::AbstractLayout) = 0mm
|
||||
# RootLayout
|
||||
|
||||
# this is the parent of the top-level layout
|
||||
immutable RootLayout <: AbstractLayout end
|
||||
struct RootLayout <: AbstractLayout end
|
||||
|
||||
Base.parent(::RootLayout) = nothing
|
||||
parent_bbox(::RootLayout) = defaultbox
|
||||
@@ -230,7 +226,7 @@ bbox(::RootLayout) = defaultbox
|
||||
# EmptyLayout
|
||||
|
||||
# contains blank space
|
||||
type EmptyLayout <: AbstractLayout
|
||||
mutable struct EmptyLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
bbox::BoundingBox
|
||||
attr::KW # store label, width, and height for initialization
|
||||
@@ -248,7 +244,7 @@ _update_min_padding!(layout::EmptyLayout) = nothing
|
||||
# GridLayout
|
||||
|
||||
# nested, gridded layout with optional size percentages
|
||||
type GridLayout <: AbstractLayout
|
||||
mutable struct GridLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox
|
||||
@@ -272,7 +268,7 @@ function GridLayout(dims...;
|
||||
widths = zeros(dims[2]),
|
||||
heights = zeros(dims[1]),
|
||||
kw...)
|
||||
grid = Matrix{AbstractLayout}(dims...)
|
||||
grid = Matrix{AbstractLayout}(undef, dims...)
|
||||
layout = GridLayout(
|
||||
parent,
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
@@ -361,10 +357,10 @@ function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0
|
||||
# get the max horizontal (left and right) padding over columns,
|
||||
# and max vertical (bottom and top) padding over rows
|
||||
# TODO: add extra padding here
|
||||
pad_left = maximum(minpad_left, 1)
|
||||
pad_top = maximum(minpad_top, 2)
|
||||
pad_right = maximum(minpad_right, 1)
|
||||
pad_bottom = maximum(minpad_bottom, 2)
|
||||
pad_left = maximum(minpad_left, dims = 1)
|
||||
pad_top = maximum(minpad_top, dims = 2)
|
||||
pad_right = maximum(minpad_right, dims = 1)
|
||||
pad_bottom = maximum(minpad_bottom, dims = 2)
|
||||
|
||||
# make sure the perimeter match the parent
|
||||
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
|
||||
@@ -468,12 +464,12 @@ end
|
||||
# constructors
|
||||
|
||||
# pass the layout arg through
|
||||
function layout_args(d::KW)
|
||||
layout_args(get(d, :layout, default(:layout)))
|
||||
function layout_args(plotattributes::KW)
|
||||
layout_args(get(plotattributes, :layout, default(:layout)))
|
||||
end
|
||||
|
||||
function layout_args(d::KW, n_override::Integer)
|
||||
layout, n = layout_args(get(d, :layout, n_override))
|
||||
function layout_args(plotattributes::KW, n_override::Integer)
|
||||
layout, n = layout_args(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
|
||||
@@ -485,12 +481,12 @@ function layout_args(n::Integer)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args{I<:Integer}(sztup::NTuple{2,I})
|
||||
function layout_args(sztup::NTuple{2,I}) where I<:Integer
|
||||
nr, nc = sztup
|
||||
GridLayout(nr, nc), nr*nc
|
||||
end
|
||||
|
||||
function layout_args{I<:Integer}(sztup::NTuple{3,I})
|
||||
function layout_args(sztup::NTuple{3,I}) where I<:Integer
|
||||
n, nr, nc = sztup
|
||||
nr, nc = compute_gridsize(n, nr, nc)
|
||||
GridLayout(nr, nc), n
|
||||
@@ -570,7 +566,7 @@ function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
|
||||
for r=1:nr, c=1:nc
|
||||
l = layout[r,c]
|
||||
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
|
||||
plt = shift!(plts) # grab the first plot out of the list
|
||||
plt = popfirst!(plts) # grab the first plot out of the list
|
||||
layout[r,c] = plt.layout
|
||||
append!(subplots, plt.subplots)
|
||||
merge!(spmap, plt.spmap)
|
||||
|
||||
+43
-101
@@ -39,7 +39,7 @@ ps(fn::AbstractString) = ps(current(), fn)
|
||||
function eps(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "eps")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("image/eps"), plt)
|
||||
show(io, MIME("image/eps"), plt)
|
||||
close(io)
|
||||
end
|
||||
eps(fn::AbstractString) = eps(current(), fn)
|
||||
@@ -145,10 +145,6 @@ function Base.display(::PlotsDisplay, plt::Plot)
|
||||
_display(plt)
|
||||
end
|
||||
|
||||
# override the REPL display to open a gui window
|
||||
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
|
||||
|
||||
|
||||
_do_plot_show(plt, showval::Bool) = showval && gui(plt)
|
||||
function _do_plot_show(plt, showval::Symbol)
|
||||
showval == :gui && gui(plt)
|
||||
@@ -157,58 +153,59 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _mimeformats = Dict(
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg",
|
||||
"text/plain" => "txt",
|
||||
"application/x-tex" => "tex",
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
:pyplot => :png,
|
||||
:unicodeplots => :txt,
|
||||
:glvisualize => :png
|
||||
:glvisualize => :png,
|
||||
:plotlyjs => :html,
|
||||
:plotly => :html
|
||||
)
|
||||
|
||||
# a backup for html... passes to svg or png depending on the html_output_format arg
|
||||
function Base.show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
function _show(io::IO, ::MIME"text/html", plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
if output_type == :png
|
||||
# info("writing png to html output")
|
||||
# @info("writing png to html output")
|
||||
print(io, "<img src=\"data:image/png;base64,", base64encode(show, MIME("image/png"), plt), "\" />")
|
||||
elseif output_type == :svg
|
||||
# info("writing svg to html output")
|
||||
# @info("writing svg to html output")
|
||||
show(io, MIME("image/svg+xml"), plt)
|
||||
elseif output_type == :txt
|
||||
show(io, MIME("text/plain"), plt)
|
||||
else
|
||||
error("only png or svg allowed. got: $output_type")
|
||||
error("only png or svg allowed. got: $(repr(output_type))")
|
||||
end
|
||||
end
|
||||
|
||||
function _show{B}(io::IO, m, plt::Plot{B})
|
||||
# Base.show_backtrace(STDOUT, backtrace())
|
||||
warn("_show is not defined for this backend. m=", string(m))
|
||||
# delegate showable to _show instead
|
||||
function Base.showable(m::M, plt::P) where {M<:MIME, P<:Plot}
|
||||
return hasmethod(_show, Tuple{IO, M, P})
|
||||
end
|
||||
|
||||
function _display(plt::Plot)
|
||||
warn("_display is not defined for this backend.")
|
||||
@warn("_display is not defined for this backend.")
|
||||
end
|
||||
|
||||
# for writing to io streams... first prepare, then callback
|
||||
for mime in keys(_mimeformats)
|
||||
@eval function Base.show{B}(io::IO, m::MIME{Symbol($mime)}, plt::Plot{B})
|
||||
prepare_output(plt)
|
||||
_show(io, m, plt)
|
||||
for mime in ("text/plain", "text/html", "image/png", "image/eps", "image/svg+xml",
|
||||
"application/eps", "application/pdf", "application/postscript",
|
||||
"application/x-tex")
|
||||
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
||||
if haskey(io, :juno_plotsize)
|
||||
showjuno(io, m, plt)
|
||||
else
|
||||
prepare_output(plt)
|
||||
_show(io, m, plt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# default text/plain for all backends
|
||||
_show(io::IO, ::MIME{Symbol("text/plain")}, plt::Plot) = show(io, plt)
|
||||
|
||||
"Close all open gui windows of the current backend"
|
||||
closeall() = closeall(backend())
|
||||
|
||||
@@ -216,9 +213,10 @@ closeall() = closeall(backend())
|
||||
# ---------------------------------------------------------
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
|
||||
const PDFBackends = Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
|
||||
if is_installed("FileIO")
|
||||
@eval import FileIO
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot)
|
||||
function _show(io::IO, ::MIME"image/png", plt::Plot{<:PDFBackends})
|
||||
fn = tempname()
|
||||
|
||||
# first save a pdf file
|
||||
@@ -232,7 +230,7 @@ if is_installed("FileIO")
|
||||
FileIO.save(pngfn, s)
|
||||
|
||||
# now write from the file
|
||||
write(io, readstring(open(pngfn)))
|
||||
write(io, read(open(pngfn), String))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -252,82 +250,26 @@ end
|
||||
#
|
||||
# html_output_format("svg")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _ijulia_output = String["text/html"]
|
||||
|
||||
using Requires
|
||||
@require IJulia begin
|
||||
if IJulia.inited
|
||||
export set_ijulia_output
|
||||
|
||||
function set_ijulia_output(mimestr::AbstractString)
|
||||
# info("Setting IJulia output format to $mimestr")
|
||||
global _ijulia_output
|
||||
_ijulia_output[1] = mimestr
|
||||
end
|
||||
function IJulia.display_dict(plt::Plot)
|
||||
global _ijulia_output
|
||||
Dict{String, String}(_ijulia_output[1] => sprint(show, _ijulia_output[1], plt))
|
||||
end
|
||||
|
||||
# default text/plain passes to html... handles Interact issues
|
||||
function Base.show(io::IO, m::MIME"text/plain", plt::Plot)
|
||||
show(io, MIME("text/html"), plt)
|
||||
end
|
||||
|
||||
ENV["MPLBACKEND"] = "Agg"
|
||||
set_ijulia_output("text/html")
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Atom PlotPane
|
||||
# ---------------------------------------------------------
|
||||
@require Juno begin
|
||||
import Hiccup, Media
|
||||
function showjuno(io::IO, m, plt)
|
||||
sz = plt[:size]
|
||||
dpi = plt[:dpi]
|
||||
thickness_scaling = plt[:thickness_scaling]
|
||||
|
||||
if Juno.isactive()
|
||||
Media.media(Plot, Media.Plot)
|
||||
jsize = get(io, :juno_plotsize, [400, 500])
|
||||
|
||||
scale = minimum(jsize[i] / sz[i] for i in 1:2)
|
||||
plt[:size] = (s * scale for s in sz)
|
||||
plt[:dpi] = Plots.DPI
|
||||
plt[:thickness_scaling] *= scale
|
||||
|
||||
_show{B}(io::IO, m::MIME"text/plain", plt::Plot{B}) = print(io, "Plot{$B}()")
|
||||
prepare_output(plt)
|
||||
_show(io, m, plt)
|
||||
|
||||
function Juno.render(e::Juno.Editor, plt::Plot)
|
||||
Juno.render(e, nothing)
|
||||
end
|
||||
|
||||
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot)
|
||||
# temporarily overwrite size to be Atom.plotsize
|
||||
sz = plt[:size]
|
||||
jsize = Juno.plotsize()
|
||||
jsize[1] == 0 && (jsize[1] = 400)
|
||||
jsize[2] == 0 && (jsize[2] = 500)
|
||||
|
||||
plt[:size] = jsize
|
||||
Juno.render(pane, HTML(stringmime(MIME("text/html"), plt)))
|
||||
plt[:size] = sz
|
||||
end
|
||||
# special handling for PlotlyJS
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
end
|
||||
else
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot)
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
s = "PlotPane turned off. Unset ENV[\"PLOTS_USE_ATOM_PLOTPANE\"] and restart Julia to enable it."
|
||||
Juno.render(pane, HTML(s))
|
||||
end
|
||||
end
|
||||
|
||||
# special handling for plotly... use PlotsDisplay
|
||||
function Juno.render(pane::Juno.PlotPane, plt::Plot{PlotlyBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
s = "PlotPane turned off. The plotly backend cannot render in the PlotPane due to javascript issues. Plotlyjs is similar to plotly and is compatible with the plot pane."
|
||||
Juno.render(pane, HTML(s))
|
||||
end
|
||||
end
|
||||
plt[:size] = sz
|
||||
plt[:dpi] = dpi
|
||||
plt[:thickness_scaling] = thickness_scaling
|
||||
end
|
||||
|
||||
+81
-74
@@ -7,42 +7,44 @@ function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
|
||||
end
|
||||
|
||||
function _expand_seriestype_array(d::KW, args)
|
||||
sts = get(d, :seriestype, :path)
|
||||
function _expand_seriestype_array(plotattributes::KW, args)
|
||||
sts = get(plotattributes, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(d, :seriestype)
|
||||
RecipeData[begin
|
||||
dc = copy(d)
|
||||
delete!(plotattributes, :seriestype)
|
||||
rd = Vector{RecipeData}(undef, size(sts, 1))
|
||||
for r in 1:size(sts, 1)
|
||||
dc = copy(plotattributes)
|
||||
dc[:seriestype] = sts[r:r,:]
|
||||
RecipeData(dc, args)
|
||||
end for r=1:size(sts,1)]
|
||||
rd[r] = RecipeData(dc, args)
|
||||
end
|
||||
rd
|
||||
else
|
||||
RecipeData[RecipeData(copy(d), args)]
|
||||
RecipeData[RecipeData(copy(plotattributes), args)]
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(d::KW, args, still_to_process::Vector{RecipeData})
|
||||
function _preprocess_args(plotattributes::KW, args, still_to_process::Vector{RecipeData})
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
if haskey(d, :group)
|
||||
args = (extractGroupArgs(d[:group], args...), args...)
|
||||
if haskey(plotattributes, :group)
|
||||
args = (extractGroupArgs(plotattributes[:group], args...), args...)
|
||||
end
|
||||
|
||||
# if we were passed a vector/matrix of seriestypes and there's more than one row,
|
||||
# we want to duplicate the inputs, once for each seriestype row.
|
||||
if !isempty(args)
|
||||
append!(still_to_process, _expand_seriestype_array(d, args))
|
||||
append!(still_to_process, _expand_seriestype_array(plotattributes, args))
|
||||
end
|
||||
|
||||
# remove subplot and axis args from d... they will be passed through in the kw_list
|
||||
# remove subplot and axis args from plotattributes... they will be passed through in the kw_list
|
||||
if !isempty(args)
|
||||
for (k,v) in d
|
||||
for (k,v) in plotattributes
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
delete!(d, k)
|
||||
delete!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -55,9 +57,9 @@ end
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
function _process_userrecipes(plt::Plot, plotattributes::KW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(d, args, still_to_process)
|
||||
args = _preprocess_args(plotattributes, args, still_to_process)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a stack of series that still need to be processed.
|
||||
@@ -70,7 +72,7 @@ function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
# grab the first in line to be processed and either add it to the kw_list or
|
||||
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes)
|
||||
# for further processing.
|
||||
next_series = shift!(still_to_process)
|
||||
next_series = popfirst!(still_to_process)
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(next_series) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
|
||||
@@ -78,20 +80,20 @@ function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
if isempty(next_series.args)
|
||||
_process_userrecipe(plt, kw_list, next_series)
|
||||
else
|
||||
rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
|
||||
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...)
|
||||
prepend!(still_to_process,rd_list)
|
||||
end
|
||||
end
|
||||
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
plotattributes[:smooth] = false
|
||||
kw_list
|
||||
end
|
||||
|
||||
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
kw = recipedata.d
|
||||
kw = recipedata.plotattributes
|
||||
preprocessArgs!(kw)
|
||||
_preprocess_userrecipe(kw)
|
||||
warnOnUnsupported_scales(plt.backend, kw)
|
||||
@@ -115,12 +117,12 @@ function _preprocess_userrecipe(kw::KW)
|
||||
# map marker_z if it's a Function
|
||||
if isa(get(kw, :marker_z, nothing), Function)
|
||||
# TODO: should this take y and/or z as arguments?
|
||||
kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) : map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# map line_z if it's a Function
|
||||
if isa(get(kw, :line_z, nothing), Function)
|
||||
kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) : map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# convert a ribbon into a fillrange
|
||||
@@ -151,7 +153,7 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
x, y = kw[:x], kw[:y]
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
sx = [ignorenan_minimum(x), ignorenan_maximum(x)]
|
||||
sy = β * sx + α
|
||||
sy = β .* sx .+ α
|
||||
push!(kw_list, merge(copy(kw), KW(
|
||||
:seriestype => :path,
|
||||
:x => sx,
|
||||
@@ -181,11 +183,11 @@ function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_pr
|
||||
st = kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
||||
for data in datalist
|
||||
preprocessArgs!(data.d)
|
||||
if data.d[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.d)")
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
|
||||
end
|
||||
push!(still_to_process, data.d)
|
||||
push!(still_to_process, data.plotattributes)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
@@ -201,14 +203,14 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
function _plot_setup(plt::Plot, plotattributes::KW, kw_list::Vector{KW})
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (d[k] = pop!(kw, k))
|
||||
haskey(_plot_defaults, k) && (plotattributes[k] = pop!(kw, k))
|
||||
end
|
||||
|
||||
# TODO: init subplots here
|
||||
_update_plot_args(plt, d)
|
||||
_update_plot_args(plt, plotattributes)
|
||||
if !plt.init
|
||||
plt.o = Base.invokelatest(_create_backend_figure, plt)
|
||||
|
||||
@@ -250,7 +252,7 @@ function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
plt[:inset_subplots] = nothing
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
function _subplot_setup(plt::Plot, plotattributes::KW, 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.
|
||||
@@ -264,7 +266,7 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
|
||||
# extract subplot/axis attributes from kw and add to sp_attr
|
||||
attr = KW()
|
||||
for (k,v) in kw
|
||||
for (k,v) in collect(kw)
|
||||
if haskey(_subplot_defaults, k) || haskey(_axis_defaults_byletter, k)
|
||||
attr[k] = pop!(kw, k)
|
||||
end
|
||||
@@ -287,8 +289,8 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
|
||||
# override subplot/axis args. `sp_attrs` take precendence
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
attr = if !haskey(d, :subplot) || d[:subplot] == idx
|
||||
merge(d, get(sp_attrs, sp, KW()))
|
||||
attr = if !haskey(plotattributes, :subplot) || plotattributes[:subplot] == idx
|
||||
merge(plotattributes, get(sp_attrs, sp, KW()))
|
||||
else
|
||||
get(sp_attrs, sp, KW())
|
||||
end
|
||||
@@ -301,13 +303,13 @@ end
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
function _prepare_subplot{T}(plt::Plot{T}, d::KW)
|
||||
st::Symbol = d[:seriestype]
|
||||
sp::Subplot{T} = d[:subplot]
|
||||
function _prepare_subplot(plt::Plot{T}, plotattributes::KW) where T
|
||||
st::Symbol = plotattributes[:seriestype]
|
||||
sp::Subplot{T} = plotattributes[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
_update_subplot_args(plt, sp, d, sp_idx, true)
|
||||
_update_subplot_args(plt, sp, plotattributes, sp_idx, true)
|
||||
|
||||
st = _override_seriestype_check(d, st)
|
||||
st = _override_seriestype_check(plotattributes, st)
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if is3d(st)
|
||||
@@ -325,28 +327,28 @@ end
|
||||
# ------------------------------------------------------------------
|
||||
# series types
|
||||
|
||||
function _override_seriestype_check(d::KW, st::Symbol)
|
||||
function _override_seriestype_check(plotattributes::KW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st)
|
||||
z = d[:z]
|
||||
if !isa(z, Void) && (size(d[:x]) == size(d[:y]) == size(z))
|
||||
if !is3d(st) && !(st in (:contour,:contour3d))
|
||||
z = plotattributes[:z]
|
||||
if !isa(z, Nothing) && (size(plotattributes[:x]) == size(plotattributes[:y]) == size(z))
|
||||
st = (st == :scatter ? :scatter3d : :path3d)
|
||||
d[:seriestype] = st
|
||||
plotattributes[:seriestype] = st
|
||||
end
|
||||
end
|
||||
st
|
||||
end
|
||||
|
||||
function _prepare_annotations(sp::Subplot, d::KW)
|
||||
function _prepare_annotations(sp::Subplot, plotattributes::KW)
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
# series_anns = annotations(pop!(d, :series_annotations, []))
|
||||
# series_anns = annotations(pop!(plotattributes, :series_annotations, []))
|
||||
# if isa(series_anns, SeriesAnnotations)
|
||||
# series_anns.x = d[:x]
|
||||
# series_anns.y = d[:y]
|
||||
# series_anns.x = plotattributes[:x]
|
||||
# series_anns.y = plotattributes[:y]
|
||||
# elseif length(series_anns) > 0
|
||||
# x, y = d[:x], d[:y]
|
||||
# x, y = plotattributes[:x], plotattributes[:y]
|
||||
# nx, ny, na = map(length, (x,y,series_anns))
|
||||
# n = max(nx, ny, na)
|
||||
# series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n]
|
||||
@@ -354,22 +356,26 @@ function _prepare_annotations(sp::Subplot, d::KW)
|
||||
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
|
||||
end
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
function _expand_subplot_extrema(sp::Subplot, plotattributes::KW, st::Symbol)
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
w, h = size(d[:z])
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y])
|
||||
expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
expand_extrema!(sp, plotattributes)
|
||||
end
|
||||
# expand for zerolines (axes through origin)
|
||||
if sp[:framestyle] in (:origin, :zerolines)
|
||||
expand_extrema!(sp[:xaxis], 0.0)
|
||||
expand_extrema!(sp[:yaxis], 0.0)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_the_series(plt, sp, d)
|
||||
warnOnUnsupported_args(plt.backend, d)
|
||||
warnOnUnsupported(plt.backend, d)
|
||||
series = Series(d)
|
||||
function _add_the_series(plt, sp, plotattributes)
|
||||
warnOnUnsupported_args(plt.backend, plotattributes)
|
||||
warnOnUnsupported(plt.backend, plotattributes)
|
||||
series = Series(plotattributes)
|
||||
push!(plt.series_list, series)
|
||||
push!(sp.series_list, series)
|
||||
_series_added(plt, series)
|
||||
@@ -379,37 +385,38 @@ end
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _process_seriesrecipe(plt::Plot, d::KW)
|
||||
function _process_seriesrecipe(plt::Plot, plotattributes::KW)
|
||||
# replace seriestype aliases
|
||||
st = Symbol(d[:seriestype])
|
||||
st = d[:seriestype] = get(_typeAliases, st, st)
|
||||
st = Symbol(plotattributes[:seriestype])
|
||||
st = plotattributes[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# shapes shouldn't have fillrange set
|
||||
if d[:seriestype] == :shape
|
||||
d[:fillrange] = nothing
|
||||
if plotattributes[:seriestype] == :shape
|
||||
plotattributes[:fillrange] = nothing
|
||||
end
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if is_seriestype_supported(st)
|
||||
sp = _prepare_subplot(plt, d)
|
||||
_prepare_annotations(sp, d)
|
||||
_expand_subplot_extrema(sp, d, st)
|
||||
_add_the_series(plt, sp, d)
|
||||
sp = _prepare_subplot(plt, plotattributes)
|
||||
_prepare_annotations(sp, plotattributes)
|
||||
_expand_subplot_extrema(sp, plotattributes, st)
|
||||
_update_series_attributes!(plotattributes, plt, sp)
|
||||
_add_the_series(plt, sp, plotattributes)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z])
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
preprocessArgs!(data.d)
|
||||
if data.d[:seriestype] == st
|
||||
preprocessArgs!(data.plotattributes)
|
||||
if data.plotattributes[:seriestype] == st
|
||||
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
|
||||
end
|
||||
_process_seriesrecipe(plt, data.d)
|
||||
_process_seriesrecipe(plt, data.plotattributes)
|
||||
else
|
||||
warn("Unhandled recipe: $(data)")
|
||||
@warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
+50
-47
@@ -1,10 +1,10 @@
|
||||
|
||||
type CurrentPlot
|
||||
nullableplot::Nullable{AbstractPlot}
|
||||
mutable struct CurrentPlot
|
||||
nullableplot::Union{AbstractPlot, Nothing}
|
||||
end
|
||||
const CURRENT_PLOT = CurrentPlot(Nullable{AbstractPlot}())
|
||||
const CURRENT_PLOT = CurrentPlot(nothing)
|
||||
|
||||
isplotnull() = isnull(CURRENT_PLOT.nullableplot)
|
||||
isplotnull() = CURRENT_PLOT.nullableplot == nothing
|
||||
|
||||
"""
|
||||
current()
|
||||
@@ -14,9 +14,9 @@ function current()
|
||||
if isplotnull()
|
||||
error("No current plot/subplot")
|
||||
end
|
||||
get(CURRENT_PLOT.nullableplot)
|
||||
CURRENT_PLOT.nullableplot
|
||||
end
|
||||
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = Nullable(plot))
|
||||
current(plot::AbstractPlot) = (CURRENT_PLOT.nullableplot = plot)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -33,7 +33,7 @@ convertSeriesIndex(plt::Plot, n::Int) = n
|
||||
|
||||
|
||||
"""
|
||||
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
|
||||
|
||||
```
|
||||
plot(args...; kw...) # creates a new plot window, and sets it to be the current
|
||||
@@ -42,36 +42,37 @@ The main plot command. Use `plot` to create a new plot object, and `plot!` to a
|
||||
```
|
||||
|
||||
There are lots of ways to pass in data, and lots of keyword arguments... just try it and it will likely work as expected.
|
||||
When you pass in matrices, it splits by columns. See the documentation for more info.
|
||||
When you pass in matrices, it splits by columns. To see the list of available attributes, use the `plotattr([attr])`
|
||||
function, where `attr` is the symbol `:Series:`, `:Subplot:`, `:Plot` or `:Axis`. Pass any attribute to `plotattr`
|
||||
as a String to look up its docstring; e.g. `plotattr("seriestype")`.
|
||||
"""
|
||||
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
function plot(args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = d
|
||||
_plot!(plt, d, args)
|
||||
# plt.user_attr = plotattributes
|
||||
_plot!(plt, plotattributes, args)
|
||||
end
|
||||
|
||||
# build a new plot from existing plots
|
||||
# note: we split into plt1 and plts_tail so we can dispatch correctly
|
||||
function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
|
||||
# build our plot vector from the args
|
||||
n = length(plts_tail) + 1
|
||||
plts = Array{Plot}(n)
|
||||
plts = Array{Plot}(undef, n)
|
||||
plts[1] = plt1
|
||||
for (i,plt) in enumerate(plts_tail)
|
||||
plts[i+1] = plt
|
||||
end
|
||||
|
||||
# compute the layout
|
||||
layout = layout_args(d, n)[1]
|
||||
layout = layout_args(plotattributes, n)[1]
|
||||
num_sp = sum([length(p.subplots) for p in plts])
|
||||
|
||||
# create a new plot object, with subplot list/map made of existing subplots.
|
||||
@@ -82,26 +83,31 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
# TODO: build the user_attr dict by creating "Any matrices" for the args of each subplot
|
||||
|
||||
# TODO: replace this with proper processing from a merged user_attr KW
|
||||
# update plot args, first with existing plots, then override with d
|
||||
# update plot args, first with existing plots, then override with plotattributes
|
||||
for p in plts
|
||||
_update_plot_args(plt, p.attr)
|
||||
_update_plot_args(plt, copy(p.attr))
|
||||
plt.n += p.n
|
||||
end
|
||||
_update_plot_args(plt, d)
|
||||
_update_plot_args(plt, plotattributes)
|
||||
|
||||
# pass new plot to the backend
|
||||
plt.o = _create_backend_figure(plt)
|
||||
plt.init = true
|
||||
|
||||
series_attr = KW()
|
||||
for (k,v) in d
|
||||
for (k,v) in plotattributes
|
||||
if haskey(_series_defaults, k)
|
||||
series_attr[k] = pop!(d,k)
|
||||
series_attr[k] = pop!(plotattributes,k)
|
||||
end
|
||||
end
|
||||
|
||||
# create the layout and initialize the subplots
|
||||
# create the layout
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# initialize the subplots
|
||||
cmdidx = 1
|
||||
for (idx, sp) in enumerate(plt.subplots)
|
||||
_initialize_subplot(plt, sp)
|
||||
@@ -112,8 +118,8 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
sp.plt = plt
|
||||
sp.attr[:subplot_index] = idx
|
||||
for series in serieslist
|
||||
merge!(series.d, series_attr)
|
||||
_add_defaults!(series.d, plt, sp, cmdidx)
|
||||
merge!(series.plotattributes, series_attr)
|
||||
_add_defaults!(series.plotattributes, plt, sp, cmdidx)
|
||||
push!(plt.series_list, series)
|
||||
_series_added(plt, series)
|
||||
cmdidx += 1
|
||||
@@ -122,15 +128,12 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
|
||||
# first apply any args for the subplots
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
_update_subplot_args(plt, sp, d, idx, false)
|
||||
_update_subplot_args(plt, sp, plotattributes, idx, false)
|
||||
end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# finish up
|
||||
current(plt)
|
||||
_do_plot_show(plt, get(d, :show, default(:show)))
|
||||
_do_plot_show(plt, get(plotattributes, :show, default(:show)))
|
||||
plt
|
||||
end
|
||||
|
||||
@@ -149,10 +152,10 @@ end
|
||||
|
||||
# this adds to a specific plot... most plot commands will flow through here
|
||||
function plot!(plt::Plot, args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
# merge!(plt.user_attr, d)
|
||||
_plot!(plt, d, args)
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
# merge!(plt.user_attr, plotattributes)
|
||||
_plot!(plt, plotattributes, args)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
@@ -160,21 +163,21 @@ end
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
d[:plot_object] = plt
|
||||
function _plot!(plt::Plot, plotattributes::KW, args::Tuple)
|
||||
plotattributes[:plot_object] = plt
|
||||
|
||||
if !isempty(args) && !isdefined(Main, :StatPlots) &&
|
||||
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 StatPlots")
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
kw_list = _process_userrecipes(plt, d, args)
|
||||
kw_list = _process_userrecipes(plt, plotattributes, args)
|
||||
|
||||
# info(1)
|
||||
# @info(1)
|
||||
# map(DD, kw_list)
|
||||
|
||||
|
||||
@@ -189,18 +192,18 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
next_kw = shift!(still_to_process)
|
||||
next_kw = popfirst!(still_to_process)
|
||||
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
|
||||
end
|
||||
|
||||
# info(2)
|
||||
# @info(2)
|
||||
# map(DD, kw_list)
|
||||
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
_plot_setup(plt, d, kw_list)
|
||||
_subplot_setup(plt, d, kw_list)
|
||||
_plot_setup(plt, plotattributes, kw_list)
|
||||
_subplot_setup(plt, plotattributes, kw_list)
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
@@ -209,7 +212,7 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
# "SERIES RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# info(3)
|
||||
# @info(3)
|
||||
# map(DD, kw_list)
|
||||
|
||||
for kw in kw_list
|
||||
@@ -280,11 +283,11 @@ end
|
||||
|
||||
function plot(sp::Subplot, args...; kw...)
|
||||
plt = sp.plt
|
||||
plot(plt, args...; kw..., subplot = findfirst(plt.subplots, sp))
|
||||
plot(plt, args...; kw..., subplot = findfirst(isequal(sp), plt.subplots))
|
||||
end
|
||||
function plot!(sp::Subplot, args...; kw...)
|
||||
plt = sp.plt
|
||||
plot!(plt, args...; kw..., subplot = findfirst(plt.subplots, sp))
|
||||
plot!(plt, args...; kw..., subplot = findfirst(isequal(sp), plt.subplots))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
+9
-6
@@ -40,23 +40,26 @@ function plotattr(attribute::AbstractString)
|
||||
error("There is no attribute named $attribute")
|
||||
end
|
||||
|
||||
printnothing(x) = x
|
||||
printnothing(x::Nothing) = "nothing"
|
||||
|
||||
function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
|
||||
|
||||
attribute = Symbol(lookup_aliases(attrtype, attribute))
|
||||
|
||||
desc = get(_arg_desc, attribute, "")
|
||||
first_period_idx = findfirst(desc, '.')
|
||||
first_period_idx = findfirst(isequal('.'), desc)
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
als = keys(filter((_,v)->v==attribute, _keyAliases)) |> collect |> sort
|
||||
als = keys(filter(x->x[2]==attribute, _keyAliases)) |> collect |> sort
|
||||
als = join(map(string,als), ", ")
|
||||
def = _attribute_defaults[attrtype][attribute]
|
||||
|
||||
|
||||
# Looks up the different elements and plots them
|
||||
println("$attribute ", typedesc == "" ? "" : "{$typedesc}", "\n",
|
||||
als == "" ? "" : "$als\n",
|
||||
"\n$desc\n",
|
||||
"$(attrtype) attribute, ", def == "" ? "" : " default: $def")
|
||||
println("$(printnothing(attribute)) ", typedesc == "" ? "" : "{$(printnothing(typedesc))}", "\n",
|
||||
als == "" ? "" : "$(printnothing(als))\n",
|
||||
"\n$(printnothing(desc))\n",
|
||||
"$(printnothing(attrtype)) attribute, ", def == "" ? "" : " default: $(printnothing(def))")
|
||||
end
|
||||
|
||||
+229
-138
@@ -47,7 +47,7 @@ end
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x) = 1
|
||||
|
||||
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::AbstractPlot) = throw(MethodError("Unmatched plot recipe: $T"))
|
||||
RecipesBase.apply_recipe(plotattributes::KW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError("Unmatched plot recipe: $T"))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -79,55 +79,101 @@ function hvline_limits(axis::Axis)
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
xmin, xmax = hvline_limits(d[:subplot][:xaxis])
|
||||
n = length(y)
|
||||
newx = repmat(Float64[xmin, xmax, NaN], n)
|
||||
newx = repeat(Float64[-1, 1, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
seriestype := :straightline
|
||||
()
|
||||
end
|
||||
@deps hline path
|
||||
@deps hline straightline
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
ymin, ymax = hvline_limits(d[:subplot][:yaxis])
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = repmat(Float64[ymin, ymax, NaN], n)
|
||||
newy = repeat(Float64[-1, 1, NaN], n)
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
seriestype := :straightline
|
||||
()
|
||||
end
|
||||
@deps vline path
|
||||
@deps vline straightline
|
||||
|
||||
@recipe function f(::Type{Val{:hspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
newy = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :shape
|
||||
()
|
||||
end
|
||||
@deps hspan shape
|
||||
|
||||
@recipe function f(::Type{Val{:vspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :shape
|
||||
()
|
||||
end
|
||||
@deps vspan shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# path and scatter
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:scatterpath}}, x, y, z)
|
||||
x := x
|
||||
y := y
|
||||
seriestype := :scatter
|
||||
@series begin
|
||||
seriestype := :path
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
()
|
||||
end
|
||||
@deps scatterpath path scatter
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# steps
|
||||
|
||||
function make_steps(x, y, st)
|
||||
make_steps(x, st) = x
|
||||
function make_steps(x::AbstractArray, st)
|
||||
n = length(x)
|
||||
n == 0 && return zeros(0),zeros(0)
|
||||
newx, newy = zeros(2n-1), zeros(2n-1)
|
||||
for i=1:n
|
||||
idx = 2i-1
|
||||
n == 0 && return zeros(0)
|
||||
newx = zeros(2n - 1)
|
||||
for i in 1:n
|
||||
idx = 2i - 1
|
||||
newx[idx] = x[i]
|
||||
newy[idx] = y[i]
|
||||
if i > 1
|
||||
newx[idx-1] = x[st == :steppre ? i-1 : i]
|
||||
newy[idx-1] = y[st == :steppre ? i : i-1]
|
||||
newx[idx - 1] = x[st == :pre ? i : i - 1]
|
||||
end
|
||||
end
|
||||
newx, newy
|
||||
return newx
|
||||
end
|
||||
make_steps(t::Tuple, st) = Tuple(make_steps(ti, st) for ti in t)
|
||||
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:steppre}}, x, y, z)
|
||||
d[:x], d[:y] = make_steps(x, y, :steppre)
|
||||
plotattributes[:x] = make_steps(x, :post)
|
||||
plotattributes[:y] = make_steps(y, :pre)
|
||||
seriestype := :path
|
||||
|
||||
# handle fillrange
|
||||
plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :pre)
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
if plotattributes[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := x
|
||||
@@ -144,11 +190,15 @@ end
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:steppost}}, x, y, z)
|
||||
d[:x], d[:y] = make_steps(x, y, :steppost)
|
||||
plotattributes[:x] = make_steps(x, :pre)
|
||||
plotattributes[:y] = make_steps(y, :post)
|
||||
seriestype := :path
|
||||
|
||||
# handle fillrange
|
||||
plotattributes[:fillrange] = make_steps(plotattributes[:fillrange], :post)
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
if plotattributes[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := x
|
||||
@@ -170,9 +220,9 @@ end
|
||||
# create vertical line segments from fill
|
||||
@recipe function f(::Type{Val{:sticks}}, x, y, z)
|
||||
n = length(x)
|
||||
fr = d[:fillrange]
|
||||
fr = plotattributes[:fillrange]
|
||||
if fr == nothing
|
||||
yaxis = d[:subplot][:yaxis]
|
||||
yaxis = plotattributes[:subplot][:yaxis]
|
||||
fr = if yaxis[:scale] == :identity
|
||||
0.0
|
||||
else
|
||||
@@ -191,7 +241,7 @@ end
|
||||
seriestype := :path
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
if plotattributes[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := x
|
||||
@@ -224,17 +274,17 @@ end
|
||||
@recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30)
|
||||
args = z != nothing ? (x,y,z) : (x,y)
|
||||
newx, newy = zeros(0), zeros(0)
|
||||
fr = d[:fillrange]
|
||||
fr = plotattributes[:fillrange]
|
||||
newfr = fr != nothing ? zeros(0) : nothing
|
||||
newz = z != nothing ? zeros(0) : nothing
|
||||
# lz = d[:line_z]
|
||||
# lz = plotattributes[:line_z]
|
||||
# newlz = lz != nothing ? zeros(0) : nothing
|
||||
|
||||
# for each line segment (point series with no NaNs), convert it into a bezier curve
|
||||
# where the points are the control points of the curve
|
||||
for rng in iter_segments(args...)
|
||||
length(rng) < 2 && continue
|
||||
ts = linspace(0, 1, npoints)
|
||||
ts = range(0, stop = 1, length = npoints)
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
|
||||
if z != nothing
|
||||
@@ -263,9 +313,9 @@ end
|
||||
end
|
||||
# if lz != nothing
|
||||
# # line_z := newlz
|
||||
# linecolor := (isa(d[:linecolor], ColorGradient) ? d[:linecolor] : cgrad())
|
||||
# linecolor := (isa(plotattributes[:linecolor], ColorGradient) ? plotattributes[:linecolor] : cgrad())
|
||||
# end
|
||||
# Plots.DD(d)
|
||||
# Plots.DD(plotattributes)
|
||||
()
|
||||
end
|
||||
@deps curves path
|
||||
@@ -274,9 +324,9 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(d, x, y)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
||||
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||
procx = if nx == ny
|
||||
cv
|
||||
@@ -287,15 +337,19 @@ end
|
||||
end
|
||||
|
||||
# compute half-width of bars
|
||||
bw = d[:bar_width]
|
||||
bw = plotattributes[:bar_width]
|
||||
hw = if bw == nothing
|
||||
0.5ignorenan_mean(diff(procx))
|
||||
if nx > 1
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
else
|
||||
0.5 * _bar_width
|
||||
end
|
||||
else
|
||||
Float64[0.5_cycle(bw,i) for i=1:length(procx)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
fillto = d[:fillrange]
|
||||
fillto = plotattributes[:fillrange]
|
||||
if fillto == nothing
|
||||
fillto = 0
|
||||
end
|
||||
@@ -320,7 +374,7 @@ end
|
||||
expand_extrema!(axis, widen(ignorenan_extrema(xseg.pts)...))
|
||||
|
||||
# switch back
|
||||
if !isvertical(d)
|
||||
if !isvertical(plotattributes)
|
||||
xseg, yseg = yseg, xseg
|
||||
end
|
||||
|
||||
@@ -335,6 +389,34 @@ end
|
||||
end
|
||||
@deps bar shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plots Heatmap
|
||||
@recipe function f(::Type{Val{:plots_heatmap}}, x, y, z)
|
||||
xe, ye = heatmap_edges(x), heatmap_edges(y)
|
||||
m, n = size(z.surf)
|
||||
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
|
||||
fz = zeros(m * n)
|
||||
for i in 1:m # y
|
||||
for j in 1:n # x
|
||||
k = (j - 1) * m + i
|
||||
inds = (6 * (k - 1) + 1):(6 * k - 1)
|
||||
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
|
||||
y_pts[inds] .= [ye[i], ye[i], ye[i + 1], ye[i + 1], ye[i]]
|
||||
fz[k] = z.surf[i, j]
|
||||
end
|
||||
end
|
||||
ensure_gradient!(plotattributes, :fillcolor, :fillalpha)
|
||||
fill_z := fz
|
||||
line_z := fz
|
||||
x := x_pts
|
||||
y := y_pts
|
||||
z := nothing
|
||||
seriestype := :shape
|
||||
label := ""
|
||||
widen --> false
|
||||
()
|
||||
end
|
||||
@deps plots_heatmap shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
@@ -343,9 +425,9 @@ _bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||
|
||||
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||
|
||||
_positive_else_nan{T}(::Type{T}, x::Real) = _is_positive(x) ? T(x) : T(NaN)
|
||||
_positive_else_nan(::Type{T}, x::Real) where {T} = _is_positive(x) ? T(x) : T(NaN)
|
||||
|
||||
function _scale_adjusted_values{T<:AbstractFloat}(::Type{T}, V::AbstractVector, scale::Symbol)
|
||||
function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) where T<:AbstractFloat
|
||||
if scale in _logScales
|
||||
[_positive_else_nan(T, x) for x in V]
|
||||
else
|
||||
@@ -354,7 +436,7 @@ function _scale_adjusted_values{T<:AbstractFloat}(::Type{T}, V::AbstractVector,
|
||||
end
|
||||
|
||||
|
||||
function _binbarlike_baseline{T<:Real}(min_value::T, scale::Symbol)
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
else
|
||||
@@ -363,7 +445,7 @@ function _binbarlike_baseline{T<:Real}(min_value::T, scale::Symbol)
|
||||
end
|
||||
|
||||
|
||||
function _preprocess_binbarlike_weights{T<:AbstractFloat}(::Type{T}, w, wscale::Symbol)
|
||||
function _preprocess_binbarlike_weights(::Type{T}, w, wscale::Symbol) where T<:AbstractFloat
|
||||
w_adj = _scale_adjusted_values(T, w, wscale)
|
||||
w_min = ignorenan_minimum(w_adj)
|
||||
w_max = ignorenan_maximum(w_adj)
|
||||
@@ -371,16 +453,16 @@ function _preprocess_binbarlike_weights{T<:AbstractFloat}(::Type{T}, w, wscale::
|
||||
w_adj, baseline
|
||||
end
|
||||
|
||||
function _preprocess_barlike(d, x, y)
|
||||
xscale = get(d, :xscale, :identity)
|
||||
yscale = get(d, :yscale, :identity)
|
||||
function _preprocess_barlike(plotattributes, x, y)
|
||||
xscale = get(plotattributes, :xscale, :identity)
|
||||
yscale = get(plotattributes, :yscale, :identity)
|
||||
weights, baseline = _preprocess_binbarlike_weights(float(eltype(y)), y, yscale)
|
||||
x, weights, xscale, yscale, baseline
|
||||
end
|
||||
|
||||
function _preprocess_binlike(d, x, y)
|
||||
xscale = get(d, :xscale, :identity)
|
||||
yscale = get(d, :yscale, :identity)
|
||||
function _preprocess_binlike(plotattributes, x, y)
|
||||
xscale = get(plotattributes, :xscale, :identity)
|
||||
yscale = get(plotattributes, :yscale, :identity)
|
||||
T = float(promote_type(eltype(x), eltype(y)))
|
||||
edge = T.(x)
|
||||
weights, baseline = _preprocess_binbarlike_weights(T, y, yscale)
|
||||
@@ -389,8 +471,8 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||
if (d[:bar_width] == nothing)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
if (plotattributes[:bar_width] == nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
x := _bin_centers(edge)
|
||||
@@ -402,7 +484,7 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge)/2
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
@@ -416,21 +498,25 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
nbins = length(linearindices(weights))
|
||||
if length(linearindices(edge)) != nbins + 1
|
||||
nbins = length(eachindex(weights))
|
||||
if length(eachindex(edge)) != nbins + 1
|
||||
error("Edge vector must be 1 longer than weight vector")
|
||||
end
|
||||
|
||||
x = eltype(edge)[]
|
||||
y = eltype(weights)[]
|
||||
|
||||
it_e, it_w = start(edge), start(weights)
|
||||
a, it_e = next(edge, it_e)
|
||||
it_tuple_e = iterate(edge)
|
||||
a, it_state_e = it_tuple_e
|
||||
it_tuple_e = iterate(edge, it_state_e)
|
||||
|
||||
it_tuple_w = iterate(weights)
|
||||
|
||||
last_w = eltype(weights)(NaN)
|
||||
i = 1
|
||||
while (!done(edge, it_e) && !done(edge, it_e))
|
||||
b, it_e = next(edge, it_e)
|
||||
w, it_w = next(weights, it_w)
|
||||
|
||||
while it_tuple_e != nothing && it_tuple_w != nothing
|
||||
b, it_state_e = it_tuple_e
|
||||
w, it_state_w = it_tuple_w
|
||||
|
||||
if (log_scale_x && a ≈ 0)
|
||||
a = b/_logScaleBases[xscale]^3
|
||||
@@ -454,6 +540,9 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
||||
|
||||
a = b
|
||||
last_w = w
|
||||
|
||||
it_tuple_e = iterate(edge, it_state_e)
|
||||
it_tuple_w = iterate(weights, it_state_w)
|
||||
end
|
||||
if (last_w != baseline)
|
||||
push!(x, a)
|
||||
@@ -465,17 +554,17 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
|
||||
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(d)
|
||||
if !isvertical(plotattributes)
|
||||
xpts, ypts = ypts, xpts
|
||||
end
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
if plotattributes[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := _bin_centers(edge)
|
||||
@@ -497,15 +586,18 @@ end
|
||||
end
|
||||
Plots.@deps stepbins path
|
||||
|
||||
wand_edges(x...) = (@warn("Load the StatPlots package in order to use :wand bins. Defaulting to :auto", once = true); :auto)
|
||||
|
||||
function _auto_binning_nbins{N}(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto)
|
||||
function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto) where N
|
||||
_cl(x) = ceil(Int, NaNMath.max(x, one(x)))
|
||||
_iqr(v) = quantile(v, 0.75) - quantile(v, 0.25)
|
||||
_iqr(v) = (q = quantile(v, 0.75) - quantile(v, 0.25); q > 0 ? q : oftype(q, 1))
|
||||
_span(v) = ignorenan_maximum(v) - ignorenan_minimum(v)
|
||||
|
||||
n_samples = length(linearindices(first(vs)))
|
||||
# Estimator for number of samples in one row/column of bins along each axis:
|
||||
n = max(1, n_samples^(1/N))
|
||||
n_samples = length(LinearIndices(first(vs)))
|
||||
|
||||
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
|
||||
nd = n_samples^(1/(2+N))
|
||||
nd = N == 2 ? min(n_samples^(1/(2+N)), nd / (1-cor(first(vs), last(vs))^2)^(3//8)) : nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
@@ -514,38 +606,40 @@ function _auto_binning_nbins{N}(vs::NTuple{N,AbstractVector}, dim::Integer; mode
|
||||
end
|
||||
|
||||
if mode == :sqrt # Square-root choice
|
||||
_cl(sqrt(n))
|
||||
_cl(sqrt(n_samples))
|
||||
elseif mode == :sturges # Sturges' formula
|
||||
_cl(log2(n)) + 1
|
||||
_cl(log2(n_samples) + 1)
|
||||
elseif mode == :rice # Rice Rule
|
||||
_cl(2 * n^(1/3))
|
||||
_cl(2 * nd)
|
||||
elseif mode == :scott # Scott's normal reference rule
|
||||
_cl(_span(v) / (3.5 * std(v) / n^(1/3)))
|
||||
_cl(_span(v) / (3.5 * std(v) / nd))
|
||||
elseif mode == :fd # Freedman–Diaconis rule
|
||||
_cl(_span(v) / (2 * _iqr(v) / n^(1/3)))
|
||||
_cl(_span(v) / (2 * _iqr(v) / nd))
|
||||
elseif mode == :wand
|
||||
wand_edges(v) # this makes this function not type stable, but the type instability does not propagate
|
||||
else
|
||||
error("Unknown auto-binning mode $mode")
|
||||
end::Int
|
||||
end
|
||||
end
|
||||
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) = binning
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} = binning
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::NTuple{N}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...))
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N, Any}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...))
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
function _make_hist{N}(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing)
|
||||
function _make_hist(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing) where N
|
||||
edges = _hist_edges(vs, binning)
|
||||
h = float( weights == nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, vs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, vs, weights, edges, closed = :left)
|
||||
StatsBase.fit(StatsBase.Histogram, vs, StatsBase.Weights(weights), edges, closed = :left)
|
||||
)
|
||||
normalize!(h, mode = _hist_norm_mode(normed))
|
||||
end
|
||||
@@ -558,7 +652,7 @@ end
|
||||
@deps histogram barhist
|
||||
|
||||
@recipe function f(::Type{Val{:barhist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :barbins
|
||||
@@ -567,7 +661,7 @@ end
|
||||
@deps barhist barbins
|
||||
|
||||
@recipe function f(::Type{Val{:stephist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :stepbins
|
||||
@@ -576,7 +670,7 @@ end
|
||||
@deps stephist stepbins
|
||||
|
||||
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :scatterbins
|
||||
@@ -585,18 +679,18 @@ end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f{T, E}(h::StatsBase.Histogram{T, 1, E})
|
||||
@recipe function f(h::StatsBase.Histogram{T, 1, E}) where {T, E}
|
||||
seriestype --> :barbins
|
||||
|
||||
st_map = Dict(
|
||||
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
|
||||
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype := get(st_map, d[:seriestype], d[:seriestype])
|
||||
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
|
||||
if d[:seriestype] == :scatterbins
|
||||
if plotattributes[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, h.edges[1], h.weights)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1])/2
|
||||
seriestype := :scatter
|
||||
(Plots._bin_centers(edge), weights)
|
||||
@@ -606,7 +700,7 @@ end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f{H <: StatsBase.Histogram}(hv::AbstractVector{H})
|
||||
@recipe function f(hv::AbstractVector{H}) where H <: StatsBase.Histogram
|
||||
for h in hv
|
||||
@series begin
|
||||
h
|
||||
@@ -643,7 +737,7 @@ Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist((x, y), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
h = _make_hist((x, y), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.edges[2]
|
||||
z := Surface(h.weights)
|
||||
@@ -653,7 +747,7 @@ end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f{T, E}(h::StatsBase.Histogram{T, 2, E})
|
||||
@recipe function f(h::StatsBase.Histogram{T, 2, E}) where {T, E}
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
@@ -664,7 +758,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:scatter3d}}, x, y, z)
|
||||
seriestype := :path3d
|
||||
if d[:markershape] == :none
|
||||
if plotattributes[:markershape] == :none
|
||||
markershape := :circle
|
||||
end
|
||||
linewidth := 0
|
||||
@@ -687,11 +781,11 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error Bars
|
||||
|
||||
function error_style!(d::KW)
|
||||
d[:seriestype] = :path
|
||||
d[:linecolor] = d[:markerstrokecolor]
|
||||
d[:linewidth] = d[:markerstrokewidth]
|
||||
d[:label] = ""
|
||||
function error_style!(plotattributes::KW)
|
||||
plotattributes[:seriestype] = :path
|
||||
plotattributes[:linecolor] = plotattributes[:markerstrokecolor]
|
||||
plotattributes[:linewidth] = plotattributes[:markerstrokewidth]
|
||||
plotattributes[:label] = ""
|
||||
end
|
||||
|
||||
# if we're passed a tuple of vectors, convert to a vector of tuples
|
||||
@@ -705,7 +799,7 @@ end
|
||||
|
||||
function error_coords(xorig, yorig, ebar)
|
||||
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
|
||||
x, y = Array{float_extended_type(xorig)}(0), Array{Float64}(0)
|
||||
x, y = Array{float_extended_type(xorig)}(undef, 0), Array{Float64}(undef, 0)
|
||||
# for each point, create a line segment from the bottom to the top of the errorbar
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
xi = _cycle(xorig, i)
|
||||
@@ -727,17 +821,17 @@ end
|
||||
# we will create a series of path segments, where each point represents one
|
||||
# side of an errorbar
|
||||
@recipe function f(::Type{Val{:yerror}}, x, y, z)
|
||||
error_style!(d)
|
||||
error_style!(plotattributes)
|
||||
markershape := :hline
|
||||
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(plotattributes[:x], plotattributes[:y], error_zipit(plotattributes[:yerror]))
|
||||
()
|
||||
end
|
||||
@deps yerror path
|
||||
|
||||
@recipe function f(::Type{Val{:xerror}}, x, y, z)
|
||||
error_style!(d)
|
||||
error_style!(plotattributes)
|
||||
markershape := :vline
|
||||
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(plotattributes[:y], plotattributes[:x], error_zipit(plotattributes[:xerror]))
|
||||
()
|
||||
end
|
||||
@deps xerror path
|
||||
@@ -748,16 +842,16 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# quiver
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(d::KW)
|
||||
d[:label] = ""
|
||||
d[:seriestype] = :path
|
||||
if !isa(d[:arrow], Arrow)
|
||||
d[:arrow] = arrow()
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(plotattributes::KW)
|
||||
plotattributes[:label] = ""
|
||||
plotattributes[:seriestype] = :path
|
||||
if !isa(plotattributes[:arrow], Arrow)
|
||||
plotattributes[:arrow] = arrow()
|
||||
end
|
||||
|
||||
velocity = error_zipit(d[:quiver])
|
||||
xorig, yorig = d[:x], d[:y]
|
||||
velocity = error_zipit(plotattributes[:quiver])
|
||||
xorig, yorig = plotattributes[:x], plotattributes[:y]
|
||||
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
x, y = zeros(0), zeros(0)
|
||||
@@ -783,17 +877,17 @@ function quiver_using_arrows(d::KW)
|
||||
nanappend!(y, [yi, yi+vy, NaN])
|
||||
end
|
||||
|
||||
d[:x], d[:y] = x, y
|
||||
# KW[d]
|
||||
plotattributes[:x], plotattributes[:y] = x, y
|
||||
# KW[plotattributes]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(d::KW)
|
||||
d[:label] = ""
|
||||
d[:seriestype] = :shape
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(plotattributes::KW)
|
||||
plotattributes[:label] = ""
|
||||
plotattributes[:seriestype] = :shape
|
||||
|
||||
velocity = error_zipit(d[:quiver])
|
||||
xorig, yorig = d[:x], d[:y]
|
||||
velocity = error_zipit(plotattributes[:quiver])
|
||||
xorig, yorig = plotattributes[:x], plotattributes[:y]
|
||||
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
pts = P2[]
|
||||
@@ -829,16 +923,16 @@ function quiver_using_hack(d::KW)
|
||||
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
|
||||
end
|
||||
|
||||
d[:x], d[:y] = Plots.unzip(pts[2:end])
|
||||
# KW[d]
|
||||
plotattributes[:x], plotattributes[:y] = Plots.unzip(pts[2:end])
|
||||
# KW[plotattributes]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
# function apply_series_recipe(plotattributes::KW, ::Type{Val{:quiver}})
|
||||
@recipe function f(::Type{Val{:quiver}}, x, y, z)
|
||||
if :arrow in supported_attrs()
|
||||
quiver_using_arrows(d)
|
||||
quiver_using_arrows(plotattributes)
|
||||
else
|
||||
quiver_using_hack(d)
|
||||
quiver_using_hack(plotattributes)
|
||||
end
|
||||
()
|
||||
end
|
||||
@@ -850,7 +944,7 @@ end
|
||||
# TODO: move OHLC to PlotRecipes finance.jl
|
||||
|
||||
"Represent Open High Low Close data (used in finance)"
|
||||
type OHLC{T<:Real}
|
||||
mutable struct OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
@@ -889,10 +983,10 @@ end
|
||||
|
||||
# to squash ambiguity warnings...
|
||||
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) = error()
|
||||
@recipe f(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
|
||||
# this must be OHLC?
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
@recipe f(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
|
||||
@recipe function f(x::AVec, v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
@@ -930,9 +1024,6 @@ end
|
||||
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
|
||||
seriestype := :spy
|
||||
mat = g.args[1]
|
||||
if length(unique(mat[mat .!= 0])) < 2
|
||||
legend --> nothing
|
||||
end
|
||||
n,m = size(mat)
|
||||
Plots.SliceIt, 1:m, 1:n, Surface(mat)
|
||||
end
|
||||
@@ -943,10 +1034,10 @@ end
|
||||
rs, cs, zs = findnz(z.surf)
|
||||
xlim := ignorenan_extrema(cs)
|
||||
ylim := ignorenan_extrema(rs)
|
||||
if d[:markershape] == :none
|
||||
if plotattributes[:markershape] == :none
|
||||
markershape := :circle
|
||||
end
|
||||
if d[:markersize] == default(:markersize)
|
||||
if plotattributes[:markersize] == default(:markersize)
|
||||
markersize := 1
|
||||
end
|
||||
markerstrokewidth := 0
|
||||
@@ -962,27 +1053,27 @@ end
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
"Adds a+bx... straight line over the current plot"
|
||||
function abline!(plt::Plot, a, b; kw...)
|
||||
plot!(plt, [ignorenan_extrema(plt)...], x -> b + a*x; kw...)
|
||||
end
|
||||
"Adds a+bx... straight line over the current plot, without changing the axis limits"
|
||||
abline!(plt::Plot, a, b; kw...) = plot!(plt, [0, 1], [b, b+a]; seriestype = :straightline, kw...)
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
# Dates
|
||||
# Dates & Times
|
||||
|
||||
dateformatter(dt) = string(Date(Dates.UTD(dt)))
|
||||
datetimeformatter(dt) = string(DateTime(Dates.UTM(dt)))
|
||||
timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> Dates.value(dt), dateformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@recipe function f{T<:Number}(A::Array{Complex{T}})
|
||||
@recipe function f(A::Array{Complex{T}}) where T<:Number
|
||||
xguide --> "Re(x)"
|
||||
yguide --> "Im(x)"
|
||||
real.(A), imag.(A)
|
||||
@@ -991,7 +1082,7 @@ end
|
||||
# Splits a complex matrix to its real and complex parts
|
||||
# Reals defaults solid, imaginary defaults dashed
|
||||
# Label defaults are changed to match the real-imaginary reference / indexing
|
||||
@recipe function f{T<:Real,T2}(x::AbstractArray{T},y::Array{Complex{T2}})
|
||||
@recipe function f(x::AbstractArray{T},y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x,real.(y),imag.(y)
|
||||
|
||||
+220
-98
@@ -8,56 +8,66 @@
|
||||
|
||||
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
all3D(plotattributes::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :seriestype, :none))
|
||||
|
||||
# unknown
|
||||
convertToAnyVector(x, plotattributes::KW) = error("No user recipe defined for $(typeof(x))")
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::Void, d::KW) = Any[nothing], nothing
|
||||
convertToAnyVector(v::Nothing, plotattributes::KW) = Any[nothing], nothing
|
||||
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
convertToAnyVector(n::Integer, plotattributes::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
|
||||
# numeric vector
|
||||
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
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{T<:AbstractString}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
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, d::KW)
|
||||
if all3D(d)
|
||||
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, d::KW) = Any[f], nothing
|
||||
convertToAnyVector(f::Function, plotattributes::KW) = Any[f], nothing
|
||||
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
|
||||
convertToAnyVector(s::Surface, plotattributes::KW) = Any[s], nothing
|
||||
|
||||
# volume
|
||||
convertToAnyVector(v::Volume, d::KW) = Any[v], nothing
|
||||
convertToAnyVector(v::Volume, plotattributes::KW) = Any[v], nothing
|
||||
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
# convertToAnyVector(v::AVec{OHLC}, plotattributes::KW) = Any[v], nothing
|
||||
|
||||
# # dates
|
||||
# convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||
convertToAnyVector(dts::AVec{D}, plotattributes::KW) where {D<:Union{Date,DateTime}} = Any[dts], nothing
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
function convertToAnyVector(v::AVec, d::KW)
|
||||
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
|
||||
else
|
||||
# something else... treat each element as an item
|
||||
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
|
||||
vcat(Any[convertToAnyVector(vi, plotattributes)[1] for vi in v]...), nothing
|
||||
# Any[vi for vi in v], nothing
|
||||
end
|
||||
end
|
||||
|
||||
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
|
||||
convertToAnyVector(t::Tuple, plotattributes::KW) = Any[t], nothing
|
||||
|
||||
|
||||
function convertToAnyVector(args...)
|
||||
@@ -69,19 +79,19 @@ end
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
# result in that array being shared. push!, etc will add too many items to that array
|
||||
|
||||
compute_x(x::Void, y::Void, z) = 1:size(z,1)
|
||||
compute_x(x::Void, y, z) = 1:size(y,1)
|
||||
compute_x(x::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)
|
||||
|
||||
# compute_y(x::Void, y::Function, z) = error()
|
||||
compute_y(x::Void, y::Void, z) = 1:size(z,2)
|
||||
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)
|
||||
|
||||
compute_z(x, y, z::Function) = map(z, x, y)
|
||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Void) = nothing
|
||||
compute_z(x, y, z::Nothing) = nothing
|
||||
compute_z(x, y, z) = copy(z)
|
||||
|
||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||
@@ -96,9 +106,9 @@ nobigs(v) = v
|
||||
end
|
||||
|
||||
# not allowed
|
||||
compute_xyz{F<:Function}(x::Void, y::FuncOrFuncs{F}, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz{F<:Function}(x::Void, y::Void, z::FuncOrFuncs{F}) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
compute_xyz(x::Nothing, y::FuncOrFuncs{F}, z) where {F<:Function} = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs{F}) where {F<:Function} = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
@@ -106,7 +116,7 @@ compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
# we are going to build recipes to do the processing and splitting of the args
|
||||
|
||||
# ensure we dispatch to the slicer
|
||||
immutable SliceIt end
|
||||
struct SliceIt end
|
||||
|
||||
# the catch-all recipes
|
||||
@recipe function f(::Type{SliceIt}, x, y, z)
|
||||
@@ -125,18 +135,26 @@ immutable SliceIt end
|
||||
z = z.data
|
||||
end
|
||||
|
||||
xs, _ = convertToAnyVector(x, d)
|
||||
ys, _ = convertToAnyVector(y, d)
|
||||
zs, _ = convertToAnyVector(z, d)
|
||||
xs, _ = convertToAnyVector(x, plotattributes)
|
||||
ys, _ = convertToAnyVector(y, plotattributes)
|
||||
zs, _ = convertToAnyVector(z, plotattributes)
|
||||
|
||||
fr = pop!(d, :fillrange, nothing)
|
||||
fr = pop!(plotattributes, :fillrange, nothing)
|
||||
fillranges, _ = if typeof(fr) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(fr, d)
|
||||
convertToAnyVector(fr, plotattributes)
|
||||
end
|
||||
mf = length(fillranges)
|
||||
|
||||
rib = pop!(plotattributes, :ribbon, nothing)
|
||||
ribbons, _ = if typeof(rib) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(rib, plotattributes)
|
||||
end
|
||||
mr = length(ribbons)
|
||||
|
||||
# @show zs
|
||||
|
||||
mx = length(xs)
|
||||
@@ -145,7 +163,7 @@ immutable SliceIt end
|
||||
if mx > 0 && my > 0 && mz > 0
|
||||
for i in 1:max(mx, my, mz)
|
||||
# add a new series
|
||||
di = copy(d)
|
||||
di = copy(plotattributes)
|
||||
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
|
||||
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
|
||||
|
||||
@@ -153,6 +171,10 @@ immutable SliceIt end
|
||||
fr = fillranges[mod1(i,mf)]
|
||||
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
|
||||
|
||||
# handle ribbons
|
||||
rib = ribbons[mod1(i,mr)]
|
||||
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
|
||||
|
||||
push!(series_list, RecipeData(di, ()))
|
||||
end
|
||||
end
|
||||
@@ -160,18 +182,19 @@ immutable SliceIt end
|
||||
end
|
||||
|
||||
# this is the default "type recipe"... just pass the object through
|
||||
@recipe f{T<:Any}(::Type{T}, v::T) = v
|
||||
@recipe f(::Type{T}, v::T) where {T<:Any} = v
|
||||
|
||||
# this should catch unhandled "series recipes" and error with a nice message
|
||||
@recipe f{V<:Val}(::Type{V}, x, y, z) = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
|
||||
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
|
||||
|
||||
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
|
||||
_apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
|
||||
|
||||
# Handle type recipes when the recipe is defined on the elements.
|
||||
# This sort of recipe should return a pair of functions... one to convert to number,
|
||||
# and one to format tick values.
|
||||
function _apply_type_recipe(d, v::AbstractArray)
|
||||
args = RecipesBase.apply_recipe(d, typeof(v[1]), v[1])[1].args
|
||||
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)
|
||||
@@ -181,13 +204,13 @@ function _apply_type_recipe(d, v::AbstractArray)
|
||||
end
|
||||
|
||||
# # special handling for Surface... need to properly unwrap and re-wrap
|
||||
# function _apply_type_recipe(d, v::Surface)
|
||||
# function _apply_type_recipe(plotattributes, v::Surface)
|
||||
# T = eltype(v.surf)
|
||||
# @show T
|
||||
# if T <: Integer || T <: AbstractFloat
|
||||
# v
|
||||
# else
|
||||
# ret = _apply_type_recipe(d, v.surf)
|
||||
# ret = _apply_type_recipe(plotattributes, v.surf)
|
||||
# if typeof(ret) <: Formatted
|
||||
# Formatted(Surface(ret.data), ret.formatter)
|
||||
# else
|
||||
@@ -197,16 +220,16 @@ end
|
||||
# end
|
||||
|
||||
# don't do anything for ints or floats
|
||||
_apply_type_recipe{T<:Union{Integer,AbstractFloat}}(d, v::AbstractArray{T}) = v
|
||||
_apply_type_recipe(plotattributes, v::AbstractArray{T}) where {T<:Union{Integer,AbstractFloat}} = v
|
||||
|
||||
# handle "type recipes" by converting inputs, and then either re-calling or slicing
|
||||
@recipe function f(x, y, z)
|
||||
did_replace = false
|
||||
newx = _apply_type_recipe(d, x)
|
||||
newx = _apply_type_recipe(plotattributes, x)
|
||||
x === newx || (did_replace = true)
|
||||
newy = _apply_type_recipe(d, y)
|
||||
newy = _apply_type_recipe(plotattributes, y)
|
||||
y === newy || (did_replace = true)
|
||||
newz = _apply_type_recipe(d, z)
|
||||
newz = _apply_type_recipe(plotattributes, z)
|
||||
z === newz || (did_replace = true)
|
||||
if did_replace
|
||||
newx, newy, newz
|
||||
@@ -216,9 +239,9 @@ _apply_type_recipe{T<:Union{Integer,AbstractFloat}}(d, v::AbstractArray{T}) = v
|
||||
end
|
||||
@recipe function f(x, y)
|
||||
did_replace = false
|
||||
newx = _apply_type_recipe(d, x)
|
||||
newx = _apply_type_recipe(plotattributes, x)
|
||||
x === newx || (did_replace = true)
|
||||
newy = _apply_type_recipe(d, y)
|
||||
newy = _apply_type_recipe(plotattributes, y)
|
||||
y === newy || (did_replace = true)
|
||||
if did_replace
|
||||
newx, newy
|
||||
@@ -227,7 +250,7 @@ end
|
||||
end
|
||||
end
|
||||
@recipe function f(y)
|
||||
newy = _apply_type_recipe(d, y)
|
||||
newy = _apply_type_recipe(plotattributes, y)
|
||||
if y !== newy
|
||||
newy
|
||||
else
|
||||
@@ -240,7 +263,7 @@ end
|
||||
@recipe function f(v1, v2, v3, v4, vrest...)
|
||||
did_replace = false
|
||||
newargs = map(v -> begin
|
||||
newv = _apply_type_recipe(d, v)
|
||||
newv = _apply_type_recipe(plotattributes, v)
|
||||
if newv !== v
|
||||
did_replace = true
|
||||
end
|
||||
@@ -258,22 +281,22 @@ end
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# helper function to ensure relevant attributes are wrapped by Surface
|
||||
function wrap_surfaces(d::KW)
|
||||
if haskey(d, :fill_z)
|
||||
v = d[:fill_z]
|
||||
function wrap_surfaces(plotattributes::KW)
|
||||
if haskey(plotattributes, :fill_z)
|
||||
v = plotattributes[:fill_z]
|
||||
if !isa(v, Surface)
|
||||
d[:fill_z] = Surface(v)
|
||||
plotattributes[:fill_z] = Surface(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@recipe f(n::Integer) = is3d(get(d,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||
|
||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||
@recipe function f{T<:Union{Integer,AbstractFloat}}(mat::AMat{T})
|
||||
if all3D(d)
|
||||
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat}
|
||||
if all3D(plotattributes)
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(d)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
SliceIt, nothing, mat, nothing
|
||||
@@ -281,11 +304,11 @@ end
|
||||
end
|
||||
|
||||
# if a matrix is wrapped by Formatted, do similar logic, but wrap data with Surface
|
||||
@recipe function f{T<:AbstractMatrix}(fmt::Formatted{T})
|
||||
if all3D(d)
|
||||
@recipe function f(fmt::Formatted{T}) where T<:AbstractMatrix
|
||||
if all3D(plotattributes)
|
||||
mat = fmt.data
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(d)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
|
||||
else
|
||||
SliceIt, nothing, fmt, nothing
|
||||
@@ -293,7 +316,7 @@ end
|
||||
end
|
||||
|
||||
# assume this is a Volume, so construct one
|
||||
@recipe function f{T<:Number}(vol::AbstractArray{T,3}, args...)
|
||||
@recipe function f(vol::AbstractArray{T,3}, args...) where T<:Number
|
||||
seriestype := :volume
|
||||
SliceIt, nothing, Volume(vol, args...), nothing
|
||||
end
|
||||
@@ -301,14 +324,16 @@ end
|
||||
|
||||
# # images - grays
|
||||
|
||||
@recipe function f{T<:Gray}(mat::AMat{T})
|
||||
@recipe function f(mat::AMat{T}) where T<:Gray
|
||||
n, m = size(mat)
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
n, m = size(mat)
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
|
||||
end
|
||||
@@ -316,16 +341,18 @@ end
|
||||
|
||||
# # images - colors
|
||||
|
||||
@recipe function f{T<:Colorant}(mat::AMat{T})
|
||||
@recipe function f(mat::AMat{T}) where T<:Colorant
|
||||
n, m = size(mat)
|
||||
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
z, d[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
cbar --> false
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(z)
|
||||
end
|
||||
end
|
||||
@@ -354,16 +381,36 @@ end
|
||||
|
||||
# function without range... use the current range of the x-axis
|
||||
|
||||
@recipe function f{F<:Function}(f::FuncOrFuncs{F})
|
||||
plt = d[:plot_object]
|
||||
@recipe function f(f::FuncOrFuncs{F}) where F<:Function
|
||||
plt = plotattributes[:plot_object]
|
||||
xmin, xmax = try
|
||||
axis_limits(plt[1][:xaxis])
|
||||
catch
|
||||
-5, 5
|
||||
xm = tryrange(f, [-5,-1,0,0.01])
|
||||
xm, tryrange(f, filter(x->x>xm, [5,1,0.99, 0, -0.01]))
|
||||
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
|
||||
@@ -373,7 +420,7 @@ end
|
||||
# # 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<:Function}(f::FuncOrFuncs{F}, x)
|
||||
@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function
|
||||
F2 = typeof(x)
|
||||
@assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here
|
||||
x, f
|
||||
@@ -388,23 +435,23 @@ end
|
||||
# # 3d line or scatter
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
||||
# st = get(d, :seriestype, :none)
|
||||
# st = get(plotattributes, :seriestype, :none)
|
||||
# if st == :scatter
|
||||
# d[:seriestype] = :scatter3d
|
||||
# plotattributes[:seriestype] = :scatter3d
|
||||
# elseif !is3d(st)
|
||||
# d[:seriestype] = :path3d
|
||||
# plotattributes[:seriestype] = :path3d
|
||||
# end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
||||
# st = get(d, :seriestype, :none)
|
||||
# st = get(plotattributes, :seriestype, :none)
|
||||
# if size(x) == size(y) == size(z)
|
||||
# if !is3d(st)
|
||||
# seriestype := :path3d
|
||||
# end
|
||||
# end
|
||||
wrap_surfaces(d)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@@ -414,7 +461,7 @@ end
|
||||
@recipe function f(x::AVec, y::AVec, zf::Function)
|
||||
# x = X <: Number ? sort(x) : x
|
||||
# y = Y <: Number ? sort(y) : y
|
||||
wrap_surfaces(d)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
end
|
||||
|
||||
@@ -422,13 +469,45 @@ end
|
||||
# # surface-like... matrix grid
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AMat)
|
||||
if !like_surface(get(d, :seriestype, :none))
|
||||
d[:seriestype] = :contour
|
||||
if !like_surface(get(plotattributes, :seriestype, :none))
|
||||
plotattributes[:seriestype] = :contour
|
||||
end
|
||||
wrap_surfaces(d)
|
||||
wrap_surfaces(plotattributes)
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
|
||||
# # images - grays
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, x, y, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, x, y, Surface(convert(Matrix{Float64}, mat))
|
||||
end
|
||||
end
|
||||
|
||||
# # images - colors
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant
|
||||
if is_seriestype_supported(:image)
|
||||
seriestype := :image
|
||||
yflip --> true
|
||||
SliceIt, x, y, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
@@ -441,20 +520,20 @@ end
|
||||
xs = adapted_grid(f, (xmin, xmax))
|
||||
xs, f
|
||||
end
|
||||
@recipe function f{F<:Function}(fs::AbstractArray{F}, xmin::Number, xmax::Number)
|
||||
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function
|
||||
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
|
||||
xs, fs
|
||||
end
|
||||
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
|
||||
@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)
|
||||
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec)
|
||||
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function}
|
||||
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
|
||||
end
|
||||
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200)
|
||||
fx, fy, fz, linspace(umin, umax, numPoints)
|
||||
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200) where {F<:Function,G<:Function,H<:Function}
|
||||
fx, fy, fz, range(umin, stop = umax, length = numPoints)
|
||||
end
|
||||
|
||||
#
|
||||
@@ -468,28 +547,28 @@ end
|
||||
|
||||
#
|
||||
# # (x,y) tuples
|
||||
@recipe f{R1<:Number,R2<:Number}(xy::AVec{Tuple{R1,R2}}) = unzip(xy)
|
||||
@recipe f{R1<:Number,R2<:Number}(xy::Tuple{R1,R2}) = [xy[1]], [xy[2]]
|
||||
@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]]
|
||||
|
||||
#
|
||||
# # (x,y,z) tuples
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::AVec{Tuple{R1,R2,R3}}) = unzip(xyz)
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
@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]]
|
||||
|
||||
# these might be points+velocity, or OHLC or something else
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::AVec{Tuple{R1,R2,R3,R4}}) = get(d,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::Tuple{R1,R2,R3,R4}) = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
|
||||
@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]]
|
||||
|
||||
|
||||
#
|
||||
# # 2D FixedSizeArrays
|
||||
@recipe f{T<:Number}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = unzip(xy)
|
||||
@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]]
|
||||
@recipe f(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T<:Number} = unzip(xy)
|
||||
@recipe f(xy::FixedSizeArrays.Vec{2,T}) where {T<:Number} = [xy[1]], [xy[2]]
|
||||
|
||||
#
|
||||
# # 3D FixedSizeArrays
|
||||
@recipe f{T<:Number}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = unzip(xyz)
|
||||
@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
@recipe f(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T<:Number} = unzip(xyz)
|
||||
@recipe f(xyz::FixedSizeArrays.Vec{3,T}) where {T<:Number} = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
@@ -501,7 +580,7 @@ end
|
||||
# # create a new series, with the label of the group, and an idxfilter (to be applied in slice_and_dice)
|
||||
# # TODO: use @series instead
|
||||
# @show i, glab, groupby.groupIds[i]
|
||||
# di = copy(d)
|
||||
# di = copy(plotattributes)
|
||||
# get!(di, :label, string(glab))
|
||||
# get!(di, :idxfilter, groupby.groupIds[i])
|
||||
# push!(series_list, RecipeData(di, args))
|
||||
@@ -512,23 +591,66 @@ end
|
||||
splittable_kw(key, val, lengthGroup) = false
|
||||
splittable_kw(key, val::AbstractArray, lengthGroup) = (key != :group) && size(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)
|
||||
|
||||
split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val)-1)...]
|
||||
split_kw(key, val::Tuple, indices) = Tuple(split_kw(key, v, indices) for v in val)
|
||||
function split_kw(key, val::SeriesAnnotations, indices)
|
||||
split_strs = split_kw(key, val.strs, indices)
|
||||
return SeriesAnnotations(split_strs, val.font, val.baseshape, val.scalefactor)
|
||||
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)
|
||||
xi = x[groupby.groupIds[i]]
|
||||
yi = y[groupby.groupIds[i]]
|
||||
y_mat[getindex.(Ref(x_ind), xi), i] = yi
|
||||
end
|
||||
return y_mat
|
||||
end
|
||||
|
||||
groupedvec2mat(x_ind, x, y::Tuple, groupby) = Tuple(groupedvec2mat(x_ind, x, v, groupby) for v in y)
|
||||
|
||||
group_as_matrix(t) = false
|
||||
|
||||
# split the group into 1 series per group, and set the label and idxfilter for each
|
||||
@recipe function f(groupby::GroupBy, args...)
|
||||
lengthGroup = maximum(union(groupby.groupIds...))
|
||||
for (i,glab) in enumerate(groupby.groupLabels)
|
||||
@series begin
|
||||
label --> string(glab)
|
||||
idxfilter --> groupby.groupIds[i]
|
||||
for (key,val) in d
|
||||
if splittable_kw(key, val, lengthGroup)
|
||||
:($key) := split_kw(key, val, groupby.groupIds[i])
|
||||
if !(group_as_matrix(args[1]))
|
||||
for (i,glab) in enumerate(groupby.groupLabels)
|
||||
@series begin
|
||||
label --> string(glab)
|
||||
idxfilter --> groupby.groupIds[i]
|
||||
for (key,val) in plotattributes
|
||||
if splittable_kw(key, val, lengthGroup)
|
||||
:($key) := split_kw(key, val, groupby.groupIds[i])
|
||||
end
|
||||
end
|
||||
args
|
||||
end
|
||||
args
|
||||
end
|
||||
else
|
||||
g = args[1]
|
||||
if length(g.args) == 1
|
||||
x = zeros(Int, lengthGroup)
|
||||
for indexes in groupby.groupIds
|
||||
x[indexes] = 1:length(indexes)
|
||||
end
|
||||
last_args = g.args
|
||||
else
|
||||
x = g.args[1]
|
||||
last_args = g.args[2:end]
|
||||
end
|
||||
x_u = unique(sort(x))
|
||||
x_ind = Dict(zip(x_u, 1:length(x_u)))
|
||||
for (key,val) in plotattributes
|
||||
if splittable_kw(key, val, lengthGroup)
|
||||
:($key) := groupedvec2mat(x_ind, x, val, groupby)
|
||||
end
|
||||
end
|
||||
label --> reshape(groupby.groupLabels, 1, :)
|
||||
typeof(g)((x_u, (groupedvec2mat(x_ind, x, arg, groupby, NaN) for arg in last_args)...))
|
||||
end
|
||||
end
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
|
||||
function Subplot(::T; parent = RootLayout()) where T<:AbstractBackend
|
||||
Subplot{T}(
|
||||
parent,
|
||||
Series[],
|
||||
@@ -34,16 +34,16 @@ bottompad(sp::Subplot) = sp.minpad[4]
|
||||
get_subplot(plt::Plot, sp::Subplot) = sp
|
||||
get_subplot(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
get_subplot(plt::Plot, k) = plt.spmap[k]
|
||||
get_subplot(series::Series) = series.d[:subplot]
|
||||
get_subplot(series::Series) = series.plotattributes[:subplot]
|
||||
|
||||
get_subplot_index(plt::Plot, idx::Integer) = Int(idx)
|
||||
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(x -> x === sp, plt.subplots)
|
||||
|
||||
series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
|
||||
series_list(sp::Subplot) = sp.series_list # filter(series -> series.plotattributes[:subplot] === sp, sp.plt.series_list)
|
||||
|
||||
function should_add_to_legend(series::Series)
|
||||
series.d[:primary] && series.d[:label] != "" &&
|
||||
!(series.d[:seriestype] in (
|
||||
series.plotattributes[:primary] && series.plotattributes[:label] != "" &&
|
||||
!(series.plotattributes[:seriestype] in (
|
||||
:hexbin,:bins2d,:histogram2d,:hline,:vline,
|
||||
:contour,:contourf,:contour3d,:surface,:wireframe,
|
||||
:heatmap, :pie, :image
|
||||
|
||||
+154
-32
@@ -4,43 +4,165 @@
|
||||
Specify the colour theme for plots.
|
||||
"""
|
||||
function theme(s::Symbol; kw...)
|
||||
# reset?
|
||||
if s == :none || s == :default
|
||||
PlotUtils.clibrary(:Plots)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
|
||||
default(;
|
||||
bg = :white,
|
||||
bglegend = :match,
|
||||
bginside = :match,
|
||||
bgoutside = :match,
|
||||
fg = :auto,
|
||||
fglegend = :match,
|
||||
fggrid = :match,
|
||||
fgaxis = :match,
|
||||
fgtext = :match,
|
||||
fgborder = :match,
|
||||
fgguide = :match,
|
||||
palette = :auto
|
||||
)
|
||||
return
|
||||
end
|
||||
defaults = _get_defaults(s)
|
||||
_theme(s, defaults; kw...)
|
||||
end
|
||||
|
||||
# update the default gradient and other defaults
|
||||
function _get_defaults(s::Symbol)
|
||||
thm = PlotThemes._themes[s]
|
||||
if thm.gradient != nothing
|
||||
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...)
|
||||
# Reset to defaults to overwrite active theme
|
||||
reset_defaults()
|
||||
|
||||
# Set the theme's gradient as default
|
||||
if haskey(defaults, :gradient)
|
||||
PlotUtils.clibrary(:misc)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
|
||||
else
|
||||
PlotUtils.clibrary(:Plots)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
|
||||
end
|
||||
default(;
|
||||
bg = thm.bg_secondary,
|
||||
bginside = thm.bg_primary,
|
||||
fg = thm.lines,
|
||||
fgtext = thm.text,
|
||||
fgguide = thm.text,
|
||||
fglegend = thm.text,
|
||||
palette = thm.palette,
|
||||
kw...
|
||||
)
|
||||
|
||||
# maybe overwrite the theme's gradient
|
||||
kw = KW(kw)
|
||||
if haskey(kw, :gradient)
|
||||
kwgrad = pop!(kw, :gradient)
|
||||
for clib in clibraries()
|
||||
if kwgrad in cgradients(clib)
|
||||
PlotUtils.clibrary(clib)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = kwgrad)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Set the theme's defaults
|
||||
default(; defaults..., kw...)
|
||||
return
|
||||
end
|
||||
|
||||
@deprecate set_theme(s) theme(s)
|
||||
|
||||
@userplot ShowTheme
|
||||
|
||||
_color_functions = KW(
|
||||
:protanopic => protanopic,
|
||||
:deuteranopic => deuteranopic,
|
||||
:tritanopic => tritanopic,
|
||||
)
|
||||
_get_showtheme_args(thm::Symbol) = thm, identity
|
||||
_get_showtheme_args(thm::Symbol, func::Symbol) = thm, get(_color_functions, func, identity)
|
||||
|
||||
@recipe function showtheme(st::ShowTheme)
|
||||
thm, cfunc = _get_showtheme_args(st.args...)
|
||||
defaults = _get_defaults(thm)
|
||||
|
||||
# get the gradient
|
||||
gradient_colors = get(defaults, :gradient, cgrad(:inferno).colors)
|
||||
gradient = cgrad(cfunc.(RGB.(gradient_colors)))
|
||||
|
||||
# get the palette
|
||||
palette = get(defaults, :palette, get_color_palette(:auto, plot_color(:white), 17))
|
||||
palette = cfunc.(RGB.(palette))
|
||||
|
||||
# apply the theme
|
||||
for k in keys(defaults)
|
||||
k in (:gradient, :palette) && continue
|
||||
def = defaults[k]
|
||||
arg = get(_keyAliases, k, k)
|
||||
plotattributes[arg] = if typeof(def) <: Colorant
|
||||
cfunc(RGB(def))
|
||||
elseif eltype(def) <: Colorant
|
||||
cfunc.(RGB.(def))
|
||||
elseif occursin("color", string(arg))
|
||||
cfunc.(RGB.(plot_color.(def)))
|
||||
else
|
||||
def
|
||||
end
|
||||
end
|
||||
|
||||
srand(1)
|
||||
|
||||
label := ""
|
||||
colorbar := false
|
||||
layout := (2, 3)
|
||||
|
||||
for j in 1:4
|
||||
@series begin
|
||||
subplot := 1
|
||||
palette := palette
|
||||
seriestype := :path
|
||||
cumsum(randn(50))
|
||||
end
|
||||
|
||||
@series begin
|
||||
subplot := 2
|
||||
seriestype := :scatter
|
||||
palette := palette
|
||||
marker := (:circle, :diamond, :star5, :square)[j]
|
||||
randn(10), randn(10)
|
||||
end
|
||||
end
|
||||
|
||||
@series begin
|
||||
subplot := 3
|
||||
seriestype := :histogram
|
||||
palette := palette
|
||||
randn(1000) .+ (0:2:4)'
|
||||
end
|
||||
|
||||
f(r) = sin(r) / r
|
||||
_norm(x, y) = norm([x, y])
|
||||
x = y = range(-3π, stop = 3π, length = 30)
|
||||
z = f.(_norm.(x, y'))
|
||||
wi = 2:3:30
|
||||
|
||||
@series begin
|
||||
subplot := 4
|
||||
seriestype := :heatmap
|
||||
seriescolor := gradient
|
||||
ticks := -5:5:5
|
||||
x, y, z
|
||||
end
|
||||
|
||||
@series begin
|
||||
subplot := 5
|
||||
seriestype := :surface
|
||||
seriescolor := gradient
|
||||
x, y, z
|
||||
end
|
||||
|
||||
n = 100
|
||||
ts = range(0, stop = 10π, length = n)
|
||||
x = ts .* cos.(ts)
|
||||
y = (0.1ts) .* sin.(ts)
|
||||
z = 1:n
|
||||
|
||||
@series begin
|
||||
subplot := 6
|
||||
seriescolor := gradient
|
||||
linewidth := 3
|
||||
line_z := z
|
||||
x, y, z
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+15
-14
@@ -5,31 +5,32 @@
|
||||
const AVec = AbstractVector
|
||||
const AMat = AbstractMatrix
|
||||
const KW = Dict{Symbol,Any}
|
||||
const TicksArgs = Union{AVec{T}, Tuple{AVec{T}, AVec{S}}, Symbol} where {T<:Real, S<:AbstractString}
|
||||
|
||||
immutable PlotsDisplay <: Display end
|
||||
struct PlotsDisplay <: AbstractDisplay end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
immutable InputWrapper{T}
|
||||
struct InputWrapper{T}
|
||||
obj::T
|
||||
end
|
||||
wrap{T}(obj::T) = InputWrapper{T}(obj)
|
||||
wrap(obj::T) where {T} = InputWrapper{T}(obj)
|
||||
Base.isempty(wrapper::InputWrapper) = false
|
||||
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
type Series
|
||||
d::KW
|
||||
mutable struct Series
|
||||
plotattributes::KW
|
||||
end
|
||||
|
||||
attr(series::Series, k::Symbol) = series.d[k]
|
||||
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
|
||||
attr(series::Series, k::Symbol) = series.plotattributes[k]
|
||||
attr!(series::Series, v, k::Symbol) = (series.plotattributes[k] = v)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# a single subplot
|
||||
type Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
series_list::Vector{Series} # arguments for each series
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
@@ -45,12 +46,12 @@ 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
|
||||
type Axis
|
||||
mutable struct Axis
|
||||
sps::Vector{Subplot}
|
||||
d::KW
|
||||
plotattributes::KW
|
||||
end
|
||||
|
||||
type Extrema
|
||||
mutable struct Extrema
|
||||
emin::Float64
|
||||
emax::Float64
|
||||
end
|
||||
@@ -63,7 +64,7 @@ const SubplotMap = Dict{Any, Subplot}
|
||||
# -----------------------------------------------------------
|
||||
|
||||
|
||||
type Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
backend::T # the backend type
|
||||
n::Int # number of series
|
||||
attr::KW # arguments for the whole plot
|
||||
@@ -87,7 +88,7 @@ end
|
||||
|
||||
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
Base.length(plt::Plot) = length(plt.subplots)
|
||||
Base.endof(plt::Plot) = length(plt)
|
||||
Base.lastindex(plt::Plot) = length(plt)
|
||||
|
||||
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
|
||||
Base.size(plt::Plot) = size(plt.layout)
|
||||
@@ -98,6 +99,6 @@ Base.ndims(plt::Plot) = 2
|
||||
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
|
||||
|
||||
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
|
||||
Base.endof(sp::Subplot) = length(series_list(sp))
|
||||
Base.lastindex(sp::Subplot) = length(series_list(sp))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
+425
-138
@@ -4,7 +4,7 @@ calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for
|
||||
"Make histogram-like bins of data"
|
||||
function binData(data, nbins)
|
||||
lo, hi = ignorenan_extrema(data)
|
||||
edges = collect(linspace(lo, hi, nbins+1))
|
||||
edges = collect(range(lo, stop=hi, length=nbins+1))
|
||||
midpoints = calcMidpoints(edges)
|
||||
buckets = Int[max(2, min(searchsortedfirst(edges, x), length(edges)))-1 for x in data]
|
||||
counts = zeros(Int, length(midpoints))
|
||||
@@ -19,15 +19,15 @@ A hacky replacement for a histogram when the backend doesn't support histograms
|
||||
Convert it into a bar chart with the appropriate x/y values.
|
||||
"""
|
||||
function histogramHack(; kw...)
|
||||
d = KW(kw)
|
||||
plotattributes = KW(kw)
|
||||
|
||||
# we assume that the y kwarg is set with the data to be binned, and nbins is also defined
|
||||
edges, midpoints, buckets, counts = binData(d[:y], d[:bins])
|
||||
d[:x] = midpoints
|
||||
d[:y] = float(counts)
|
||||
d[:seriestype] = :bar
|
||||
d[:fillrange] = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
|
||||
d
|
||||
edges, midpoints, buckets, counts = binData(plotattributes[:y], plotattributes[:bins])
|
||||
plotattributes[:x] = midpoints
|
||||
plotattributes[:y] = float(counts)
|
||||
plotattributes[:seriestype] = :bar
|
||||
plotattributes[:fillrange] = plotattributes[:fillrange] == nothing ? 0.0 : plotattributes[:fillrange]
|
||||
plotattributes
|
||||
end
|
||||
|
||||
"""
|
||||
@@ -35,10 +35,10 @@ A hacky replacement for a bar graph when the backend doesn't support bars direct
|
||||
Convert it into a line chart with fillrange set.
|
||||
"""
|
||||
function barHack(; kw...)
|
||||
d = KW(kw)
|
||||
midpoints = d[:x]
|
||||
heights = d[:y]
|
||||
fillrange = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
|
||||
plotattributes = KW(kw)
|
||||
midpoints = plotattributes[:x]
|
||||
heights = plotattributes[:y]
|
||||
fillrange = plotattributes[:fillrange] == nothing ? 0.0 : plotattributes[:fillrange]
|
||||
|
||||
# estimate the edges
|
||||
dists = diff(midpoints) * 0.5
|
||||
@@ -62,11 +62,11 @@ function barHack(; kw...)
|
||||
append!(y, [fillrange, heights[i], heights[i], fillrange])
|
||||
end
|
||||
|
||||
d[:x] = x
|
||||
d[:y] = y
|
||||
d[:seriestype] = :path
|
||||
d[:fillrange] = fillrange
|
||||
d
|
||||
plotattributes[:x] = x
|
||||
plotattributes[:y] = y
|
||||
plotattributes[:seriestype] = :path
|
||||
plotattributes[:fillrange] = fillrange
|
||||
plotattributes
|
||||
end
|
||||
|
||||
|
||||
@@ -75,33 +75,33 @@ A hacky replacement for a sticks graph when the backend doesn't support sticks d
|
||||
Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points.
|
||||
"""
|
||||
function sticksHack(; kw...)
|
||||
dLine = KW(kw)
|
||||
dScatter = copy(dLine)
|
||||
plotattributesLine = KW(kw)
|
||||
plotattributesScatter = copy(plotattributesLine)
|
||||
|
||||
# these are the line vertices
|
||||
x = Float64[]
|
||||
y = Float64[]
|
||||
fillrange = dLine[:fillrange] == nothing ? 0.0 : dLine[:fillrange]
|
||||
fillrange = plotattributesLine[:fillrange] == nothing ? 0.0 : plotattributesLine[:fillrange]
|
||||
|
||||
# calculate the vertices
|
||||
yScatter = dScatter[:y]
|
||||
for (i,xi) in enumerate(dScatter[:x])
|
||||
yScatter = plotattributesScatter[:y]
|
||||
for (i,xi) in enumerate(plotattributesScatter[:x])
|
||||
yi = yScatter[i]
|
||||
for j in 1:3 push!(x, xi) end
|
||||
append!(y, [fillrange, yScatter[i], fillrange])
|
||||
end
|
||||
|
||||
# change the line args
|
||||
dLine[:x] = x
|
||||
dLine[:y] = y
|
||||
dLine[:seriestype] = :path
|
||||
dLine[:markershape] = :none
|
||||
dLine[:fillrange] = nothing
|
||||
plotattributesLine[:x] = x
|
||||
plotattributesLine[:y] = y
|
||||
plotattributesLine[:seriestype] = :path
|
||||
plotattributesLine[:markershape] = :none
|
||||
plotattributesLine[:fillrange] = nothing
|
||||
|
||||
# change the scatter args
|
||||
dScatter[:seriestype] = :none
|
||||
plotattributesScatter[:seriestype] = :none
|
||||
|
||||
dLine, dScatter
|
||||
plotattributesLine, plotattributesScatter
|
||||
end
|
||||
|
||||
function regressionXY(x, y)
|
||||
@@ -114,12 +114,12 @@ function regressionXY(x, y)
|
||||
regx, regy
|
||||
end
|
||||
|
||||
function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
|
||||
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
|
||||
@show T, size(z)
|
||||
n, m = size(z)
|
||||
# idx = 0
|
||||
colors = ColorGradient(vec(z))
|
||||
newz = reshape(linspace(0, 1, n*m), n, m)
|
||||
newz = reshape(range(0, stop=1, length=n*m), n, m)
|
||||
newz, colors
|
||||
# newz = zeros(n, m)
|
||||
# for i=1:n, j=1:m
|
||||
@@ -130,22 +130,22 @@ function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
|
||||
# newz, ColorGradient(colors)
|
||||
end
|
||||
|
||||
function imageHack(d::KW)
|
||||
function imageHack(plotattributes::KW)
|
||||
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
|
||||
d[:seriestype] = :heatmap
|
||||
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
|
||||
plotattributes[:seriestype] = :heatmap
|
||||
plotattributes[:z], plotattributes[:fillcolor] = replace_image_with_heatmap(plotattributes[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
"Build line segments for plotting"
|
||||
type Segments{T}
|
||||
mutable struct Segments{T}
|
||||
pts::Vector{T}
|
||||
end
|
||||
|
||||
# Segments() = Segments{Float64}(zeros(0))
|
||||
|
||||
Segments() = Segments(Float64)
|
||||
Segments{T}(::Type{T}) = Segments(T[])
|
||||
Segments(::Type{T}) where {T} = Segments(T[])
|
||||
Segments(p::Int) = Segments(NTuple{2,Float64}[])
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
|
||||
coords(segs::Segments{Float64}) = segs.pts
|
||||
coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
|
||||
|
||||
function Base.push!{T}(segments::Segments{T}, vs...)
|
||||
function Base.push!(segments::Segments{T}, vs...) where T
|
||||
if !isempty(segments.pts)
|
||||
push!(segments.pts, to_nan(T))
|
||||
end
|
||||
@@ -167,7 +167,7 @@ function Base.push!{T}(segments::Segments{T}, vs...)
|
||||
segments
|
||||
end
|
||||
|
||||
function Base.push!{T}(segments::Segments{T}, vs::AVec)
|
||||
function Base.push!(segments::Segments{T}, vs::AVec) where T
|
||||
if !isempty(segments.pts)
|
||||
push!(segments.pts, to_nan(T))
|
||||
end
|
||||
@@ -181,7 +181,7 @@ end
|
||||
# -----------------------------------------------------
|
||||
# helper to manage NaN-separated segments
|
||||
|
||||
type SegmentsIterator
|
||||
mutable struct SegmentsIterator
|
||||
args::Tuple
|
||||
n::Int
|
||||
end
|
||||
@@ -192,20 +192,35 @@ function iter_segments(args...)
|
||||
SegmentsIterator(tup, n)
|
||||
end
|
||||
|
||||
function iter_segments(series::Series)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
if has_attribute_segments(series)
|
||||
if series[:seriestype] in (:scatter, :scatter3d)
|
||||
return [[i] for i in 1:length(y)]
|
||||
else
|
||||
return [i:(i + 1) for i in 1:(length(y) - 1)]
|
||||
end
|
||||
else
|
||||
segs = UnitRange{Int}[]
|
||||
args = is3d(series) ? (x, y, z) : (x, y)
|
||||
for seg in iter_segments(args...)
|
||||
push!(segs, seg)
|
||||
end
|
||||
return segs
|
||||
end
|
||||
end
|
||||
|
||||
# helpers to figure out if there are NaN values in a list of array types
|
||||
anynan(i::Int, args::Tuple) = any(a -> !isfinite(_cycle(a,i)), args)
|
||||
anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args)
|
||||
anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend)
|
||||
|
||||
function Base.start(itr::SegmentsIterator)
|
||||
nextidx = 1
|
||||
if anynan(1, itr.args)
|
||||
_, nextidx = next(itr, 1)
|
||||
function Base.iterate(itr::SegmentsIterator, nextidx::Int = 1)
|
||||
nextidx > itr.n && return nothing
|
||||
if nextidx == 1 && !any(isempty,itr.args) && anynan(1, itr.args)
|
||||
nextidx = 2
|
||||
end
|
||||
nextidx
|
||||
end
|
||||
Base.done(itr::SegmentsIterator, nextidx::Int) = nextidx > itr.n
|
||||
function Base.next(itr::SegmentsIterator, nextidx::Int)
|
||||
|
||||
i = istart = iend = nextidx
|
||||
|
||||
# find the next NaN, and iend is the one before
|
||||
@@ -232,8 +247,8 @@ end
|
||||
# Find minimal type that can contain NaN and x
|
||||
# To allow use of NaN separated segments with categorical x axis
|
||||
|
||||
float_extended_type{T}(x::AbstractArray{T}) = Union{T,Float64}
|
||||
float_extended_type{T<:Real}(x::AbstractArray{T}) = Float64
|
||||
float_extended_type(x::AbstractArray{T}) where {T} = Union{T,Float64}
|
||||
float_extended_type(x::AbstractArray{T}) where {T<:Real} = Float64
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
@@ -241,7 +256,7 @@ float_extended_type{T<:Real}(x::AbstractArray{T}) = Float64
|
||||
nop() = nothing
|
||||
notimpl() = error("This has not been implemented yet")
|
||||
|
||||
isnothing(x::Void) = true
|
||||
isnothing(x::Nothing) = true
|
||||
isnothing(x) = false
|
||||
|
||||
_cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||
@@ -259,27 +274,27 @@ _cycle(grad::ColorGradient, idx::Int) = _cycle(grad.colors, idx)
|
||||
_cycle(grad::ColorGradient, indices::AVec{Int}) = _cycle(grad.colors, indices)
|
||||
|
||||
makevec(v::AVec) = v
|
||||
makevec{T}(v::T) = T[v]
|
||||
makevec(v::T) where {T} = T[v]
|
||||
|
||||
"duplicate a single value, or pass the 2-tuple through"
|
||||
maketuple(x::Real) = (x,x)
|
||||
maketuple{T,S}(x::Tuple{T,S}) = x
|
||||
maketuple(x::Tuple{T,S}) where {T,S} = x
|
||||
|
||||
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
||||
mapFuncOrFuncs{F<:Function}(fs::AVec{F}, u::AVec) = [map(f, u) for f in fs]
|
||||
mapFuncOrFuncs(fs::AVec{F}, u::AVec) where {F<:Function} = [map(f, u) for f in fs]
|
||||
|
||||
unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy]
|
||||
unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
|
||||
unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
|
||||
unzip(xy::AVec{Tuple{X,Y}}) where {X,Y} = [t[1] for t in xy], [t[2] for t in xy]
|
||||
unzip(xyz::AVec{Tuple{X,Y,Z}}) where {X,Y,Z} = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
|
||||
unzip(xyuv::AVec{Tuple{X,Y,U,V}}) where {X,Y,U,V} = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
|
||||
|
||||
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
|
||||
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
|
||||
unzip(xy::AVec{FixedSizeArrays.Vec{2,T}}) where {T} = T[t[1] for t in xy], T[t[2] for t in xy]
|
||||
unzip(xy::FixedSizeArrays.Vec{2,T}) where {T} = T[xy[1]], T[xy[2]]
|
||||
|
||||
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
|
||||
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
|
||||
unzip(xyz::AVec{FixedSizeArrays.Vec{3,T}}) where {T} = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
|
||||
unzip(xyz::FixedSizeArrays.Vec{3,T}) where {T} = T[xyz[1]], T[xyz[2]], T[xyz[3]]
|
||||
|
||||
unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
|
||||
unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
|
||||
unzip(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) where {T} = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
|
||||
unzip(xyuv::FixedSizeArrays.Vec{4,T}) where {T} = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
|
||||
|
||||
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
||||
function _expand_limits(lims, x)
|
||||
@@ -288,7 +303,8 @@ function _expand_limits(lims, x)
|
||||
lims[1] = NaNMath.min(lims[1], e1)
|
||||
lims[2] = NaNMath.max(lims[2], e2)
|
||||
# catch err
|
||||
# warn(err)
|
||||
# @warn(err)
|
||||
catch
|
||||
end
|
||||
nothing
|
||||
end
|
||||
@@ -312,47 +328,46 @@ function replaceType(vec, val)
|
||||
push!(vec, val)
|
||||
end
|
||||
|
||||
function replaceAlias!(d::KW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
||||
function replaceAlias!(plotattributes::KW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = pop!(d, k)
|
||||
plotattributes[aliases[k]] = pop!(plotattributes, k)
|
||||
end
|
||||
end
|
||||
|
||||
function replaceAliases!(d::KW, aliases::Dict{Symbol,Symbol})
|
||||
ks = collect(keys(d))
|
||||
function replaceAliases!(plotattributes::KW, aliases::Dict{Symbol,Symbol})
|
||||
ks = collect(keys(plotattributes))
|
||||
for k in ks
|
||||
replaceAlias!(d, k, aliases)
|
||||
replaceAlias!(plotattributes, k, aliases)
|
||||
end
|
||||
end
|
||||
|
||||
createSegments(z) = collect(repmat(reshape(z,1,:),2,1))[2:end]
|
||||
createSegments(z) = collect(repeat(reshape(z,1,:),2,1))[2:end]
|
||||
|
||||
Base.first(c::Colorant) = c
|
||||
Base.first(x::Symbol) = x
|
||||
|
||||
|
||||
sortedkeys(d::Dict) = sort(collect(keys(d)))
|
||||
sortedkeys(plotattributes::Dict) = sort(collect(keys(plotattributes)))
|
||||
|
||||
|
||||
const _scale_base = Dict{Symbol, Real}(
|
||||
:log10 => 10,
|
||||
:log2 => 2,
|
||||
:ln => e,
|
||||
:ln => ℯ,
|
||||
)
|
||||
|
||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||
function heatmap_edges(v::AVec, scale::Symbol = :identity)
|
||||
function _heatmap_edges(v::AVec)
|
||||
vmin, vmax = ignorenan_extrema(v)
|
||||
extra_min = extra_max = 0.5 * (vmax-vmin) / (length(v)-1)
|
||||
if scale in _logScales
|
||||
vmin > 0 || error("The axis values must be positive for a $scale scale")
|
||||
while vmin - extra_min <= 0
|
||||
extra_min /= _scale_base[scale]
|
||||
end
|
||||
end
|
||||
extra_min = (v[2] - v[1]) / 2
|
||||
extra_max = (v[end] - v[end - 1]) / 2
|
||||
vcat(vmin-extra_min, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra_max)
|
||||
end
|
||||
|
||||
"create an (n+1) list of the outsides of heatmap rectangles"
|
||||
function heatmap_edges(v::AVec, scale::Symbol = :identity)
|
||||
f, invf = scalefunc(scale), invscalefunc(scale)
|
||||
map(invf, _heatmap_edges(map(f,v)))
|
||||
end
|
||||
|
||||
function calc_r_extrema(x, y)
|
||||
xmin, xmax = ignorenan_extrema(x)
|
||||
@@ -363,15 +378,26 @@ end
|
||||
|
||||
function convert_to_polar(x, y, r_extrema = calc_r_extrema(x, y))
|
||||
rmin, rmax = r_extrema
|
||||
phi, r = x, y
|
||||
r = (r - rmin) / (rmax - rmin)
|
||||
n = max(length(phi), length(r))
|
||||
x = zeros(n)
|
||||
y = zeros(n)
|
||||
theta, r = filter_radial_data(x, y, r_extrema)
|
||||
r = (r .- rmin) ./ (rmax .- rmin)
|
||||
x = r.*cos.(theta)
|
||||
y = r.*sin.(theta)
|
||||
x, y
|
||||
end
|
||||
|
||||
# Filters radial data for points within the axis limits
|
||||
function filter_radial_data(theta, r, r_extrema::Tuple{Real, Real})
|
||||
n = max(length(theta), length(r))
|
||||
rmin, rmax = r_extrema
|
||||
x, y = zeros(n), zeros(n)
|
||||
for i in 1:n
|
||||
x[i] = _cycle(r,i) * cos.(_cycle(phi,i))
|
||||
y[i] = _cycle(r,i) * sin.(_cycle(phi,i))
|
||||
x[i] = _cycle(theta, i)
|
||||
y[i] = _cycle(r, i)
|
||||
end
|
||||
points = map((a, b) -> (a, b), x, y)
|
||||
filter!(a -> a[2] >= rmin && a[2] <= rmax, points)
|
||||
x = map(a -> a[1], points)
|
||||
y = map(a -> a[2], points)
|
||||
x, y
|
||||
end
|
||||
|
||||
@@ -383,12 +409,12 @@ function fakedata(sz...)
|
||||
y
|
||||
end
|
||||
|
||||
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited
|
||||
isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected()
|
||||
isijulia() = :IJulia in nameof.(collect(values(Base.loaded_modules)))
|
||||
isatom() = :Atom in nameof.(collect(values(Base.loaded_modules)))
|
||||
|
||||
function is_installed(pkgstr::AbstractString)
|
||||
try
|
||||
Pkg.installed(pkgstr) === nothing ? false: true
|
||||
Pkg.installed(pkgstr) === nothing ? false : true
|
||||
catch
|
||||
false
|
||||
end
|
||||
@@ -406,24 +432,24 @@ isscalar(::Any) = false
|
||||
is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
|
||||
|
||||
|
||||
isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
|
||||
isvertical(series::Series) = isvertical(series.d)
|
||||
isvertical(plotattributes::KW) = get(plotattributes, :orientation, :vertical) in (:vertical, :v, :vert)
|
||||
isvertical(series::Series) = isvertical(series.plotattributes)
|
||||
|
||||
|
||||
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
|
||||
ticksType{T<:AbstractString}(ticks::AVec{T}) = :labels
|
||||
ticksType{T<:AVec,S<:AVec}(ticks::Tuple{T,S}) = :ticks_and_labels
|
||||
ticksType(ticks::AVec{T}) where {T<:Real} = :ticks
|
||||
ticksType(ticks::AVec{T}) where {T<:AbstractString} = :labels
|
||||
ticksType(ticks::Tuple{T,S}) where {T<:AVec,S<:AVec} = :ticks_and_labels
|
||||
ticksType(ticks) = :invalid
|
||||
|
||||
limsType{T<:Real,S<:Real}(lims::Tuple{T,S}) = :limits
|
||||
limsType(lims::Tuple{T,S}) where {T<:Real,S<:Real} = :limits
|
||||
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
|
||||
limsType(lims) = :invalid
|
||||
|
||||
# axis_Symbol(letter, postfix) = Symbol(letter * postfix)
|
||||
# axis_symbols(letter, postfix...) = map(s -> axis_Symbol(letter, s), postfix)
|
||||
|
||||
Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng]
|
||||
Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]
|
||||
Base.convert(::Type{Vector{T}}, rng::AbstractRange{T}) where {T<:Real} = T[x for x in rng]
|
||||
Base.convert(::Type{Vector{T}}, rng::AbstractRange{S}) where {T<:Real,S<:Real} = T[x for x in rng]
|
||||
|
||||
Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
|
||||
|
||||
@@ -466,8 +492,8 @@ end
|
||||
# this is a helper function to determine whether we need to transpose a surface matrix.
|
||||
# it depends on whether the backend matches rows to x (transpose_on_match == true) or vice versa
|
||||
# for example: PyPlot sends rows to y, so transpose_on_match should be true
|
||||
function transpose_z(d, z, transpose_on_match::Bool = true)
|
||||
if d[:match_dimensions] == transpose_on_match
|
||||
function transpose_z(plotattributes, z, transpose_on_match::Bool = true)
|
||||
if plotattributes[:match_dimensions] == transpose_on_match
|
||||
# z'
|
||||
permutedims(z, [2,1])
|
||||
else
|
||||
@@ -539,6 +565,124 @@ xlims(sp_idx::Int = 1) = xlims(current(), sp_idx)
|
||||
ylims(sp_idx::Int = 1) = ylims(current(), sp_idx)
|
||||
zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
||||
|
||||
|
||||
function get_clims(sp::Subplot)
|
||||
zmin, zmax = Inf, -Inf
|
||||
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface)
|
||||
for series in series_list(sp)
|
||||
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
||||
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Real)
|
||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals.surf)...)
|
||||
elseif (vals != nothing) && (eltype(vals) <: Real)
|
||||
zmin, zmax = _update_clims(zmin, zmax, ignorenan_extrema(vals)...)
|
||||
end
|
||||
end
|
||||
end
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
return zmin < zmax ? (zmin, zmax) : (-0.1, 0.1)
|
||||
end
|
||||
|
||||
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
|
||||
|
||||
function hascolorbar(series::Series)
|
||||
st = series[:seriestype]
|
||||
hascbar = st == :heatmap
|
||||
if st == :contour
|
||||
hascbar = (isscalar(series[:levels]) ? (series[:levels] > 1) : (length(series[:levels]) > 1)) && (length(unique(Array(series[:z]))) > 1)
|
||||
end
|
||||
if series[:marker_z] != nothing || series[:line_z] != nothing || series[:fill_z] != nothing
|
||||
hascbar = true
|
||||
end
|
||||
# no colorbar if we are creating a surface LightSource
|
||||
if xor(st == :surface, series[:fill_z] != nothing)
|
||||
hascbar = true
|
||||
end
|
||||
return hascbar
|
||||
end
|
||||
|
||||
function hascolorbar(sp::Subplot)
|
||||
cbar = sp[:colorbar]
|
||||
hascbar = false
|
||||
if cbar != :none
|
||||
for series in series_list(sp)
|
||||
if hascolorbar(series)
|
||||
hascbar = true
|
||||
end
|
||||
end
|
||||
end
|
||||
hascbar
|
||||
end
|
||||
|
||||
for comp in (:line, :fill, :marker)
|
||||
|
||||
compcolor = string(comp, :color)
|
||||
get_compcolor = Symbol(:get_, compcolor)
|
||||
comp_z = string(comp, :_z)
|
||||
|
||||
compalpha = string(comp, :alpha)
|
||||
get_compalpha = Symbol(:get_, compalpha)
|
||||
|
||||
@eval begin
|
||||
|
||||
function $get_compcolor(series, cmin::Real, cmax::Real, i::Int = 1)
|
||||
c = series[$Symbol($compcolor)]
|
||||
z = series[$Symbol($comp_z)]
|
||||
if z == nothing
|
||||
isa(c, ColorGradient) ? c : plot_color(_cycle(c, i))
|
||||
else
|
||||
grad = isa(c, ColorGradient) ? c : cgrad()
|
||||
grad[clamp((_cycle(z, i) - cmin) / (cmax - cmin), 0, 1)]
|
||||
end
|
||||
end
|
||||
|
||||
$get_compcolor(series, clims, i::Int = 1) = $get_compcolor(series, clims[1], clims[2], i)
|
||||
|
||||
function $get_compcolor(series, i::Int = 1)
|
||||
if series[$Symbol($comp_z)] == nothing
|
||||
$get_compcolor(series, 0, 1, i)
|
||||
else
|
||||
$get_compcolor(series, get_clims(series[:subplot]), i)
|
||||
end
|
||||
end
|
||||
|
||||
$get_compalpha(series, i::Int = 1) = _cycle(series[$Symbol($compalpha)], i)
|
||||
end
|
||||
end
|
||||
|
||||
function get_linewidth(series, i::Int = 1)
|
||||
_cycle(series[:linewidth], i)
|
||||
end
|
||||
|
||||
function get_linestyle(series, i::Int = 1)
|
||||
_cycle(series[:linestyle], i)
|
||||
end
|
||||
|
||||
function get_markerstrokecolor(series, i::Int = 1)
|
||||
msc = series[:markerstrokecolor]
|
||||
isa(msc, ColorGradient) ? msc : _cycle(msc, i)
|
||||
end
|
||||
|
||||
function get_markerstrokealpha(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokealpha], i)
|
||||
end
|
||||
|
||||
function has_attribute_segments(series::Series)
|
||||
# we want to check if a series needs to be split into segments just because
|
||||
# of its attributes
|
||||
for letter in (:x, :y, :z)
|
||||
# If we have NaNs in the data they define the segments and
|
||||
# SegmentsIterator is used
|
||||
series[letter] != nothing && NaN in collect(series[letter]) && return false
|
||||
end
|
||||
series[:seriestype] == :shape && return false
|
||||
# ... else we check relevant attributes if they have multiple inputs
|
||||
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray{<:Real} for attr in (:line_z, :fill_z, :marker_z))
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
@@ -650,7 +794,7 @@ end
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
type DebugMode
|
||||
mutable struct DebugMode
|
||||
on::Bool
|
||||
end
|
||||
const _debugMode = DebugMode(false)
|
||||
@@ -662,20 +806,20 @@ end
|
||||
debugshow(x) = show(x)
|
||||
debugshow(x::AbstractArray) = print(summary(x))
|
||||
|
||||
function dumpdict(d::KW, prefix = "", alwaysshow = false)
|
||||
function dumpdict(plotattributes::KW, prefix = "", alwaysshow = false)
|
||||
_debugMode.on || alwaysshow || return
|
||||
println()
|
||||
if prefix != ""
|
||||
println(prefix, ":")
|
||||
end
|
||||
for k in sort(collect(keys(d)))
|
||||
for k in sort(collect(keys(plotattributes)))
|
||||
@printf("%14s: ", k)
|
||||
debugshow(d[k])
|
||||
debugshow(plotattributes[k])
|
||||
println()
|
||||
end
|
||||
println()
|
||||
end
|
||||
DD(d::KW, prefix = "") = dumpdict(d, prefix, true)
|
||||
DD(plotattributes::KW, prefix = "") = dumpdict(plotattributes, prefix, true)
|
||||
|
||||
|
||||
function dumpcallstack()
|
||||
@@ -688,43 +832,43 @@ end
|
||||
|
||||
extendSeriesByOne(v::UnitRange{Int}, n::Int = 1) = isempty(v) ? (1:n) : (minimum(v):maximum(v)+n)
|
||||
extendSeriesByOne(v::AVec, n::Integer = 1) = isempty(v) ? (1:n) : vcat(v, (1:n) + ignorenan_maximum(v))
|
||||
extendSeriesData{T}(v::Range{T}, z::Real) = extendSeriesData(float(collect(v)), z)
|
||||
extendSeriesData{T}(v::Range{T}, z::AVec) = extendSeriesData(float(collect(v)), z)
|
||||
extendSeriesData{T}(v::AVec{T}, z::Real) = (push!(v, convert(T, z)); v)
|
||||
extendSeriesData{T}(v::AVec{T}, z::AVec) = (append!(v, convert(Vector{T}, z)); v)
|
||||
extendSeriesData(v::AbstractRange{T}, z::Real) where {T} = extendSeriesData(float(collect(v)), z)
|
||||
extendSeriesData(v::AbstractRange{T}, z::AVec) where {T} = extendSeriesData(float(collect(v)), z)
|
||||
extendSeriesData(v::AVec{T}, z::Real) where {T} = (push!(v, convert(T, z)); v)
|
||||
extendSeriesData(v::AVec{T}, z::AVec) where {T} = (append!(v, convert(Vector{T}, z)); v)
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
# NOTE: backends should implement the following methods to get/set the x/y/z data objects
|
||||
|
||||
tovec(v::AbstractVector) = v
|
||||
tovec(v::Void) = zeros(0)
|
||||
tovec(v::Nothing) = zeros(0)
|
||||
|
||||
function getxy(plt::Plot, i::Integer)
|
||||
d = plt.series_list[i].d
|
||||
tovec(d[:x]), tovec(d[:y])
|
||||
plotattributes = plt.series_list[i].plotattributes
|
||||
tovec(plotattributes[:x]), tovec(plotattributes[:y])
|
||||
end
|
||||
function getxyz(plt::Plot, i::Integer)
|
||||
d = plt.series_list[i].d
|
||||
tovec(d[:x]), tovec(d[:y]), tovec(d[:z])
|
||||
plotattributes = plt.series_list[i].plotattributes
|
||||
tovec(plotattributes[:x]), tovec(plotattributes[:y]), tovec(plotattributes[:z])
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
|
||||
function setxy!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y}
|
||||
series = plt.series_list[i]
|
||||
series.d[:x], series.d[:y] = xy
|
||||
sp = series.d[:subplot]
|
||||
series.plotattributes[:x], series.plotattributes[:y] = xy
|
||||
sp = series.plotattributes[:subplot]
|
||||
reset_extrema!(sp)
|
||||
_series_updated(plt, series)
|
||||
end
|
||||
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
|
||||
function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z}
|
||||
series = plt.series_list[i]
|
||||
series.d[:x], series.d[:y], series.d[:z] = xyz
|
||||
sp = series.d[:subplot]
|
||||
series.plotattributes[:x], series.plotattributes[:y], series.plotattributes[:z] = xyz
|
||||
sp = series.plotattributes[:subplot]
|
||||
reset_extrema!(sp)
|
||||
_series_updated(plt, series)
|
||||
end
|
||||
|
||||
function setxyz!{X,Y,Z<:AbstractMatrix}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
|
||||
function setxyz!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z<:AbstractMatrix}
|
||||
setxyz!(plt, (xyz[1], xyz[2], Surface(xyz[3])), i)
|
||||
end
|
||||
|
||||
@@ -733,8 +877,8 @@ end
|
||||
# indexing notation
|
||||
|
||||
# Base.getindex(plt::Plot, i::Integer) = getxy(plt, i)
|
||||
Base.setindex!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer) = (setxy!(plt, xy, i); plt)
|
||||
Base.setindex!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) = (setxyz!(plt, xyz, i); plt)
|
||||
Base.setindex!(plt::Plot, xy::Tuple{X,Y}, i::Integer) where {X,Y} = (setxy!(plt, xy, i); plt)
|
||||
Base.setindex!(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer) where {X,Y,Z} = (setxyz!(plt, xyz, i); plt)
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
@@ -768,13 +912,13 @@ Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi)
|
||||
# -------------------------------------------------------
|
||||
|
||||
function attr!(series::Series; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
for (k,v) in d
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
for (k,v) in plotattributes
|
||||
if haskey(_series_defaults, k)
|
||||
series[k] = v
|
||||
else
|
||||
warn("unused key $k in series attr")
|
||||
@warn("unused key $k in series attr")
|
||||
end
|
||||
end
|
||||
_series_updated(series[:subplot].plt, series)
|
||||
@@ -782,13 +926,13 @@ function attr!(series::Series; kw...)
|
||||
end
|
||||
|
||||
function attr!(sp::Subplot; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
for (k,v) in d
|
||||
plotattributes = KW(kw)
|
||||
preprocessArgs!(plotattributes)
|
||||
for (k,v) in plotattributes
|
||||
if haskey(_subplot_defaults, k)
|
||||
sp[k] = v
|
||||
else
|
||||
warn("unused key $k in subplot attr")
|
||||
@warn("unused key $k in subplot attr")
|
||||
end
|
||||
end
|
||||
sp
|
||||
@@ -846,10 +990,10 @@ function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec, z::AVec)
|
||||
end
|
||||
|
||||
# tuples
|
||||
Base.push!{X,Y}(plt::Plot, xy::Tuple{X,Y}) = push!(plt, 1, xy...)
|
||||
Base.push!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}) = push!(plt, 1, xyz...)
|
||||
Base.push!{X,Y}(plt::Plot, i::Integer, xy::Tuple{X,Y}) = push!(plt, i, xy...)
|
||||
Base.push!{X,Y,Z}(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) = push!(plt, i, xyz...)
|
||||
Base.push!(plt::Plot, xy::Tuple{X,Y}) where {X,Y} = push!(plt, 1, xy...)
|
||||
Base.push!(plt::Plot, xyz::Tuple{X,Y,Z}) where {X,Y,Z} = push!(plt, 1, xyz...)
|
||||
Base.push!(plt::Plot, i::Integer, xy::Tuple{X,Y}) where {X,Y} = push!(plt, i, xy...)
|
||||
Base.push!(plt::Plot, i::Integer, xyz::Tuple{X,Y,Z}) where {X,Y,Z} = push!(plt, i, xyz...)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# push/append for all series
|
||||
@@ -913,9 +1057,152 @@ mm2px(mm::Real) = float(px / MM_PER_PX)
|
||||
|
||||
|
||||
"Smallest x in plot"
|
||||
xmin(plt::Plot) = ignorenan_minimum([ignorenan_minimum(series.d[:x]) for series in plt.series_list])
|
||||
xmin(plt::Plot) = ignorenan_minimum([ignorenan_minimum(series.plotattributes[:x]) for series in plt.series_list])
|
||||
"Largest x in plot"
|
||||
xmax(plt::Plot) = ignorenan_maximum([ignorenan_maximum(series.d[:x]) for series in plt.series_list])
|
||||
xmax(plt::Plot) = ignorenan_maximum([ignorenan_maximum(series.plotattributes[:x]) for series in plt.series_list])
|
||||
|
||||
"Extrema of x-values in plot"
|
||||
ignorenan_extrema(plt::Plot) = (xmin(plt), xmax(plt))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# get fonts from objects:
|
||||
|
||||
titlefont(sp::Subplot) = font(
|
||||
sp[:titlefontfamily],
|
||||
sp[:titlefontsize],
|
||||
sp[:titlefontvalign],
|
||||
sp[:titlefonthalign],
|
||||
sp[:titlefontrotation],
|
||||
sp[:titlefontcolor],
|
||||
)
|
||||
|
||||
legendfont(sp::Subplot) = font(
|
||||
sp[:legendfontfamily],
|
||||
sp[:legendfontsize],
|
||||
sp[:legendfontvalign],
|
||||
sp[:legendfonthalign],
|
||||
sp[:legendfontrotation],
|
||||
sp[:legendfontcolor],
|
||||
)
|
||||
|
||||
tickfont(ax::Axis) = font(
|
||||
ax[:tickfontfamily],
|
||||
ax[:tickfontsize],
|
||||
ax[:tickfontvalign],
|
||||
ax[:tickfonthalign],
|
||||
ax[:tickfontrotation],
|
||||
ax[:tickfontcolor],
|
||||
)
|
||||
|
||||
guidefont(ax::Axis) = font(
|
||||
ax[:guidefontfamily],
|
||||
ax[:guidefontsize],
|
||||
ax[:guidefontvalign],
|
||||
ax[:guidefonthalign],
|
||||
ax[:guidefontrotation],
|
||||
ax[:guidefontcolor],
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# converts unicode scientific notation unsupported by pgfplots and gr
|
||||
# into a format that works
|
||||
|
||||
function convert_sci_unicode(label::AbstractString)
|
||||
unicode_dict = Dict(
|
||||
'⁰' => "0",
|
||||
'¹' => "1",
|
||||
'²' => "2",
|
||||
'³' => "3",
|
||||
'⁴' => "4",
|
||||
'⁵' => "5",
|
||||
'⁶' => "6",
|
||||
'⁷' => "7",
|
||||
'⁸' => "8",
|
||||
'⁹' => "9",
|
||||
'⁻' => "-",
|
||||
"×10" => "×10^{",
|
||||
)
|
||||
for key in keys(unicode_dict)
|
||||
label = replace(label, key => unicode_dict[key])
|
||||
end
|
||||
if occursin("10^{", label)
|
||||
label = string(label, "}")
|
||||
end
|
||||
label
|
||||
end
|
||||
|
||||
function straightline_data(series)
|
||||
sp = series[:subplot]
|
||||
xl, yl = isvertical(series) ? (xlims(sp), ylims(sp)) : (ylims(sp), xlims(sp))
|
||||
x, y = series[:x], series[:y]
|
||||
n = length(x)
|
||||
if n == 2
|
||||
return straightline_data(xl, yl, x, y)
|
||||
else
|
||||
k, r = divrem(n, 3)
|
||||
if r == 0
|
||||
xdata, ydata = fill(NaN, n), fill(NaN, n)
|
||||
for i in 1:k
|
||||
inds = (3 * i - 2):(3 * i - 1)
|
||||
xdata[inds], ydata[inds] = straightline_data(xl, yl, x[inds], y[inds])
|
||||
end
|
||||
return xdata, ydata
|
||||
else
|
||||
error("Misformed data. `straightline_data` either accepts vectors of length 2 or 3k. The provided series has length $n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function straightline_data(xl, yl, x, y)
|
||||
x_vals, y_vals = if y[1] == y[2]
|
||||
if x[1] == x[2]
|
||||
error("Two identical points cannot be used to describe a straight line.")
|
||||
else
|
||||
[xl[1], xl[2]], [y[1], y[2]]
|
||||
end
|
||||
elseif x[1] == x[2]
|
||||
[x[1], x[2]], [yl[1], yl[2]]
|
||||
else
|
||||
# get a and b from the line y = a * x + b through the points given by
|
||||
# the coordinates x and x
|
||||
b = y[1] - (y[1] - y[2]) * x[1] / (x[1] - x[2])
|
||||
a = (y[1] - y[2]) / (x[1] - x[2])
|
||||
# get the data values
|
||||
xdata = [clamp(x[1] + (x[1] - x[2]) * (ylim - y[1]) / (y[1] - y[2]), xl...) for ylim in yl]
|
||||
|
||||
xdata, a .* xdata .+ b
|
||||
end
|
||||
# expand the data outside the axis limits, by a certain factor too improve
|
||||
# plotly(js) and interactive behaviour
|
||||
factor = 100
|
||||
x_vals = x_vals .+ (x_vals[2] - x_vals[1]) .* factor .* [-1, 1]
|
||||
y_vals = y_vals .+ (y_vals[2] - y_vals[1]) .* factor .* [-1, 1]
|
||||
return x_vals, y_vals
|
||||
end
|
||||
|
||||
function shape_data(series)
|
||||
sp = series[:subplot]
|
||||
xl, yl = isvertical(series) ? (xlims(sp), ylims(sp)) : (ylims(sp), xlims(sp))
|
||||
x, y = series[:x], series[:y]
|
||||
factor = 100
|
||||
for i in eachindex(x)
|
||||
if x[i] == -Inf
|
||||
x[i] = xl[1] - factor * (xl[2] - xl[1])
|
||||
elseif x[i] == Inf
|
||||
x[i] = xl[2] + factor * (xl[2] - xl[1])
|
||||
end
|
||||
end
|
||||
for i in eachindex(y)
|
||||
if y[i] == -Inf
|
||||
y[i] = yl[1] - factor * (yl[2] - yl[1])
|
||||
elseif y[i] == Inf
|
||||
y[i] = yl[2] + factor * (yl[2] - yl[1])
|
||||
end
|
||||
end
|
||||
return x, y
|
||||
end
|
||||
|
||||
function construct_categorical_data(x::AbstractArray, axis::Axis)
|
||||
map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x)
|
||||
end
|
||||
|
||||
+3
-1
@@ -2,7 +2,9 @@ StatPlots
|
||||
Images
|
||||
ImageMagick
|
||||
@osx QuartzImageIO
|
||||
GR
|
||||
FileIO
|
||||
GR 0.31.0
|
||||
RDatasets
|
||||
VisualRegressionTests
|
||||
UnicodePlots
|
||||
LaTeXStrings
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using Pkg
|
||||
|
||||
# need this to use Conda
|
||||
# ENV["PYTHON"] = ""
|
||||
|
||||
to_add = [
|
||||
PackageSpec(url="https://github.com/JuliaPlots/PlotReferenceImages.jl.git"),
|
||||
# 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()
|
||||
append!(to_add, [
|
||||
PackageSpec(name="FileIO"),
|
||||
PackageSpec(name="ImageMagick"),
|
||||
PackageSpec(name="UnicodePlots"),
|
||||
PackageSpec(name="VisualRegressionTests"),
|
||||
])
|
||||
end
|
||||
|
||||
Pkg.add(to_add)
|
||||
|
||||
Pkg.build("ImageMagick")
|
||||
# Pkg.build("GR")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.build("PyPlot")
|
||||
+22
-16
@@ -2,20 +2,26 @@
|
||||
using VisualRegressionTests
|
||||
# using ExamplePlots
|
||||
|
||||
import DataFrames, RDatasets
|
||||
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
|
||||
# ENV["MPLBACKEND"] = "Agg"
|
||||
# try
|
||||
# @eval import PyPlot
|
||||
# @info("Matplotlib version: $(PyPlot.matplotlib[:__version__])")
|
||||
# end
|
||||
|
||||
|
||||
using Plots
|
||||
using StatPlots
|
||||
using Base.Test
|
||||
# using StatPlots
|
||||
import PlotReferenceImages
|
||||
using Random
|
||||
using Test
|
||||
|
||||
default(size=(500,300))
|
||||
|
||||
@@ -23,22 +29,22 @@ default(size=(500,300))
|
||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
||||
|
||||
const _current_plots_version = v"0.12.3"
|
||||
const _current_plots_version = v"0.17.4"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], tol = 1e-2)
|
||||
Plots._debugMode.on = debug
|
||||
example = Plots._examples[idx]
|
||||
info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
@info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
backend(pkg)
|
||||
backend()
|
||||
|
||||
# ensure consistent results
|
||||
srand(1234)
|
||||
Random.seed!(1234)
|
||||
|
||||
# reference image directory setup
|
||||
# refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
|
||||
refdir = Pkg.dir("PlotReferenceImages", "Plots", string(pkg))
|
||||
refdir = joinpath(dirname(pathof(PlotReferenceImages)), "..", "Plots", string(pkg))
|
||||
fn = "ref$idx.png"
|
||||
|
||||
# firgure out version info
|
||||
@@ -83,7 +89,7 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
|
||||
|
||||
# the test
|
||||
vtest = VisualTest(func, reffn, idx)
|
||||
test_images(vtest, popup=popup, sigma=sigma, eps=eps, newfn = newfn)
|
||||
test_images(vtest, popup=popup, sigma=sigma, tol=tol, newfn = newfn)
|
||||
end
|
||||
|
||||
function image_comparison_facts(pkg::Symbol;
|
||||
@@ -91,11 +97,11 @@ function image_comparison_facts(pkg::Symbol;
|
||||
only = nothing, # limit to these examples (int index)
|
||||
debug = false, # print debug information?
|
||||
sigma = [1,1], # number of pixels to "blur"
|
||||
eps = 1e-2) # acceptable error (percent)
|
||||
tol = 1e-2) # acceptable error (percent)
|
||||
for i in 1:length(Plots._examples)
|
||||
i in skip && continue
|
||||
if only == nothing || i in only
|
||||
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success == true
|
||||
@test image_comparison_tests(pkg, i, debug=debug, sigma=sigma, tol=tol) |> success == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+21
-16
@@ -1,27 +1,29 @@
|
||||
module PlotsTests
|
||||
|
||||
include("add_packages.jl")
|
||||
include("imgcomp.jl")
|
||||
|
||||
# don't actually show the plots
|
||||
srand(1234)
|
||||
Random.seed!(1234)
|
||||
default(show=false, reuse=true)
|
||||
img_eps = isinteractive() ? 1e-2 : 10e-2
|
||||
img_tol = isinteractive() ? 1e-2 : 10e-2
|
||||
|
||||
@testset "GR" begin
|
||||
ENV["PLOTS_TEST"] = "true"
|
||||
ENV["GKSwstype"] = "100"
|
||||
@test gr() == Plots.GRBackend()
|
||||
@test backend() == Plots.GRBackend()
|
||||
|
||||
image_comparison_facts(:gr, eps=img_eps)
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = [25, 30])
|
||||
end
|
||||
|
||||
|
||||
@testset "PyPlot" begin
|
||||
@test pyplot() == Plots.PyPlotBackend()
|
||||
@test backend() == Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, eps=img_eps)
|
||||
end
|
||||
#@testset "PyPlot" begin
|
||||
# @test pyplot() == Plots.PyPlotBackend()
|
||||
# @test backend() == Plots.PyPlotBackend()
|
||||
#
|
||||
# image_comparison_facts(:pyplot, tol=img_tol)
|
||||
#end
|
||||
|
||||
@testset "UnicodePlots" begin
|
||||
@test unicodeplots() == Plots.UnicodePlotsBackend()
|
||||
@@ -38,14 +40,14 @@ end
|
||||
# @test plotlyjs() == Plots.PlotlyJSBackend()
|
||||
# @test backend() == Plots.PlotlyJSBackend()
|
||||
#
|
||||
# if is_linux() && isinteractive()
|
||||
# if Sys.islinux() && isinteractive()
|
||||
# image_comparison_facts(:plotlyjs,
|
||||
# skip=[
|
||||
# 2, # animation (skipped for speed)
|
||||
# 27, # (polar plots) takes very long / not working
|
||||
# 31, # animation (skipped for speed)
|
||||
# ],
|
||||
# eps=img_eps)
|
||||
# tol=img_tol)
|
||||
# end
|
||||
# end
|
||||
|
||||
@@ -69,7 +71,7 @@ end
|
||||
# 28, # heatmap not defined
|
||||
# 31, # animation
|
||||
# ],
|
||||
# eps=img_eps)
|
||||
# tol=img_tol)
|
||||
# end
|
||||
|
||||
|
||||
@@ -78,7 +80,7 @@ end
|
||||
# @test backend() == Plots.PlotlyBackend()
|
||||
#
|
||||
# # # until png generation is reliable on OSX, just test on linux
|
||||
# # @static is_linux() && image_comparison_facts(:plotly, only=[1,3,4,7,8,9,10,11,12,14,15,20,22,23,27], eps=img_eps)
|
||||
# # @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
|
||||
|
||||
|
||||
@@ -87,7 +89,7 @@ end
|
||||
# @test backend() == Plots.ImmerseBackend()
|
||||
#
|
||||
# # as long as we can plot anything without error, it should be the same as Gadfly
|
||||
# image_comparison_facts(:immerse, only=[1], eps=img_eps)
|
||||
# image_comparison_facts(:immerse, only=[1], tol=img_tol)
|
||||
# end
|
||||
|
||||
|
||||
@@ -96,7 +98,7 @@ end
|
||||
# @test backend() == Plots.PlotlyJSBackend()
|
||||
#
|
||||
# # as long as we can plot anything without error, it should be the same as Plotly
|
||||
# image_comparison_facts(:plotlyjs, only=[1], eps=img_eps)
|
||||
# image_comparison_facts(:plotlyjs, only=[1], tol=img_tol)
|
||||
# end
|
||||
|
||||
|
||||
@@ -109,7 +111,7 @@ end
|
||||
# @test plot(sort(rand(10)), rand(Int, 10, 3)) == not(nothing)
|
||||
# @test plot!(rand(10,3), rand(10,3)) == not(nothing)
|
||||
#
|
||||
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
|
||||
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], tol=img_tol)
|
||||
# end
|
||||
|
||||
|
||||
@@ -129,6 +131,9 @@ end
|
||||
@test Plots.ignorenan_extrema(axis) == (0.5, 7.5)
|
||||
end
|
||||
|
||||
@testset "NoFail" begin
|
||||
histogram([1, 0, 0, 0, 0, 0])
|
||||
end
|
||||
|
||||
# tests for preprocessing recipes
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
Pkg.add("ImageMagick")
|
||||
Pkg.build("ImageMagick")
|
||||
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
|
||||
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
|
||||
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
|
||||
|
||||
Pkg.clone("StatPlots")
|
||||
Pkg.checkout("PlotUtils")
|
||||
|
||||
# Pkg.clone("Blink")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.add("Rsvg")
|
||||
# Pkg.add("PlotlyJS")
|
||||
|
||||
# Pkg.checkout("RecipesBase")
|
||||
# Pkg.clone("VisualRegressionTests")
|
||||
|
||||
# need this to use Conda
|
||||
ENV["PYTHON"] = ""
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
|
||||
# Pkg.add("InspectDR")
|
||||
|
||||
Pkg.test("Plots"; coverage=false)
|
||||
Reference in New Issue
Block a user