Compare commits

..

341 Commits

Author SHA1 Message Date
Thomas Breloff 42c3827ded 0.3 fix 2015-10-29 13:26:40 -04:00
Thomas Breloff 26e4b4efda back to 0.3; working on fixes 2015-10-29 12:57:07 -04:00
Thomas Breloff 2169ab3bb8 ref imgs 2015-10-29 12:50:25 -04:00
Thomas Breloff 85dad8db88 removed gitattributes 2015-10-29 11:25:08 -04:00
Thomas Breloff 8120c34064 added smooth and opacity args for pyplot; fixes in regressionXY, replaceAliases 2015-10-28 12:14:58 -04:00
Thomas Breloff 1f1d1f70a1 fix for group arg 2015-10-28 01:37:52 -04:00
Thomas Breloff 373d868a17 gadfly color fixes; pyplot grid support 2015-10-28 01:12:20 -04:00
Thomas Breloff 834ba9bc61 fix imgcmp 2015-10-27 23:37:58 -04:00
Thomas Breloff 3ffec5aed9 fixed colors from subplot changes; fix imgcmp; new refimgs 2015-10-27 23:03:30 -04:00
Thomas Breloff ef20c1d684 subplot initargs fix 2015-10-27 22:08:47 -04:00
Thomas Breloff 697a59568c fixes 2015-10-27 20:08:33 -04:00
Thomas Breloff e58fb935b0 several fixes, plus qwt pos/size 2015-10-27 19:25:27 -04:00
Thomas Breloff 044d23f8a5 working on fixing problems with subplots and contours 2015-10-27 18:03:37 -04:00
Thomas Breloff 1604d867c3 cleared out examples markdown 2015-10-27 13:43:49 -04:00
Thomas Breloff 011db481c3 cleaned out examples and img directories 2015-10-27 13:42:29 -04:00
Thomas Breloff c404cdc38a qwt subplot pos/size 2015-10-27 13:38:16 -04:00
Thomas Breloff 20689af7dd alpha kw for colors; pyplot fix, debugshow 2015-10-27 12:40:38 -04:00
Thomas Breloff 9ea0585d71 support for contours in gadfly/immerse/pyplot 2015-10-26 18:07:22 -04:00
Thomas Breloff 614c5f694b readme 2015-10-26 15:58:26 -04:00
Thomas Breloff fa36aae068 readme 2015-10-26 15:52:44 -04:00
Thomas Breloff 3e85232310 readme 2015-10-26 15:46:34 -04:00
Thomas Breloff 894928d7ca fixes, examples, and readme; also deprecated winston 2015-10-26 14:03:06 -04:00
Thomas Breloff afb1486f86 tests and readme 2015-10-26 13:18:22 -04:00
Thomas Breloff 2799bd18e1 moved refimg dirs 2015-10-26 12:44:16 -04:00
Thomas Breloff 43c2a885b9 travis and 0.3 fixes 2015-10-26 12:25:09 -04:00
Thomas Breloff be7feb0eaa travis and readme 2015-10-26 11:43:30 -04:00
Thomas Breloff e3d653a8d9 tests 2015-10-26 11:38:33 -04:00
Thomas Breloff b9f9d8160d working on tests 2015-10-26 11:28:49 -04:00
Thomas Breloff 141b5c604d pyplot test fixes; pyplot ref imgs; travis fix 2015-10-26 10:52:29 -04:00
Thomas Breloff 8a0fc00435 imgcmp and travis 2015-10-26 09:59:26 -04:00
Thomas Breloff a3a8cb9368 nnet 2015-10-26 01:40:12 -04:00
Thomas Breloff 96c66b33a2 nnet 2015-10-26 00:11:43 -04:00
Thomas Breloff 62fad3724f pyplot push and nnet 2015-10-25 17:08:42 -04:00
Thomas Breloff ca2b6c87ef enabled lfs 2015-10-25 13:53:09 -04:00
Thomas Breloff 20348645a3 pyplot fixes 2015-10-25 00:44:18 -04:00
Thomas Breloff a9b39fad64 pyplot gui fixes and background/foreground fixes with writemime 2015-10-25 00:34:20 -04:00
Thomas Breloff 1c556ade57 more subplot fixes 2015-10-24 22:29:27 -04:00
Thomas Breloff e08957f5ec cleanup and fixes for subplot logic 2015-10-24 20:59:42 -04:00
Thomas Breloff e76ded69e6 some fixes for subplots and imgcmp 2015-10-24 16:11:55 -04:00
Thomas Breloff 7e740861d8 palette fixes and working on imgcmp 2015-10-24 15:09:13 -04:00
Thomas Breloff f28e65a26c meetup 2015-10-24 00:00:26 -04:00
Thomas Breloff a29f0c820b nnet 2015-10-23 17:05:35 -04:00
Thomas Breloff b2bc30cfed qwt fix 2015-10-23 15:58:42 -04:00
Thomas Breloff 6ce235bb68 immerse gui fix 2015-10-23 13:53:13 -04:00
Thomas Breloff 479ab989df immerse subplot gui fix; pyplot colorbars; subplot creation fix 2015-10-23 13:26:34 -04:00
Thomas Breloff 8d490b9e52 meetup nnet 2015-10-22 16:53:39 -04:00
Thomas Breloff 949f11a8c9 meetup 2015-10-22 15:10:15 -04:00
Thomas Breloff d4896e1978 improved color palette generation; removed some predefined gradients 2015-10-22 12:16:00 -04:00
Thomas Breloff c4020080b3 nnet 2015-10-22 00:40:41 -04:00
Thomas Breloff 5a68003d16 fixed empty plots 2015-10-21 21:30:05 -04:00
Thomas Breloff 9fcc3cdc24 pre/post processing subplots 2015-10-21 13:52:58 -04:00
Thomas Breloff ee80ce5ae6 subplots and colors 2015-10-21 12:27:13 -04:00
Thomas Breloff 456c4019c4 misc 2015-10-21 01:34:50 -04:00
Thomas Breloff f65627a505 travis; wine 2015-10-21 00:44:48 -04:00
Thomas Breloff 38d1fc15fe travis; wine 2015-10-21 00:29:53 -04:00
Thomas Breloff 5eead5b0fa travis; wine 2015-10-21 00:21:55 -04:00
Thomas Breloff 4223bd57f1 travis; wine 2015-10-21 00:15:11 -04:00
Thomas Breloff 5bfa653858 travis; wine 2015-10-20 23:57:18 -04:00
Thomas Breloff 5342764caf travis 2015-10-20 23:23:38 -04:00
Thomas Breloff 04a44a82c9 tests 2015-10-20 23:07:47 -04:00
Thomas Breloff c28820f39c colors and tests 2015-10-20 17:11:21 -04:00
Thomas Breloff 546955a78b working on colors and tests 2015-10-20 14:49:01 -04:00
Thomas Breloff d1e9cc5800 palettes notebook 2015-10-20 12:55:36 -04:00
Thomas Breloff 4ef2fb9933 palettes notebook 2015-10-20 12:23:45 -04:00
Thomas Breloff d7bc8adf21 palettes notebook 2015-10-20 12:15:13 -04:00
Thomas Breloff b5f44efc69 palettes notebook 2015-10-20 11:56:06 -04:00
Thomas Breloff 5e8abea222 palettes notebook 2015-10-20 11:53:02 -04:00
Thomas Breloff f291454ccb palettes notebook 2015-10-20 10:40:49 -04:00
Thomas Breloff bac6637b46 messing with colors 2015-10-20 08:24:58 -04:00
Thomas Breloff 866ac9fb72 working on test framework 2015-10-19 17:53:17 -04:00
Thomas Breloff 8208f6947f pdf/ps/svg outputs 2015-10-19 14:11:22 -04:00
Thomas Breloff dcd50a7c62 started work on imgcomp tests 2015-10-19 00:31:43 -04:00
Thomas Breloff 781aa86568 started work on imgcomp tests 2015-10-19 00:24:04 -04:00
Thomas Breloff 3facc116c8 gadfly examples 2015-10-18 00:56:38 -04:00
Thomas Breloff 9178c3b087 winston examples 2015-10-18 00:50:30 -04:00
Thomas Breloff 5c6c85a22f qwt fixes and examples 2015-10-18 00:41:04 -04:00
Thomas Breloff b8adb4d229 unicodeplots examples 2015-10-18 00:08:42 -04:00
Thomas Breloff 093118da8b unicodeplots examples 2015-10-17 23:52:07 -04:00
Thomas Breloff 0aa760119f examples 2015-10-17 23:43:52 -04:00
Thomas Breloff 60b3a1583d examples 2015-10-17 23:31:50 -04:00
Thomas Breloff 593c47b938 examples 2015-10-17 23:29:03 -04:00
Thomas Breloff 0f6bb7ce6f examples 2015-10-17 23:10:45 -04:00
Thomas Breloff 048c2541e6 readme 2015-10-17 23:07:35 -04:00
Thomas Breloff f3e7d1a43b examples 2015-10-17 22:43:36 -04:00
Thomas Breloff 5df24d6683 custom markers example; pyplot markers 2015-10-17 22:16:06 -04:00
Thomas Breloff 73779898b7 pyplot fonts and fixes; examples 2015-10-17 16:49:45 -04:00
Thomas Breloff 7200b6adc6 pyplot set/get data; examples 2015-10-17 16:17:18 -04:00
Thomas Breloff d48f15ab4d examples 2015-10-17 15:22:00 -04:00
Thomas Breloff 2747671935 readme 2015-10-17 14:07:53 -04:00
Thomas Breloff d86f24dd35 pyplot fixes 2015-10-17 01:26:58 -04:00
Thomas Breloff 60edccade5 readme 2015-10-16 17:46:20 -04:00
Thomas Breloff 540460cae9 readme 2015-10-16 17:37:30 -04:00
Thomas Breloff df44d86f46 examples and readme 2015-10-16 17:25:54 -04:00
Thomas Breloff c287b446c9 fixes and examples 2015-10-16 17:21:28 -04:00
Thomas Breloff 1db36c05a8 animations 2015-10-16 16:36:40 -04:00
Thomas Breloff 2c884ab5ab animations 2015-10-16 12:03:40 -04:00
Thomas Breloff cd0dfc956d gadfly cleanup 2015-10-16 11:03:34 -04:00
Thomas Breloff 03392a5f03 gadfly cleanup 2015-10-16 10:58:44 -04:00
Thomas Breloff a7da999bd7 Font and tick/guide/legend fonts 2015-10-16 10:52:36 -04:00
Thomas Breloff 47f21b1652 fixes; changed reg to smooth, added loess smoothing in gadfly 2015-10-16 00:47:46 -04:00
Thomas Breloff 7c91d10c79 several fixes after gadfly reorg 2015-10-16 00:21:37 -04:00
Thomas Breloff fac2f5494b gadfly overhaul 2015-10-15 23:11:17 -04:00
Thomas Breloff bfcdb7e840 example 2015-10-15 17:30:44 -04:00
Thomas Breloff 21d55db3b4 slides 2015-10-15 17:19:57 -04:00
Thomas Breloff 18e49888fe jpg 2015-10-15 16:51:48 -04:00
Thomas Breloff 7ba2f61361 0.3 fix 2015-10-15 16:50:57 -04:00
Thomas Breloff 407df158ef PlotText/text for annotations; fixes 2015-10-15 14:46:29 -04:00
Thomas Breloff eab46ba9c4 fixes 2015-10-15 01:34:06 -04:00
Thomas Breloff 39318e5e61 working on shapes 2015-10-14 20:53:44 -04:00
Thomas Breloff ac1ce03fe5 working on shapes overhaul 2015-10-14 20:46:17 -04:00
Thomas Breloff 4fa697ec75 big shapes overhaul; gadfly ShapeGeometry; some reorg and cleanup... still working on it 2015-10-14 18:14:44 -04:00
Thomas Breloff 027961bffa various fixes 2015-10-14 12:11:20 -04:00
Thomas Breloff 7dfcbabe0c wine 2015-10-13 17:35:10 -04:00
Thomas Breloff dfc7658426 misc 2015-10-13 16:51:19 -04:00
Thomas Breloff 04c6726b76 pyplot cleanup 2015-10-13 15:56:14 -04:00
Thomas Breloff 8fdbbe4c16 pyplot subplots 2015-10-13 15:30:25 -04:00
Thomas Breloff 09fceab75b pyplot subplots 2015-10-13 15:02:27 -04:00
Thomas Breloff 8df4c27285 pyplot, corrplot, and nba 2015-10-13 01:16:11 -04:00
Thomas Breloff e38820b74a working on pyplot 2015-10-12 18:16:55 -04:00
Thomas Breloff c052e731f8 fix links to keep separate lims per row/column 2015-10-12 16:42:34 -04:00
Thomas Breloff 3636860262 subplot linkfunc, fixes, cleanup 2015-10-12 14:31:27 -04:00
Thomas Breloff ce7421bef4 immerse fix 2015-10-12 10:42:29 -04:00
Thomas Breloff 23b51c4c3e working on pyplot subplots 2015-10-10 00:57:48 -04:00
Thomas Breloff 48d802fc40 qwt subplots 2015-10-10 00:03:23 -04:00
Thomas Breloff 51e8f3ff6e working on subplots 2015-10-09 23:21:45 -04:00
Thomas Breloff 7917397bf3 working on corrplot and recipes 2015-10-09 17:08:17 -04:00
Thomas Breloff e0c0052b63 working on corrplot and recipes 2015-10-09 17:08:06 -04:00
Thomas Breloff 15a4b50ce4 working on corrplot and recipes 2015-10-09 17:03:54 -04:00
Thomas Breloff a51a8abf27 working on subplots and recipes 2015-10-09 16:00:24 -04:00
Thomas Breloff 45747e3635 working on recipes 2015-10-09 14:35:00 -04:00
Thomas Breloff 64b8e15918 working on subplots and recipes 2015-10-09 13:12:40 -04:00
Thomas Breloff f4b716b255 working on linked subplots 2015-10-08 17:44:00 -04:00
Thomas Breloff 1115ff83bb working on contours 2015-10-08 14:18:49 -04:00
Thomas Breloff c24550c756 0.3 fix gadfly shapes 2015-10-08 14:01:30 -04:00
Thomas Breloff b5cdfe30ea readme and examples 2015-10-08 13:33:14 -04:00
Thomas Breloff a10e594da1 tests 2015-10-08 12:31:33 -04:00
Thomas Breloff 63aee387f0 badges and test fix 2015-10-08 12:05:21 -04:00
Thomas Breloff ab9de1e199 added 0.3 and nightly to travis; force Winston to use gtk 2015-10-08 11:48:54 -04:00
Thomas Breloff e60c463328 0.3 support; require Compat; pyplot scatter colormaps 2015-10-08 11:30:20 -04:00
Thomas Breloff 537d74196b working on contours 2015-10-08 07:49:26 -04:00
Thomas Breloff a8bbbe8582 working on contours 2015-10-08 00:49:30 -04:00
Thomas Breloff 29471a7aac readme 2015-10-07 18:18:44 -04:00
Thomas Breloff 894a4a5567 winston fixes/examples and updated readme 2015-10-07 18:09:32 -04:00
Thomas Breloff f7cd5276f0 fixed qwt and examples 2015-10-07 17:42:08 -04:00
Thomas Breloff 95b8c71083 examples 2015-10-07 17:04:19 -04:00
Thomas Breloff 52c82e6fe2 examples 2015-10-07 16:54:42 -04:00
Thomas Breloff cef1abd6aa readme 2015-10-07 16:29:32 -04:00
Thomas Breloff 91da261128 working on colors/args and examples 2015-10-07 16:04:30 -04:00
Thomas Breloff 1c70346a61 bringing pyplot up to speed 2015-10-07 13:45:38 -04:00
Thomas Breloff 2de017086d working on colors and args 2015-10-07 12:50:07 -04:00
Thomas Breloff d6253c72d4 working on colors, args, and axis flip 2015-10-06 18:19:44 -04:00
Thomas Breloff e5007b6c87 working on colors, args, and axis flip 2015-10-06 17:51:54 -04:00
Thomas Breloff bbc18549b3 working on args 2015-10-06 11:37:45 -05:00
Thomas Breloff b6855b6fba working on args 2015-10-06 10:07:18 -05:00
Thomas Breloff 70db97d576 working on args 2015-10-06 09:49:33 -05:00
Thomas Breloff f632f2f39e working on args 2015-10-06 09:04:57 -05:00
Thomas Breloff b23f968d57 working on axis/line/marker/fill overhaul 2015-10-06 08:45:53 -05:00
Thomas Breloff ea88877a11 working on color schemes 2015-10-05 17:06:52 -05:00
Thomas Breloff 1de3decede working on color schemes 2015-10-05 16:28:43 -05:00
Thomas Breloff 78045fd2ab working on spy and nbins tuple 2015-10-04 21:39:32 -05:00
Thomas Breloff 7c8898158b working on gadfly continuous colors 2015-10-04 16:52:29 -05:00
Thomas Breloff 3fc5f87388 added better error for Winston output_surface 2015-10-04 09:41:42 -05:00
Thomas Breloff 623ca1effc winston log10 scale 2015-10-03 17:18:58 -05:00
Thomas Breloff da05e7fe5e qwt log10 scale 2015-10-03 17:03:13 -05:00
Thomas Breloff f3734d4c64 added pyplot scales 2015-10-03 09:46:02 -05:00
Thomas Breloff 9ab458e109 switched gadfly heatmap to Geom.histogram2d 2015-10-01 12:55:05 -05:00
Thomas Breloff d4c72e6f78 gadfly ribbon 2015-09-30 10:36:00 -04:00
Thomas Breloff 1f58ef5140 don't error on savefig without an ext, use default; closes #32 2015-09-30 09:55:07 -04:00
Thomas Breloff 69d574c587 added scales globally and to gadfly/immerse 2015-09-30 09:18:16 -04:00
Thomas Breloff a027fdded7 autoload dataframes 2015-09-29 14:40:11 -04:00
Thomas Breloff 443df0501d supported graphs 2015-09-29 14:27:18 -04:00
Thomas Breloff d68fe5f80d supported readme 2015-09-29 14:14:13 -04:00
Thomas Breloff ebf868bae3 supported readme 2015-09-29 14:13:46 -04:00
Thomas Breloff 8a9b3286e9 supportedGraphs utils and pngs; removed args/kwargs from defaults 2015-09-29 14:05:24 -04:00
Thomas Breloff a0455561b6 unicode hline/vline; supportGraph utils 2015-09-29 13:46:31 -04:00
Thomas Breloff 518ccf5a74 pyplot hline/vline 2015-09-29 12:20:58 -04:00
Thomas Breloff 54f09c58cb unicodeplots lims 2015-09-29 12:00:48 -04:00
Thomas Breloff 029ad1045a qwt lims/ticks 2015-09-29 11:50:02 -04:00
Thomas Breloff 2f55fb4d84 winston lims 2015-09-29 10:40:46 -04:00
Thomas Breloff add724eee9 working on gadfly ticks 2015-09-29 09:50:53 -04:00
Thomas Breloff 07324ae892 working on gadfly ticks 2015-09-29 09:49:58 -04:00
Thomas Breloff ffc05e971f working on ticks 2015-09-28 23:49:03 -04:00
Thomas Breloff 30f42fd1d4 added lims/ticks for pyplot 2015-09-28 22:49:46 -04:00
Thomas Breloff f284857438 added qwt hline/vline; working on setTicksFromStringVector 2015-09-28 17:56:32 -04:00
Thomas Breloff 06ab514472 qwt annotations and examples 2015-09-28 17:11:43 -04:00
Thomas Breloff 85b65acbe0 readme 2015-09-28 16:48:32 -04:00
Thomas Breloff 4d1d9de22f gitignore 2015-09-28 16:43:04 -04:00
Thomas Breloff a68ec9b191 savefig and png 2015-09-28 16:42:02 -04:00
Thomas Breloff e386636d66 readme 2015-09-28 16:01:48 -04:00
Thomas Breloff cc3311b8eb examples 2015-09-28 15:49:46 -04:00
Thomas Breloff d3d1181769 examples and readme 2015-09-28 15:44:50 -04:00
Thomas Breloff 001abf684c examples 2015-09-28 15:35:45 -04:00
Thomas Breloff 92a65296b5 redid supportedArgs 2015-09-28 15:21:45 -04:00
Thomas Breloff 3a7887d003 working on massive naming overhaul and fixes 2015-09-28 14:58:56 -04:00
Thomas Breloff 3334c4a87a winston annotations 2015-09-28 12:52:44 -04:00
Thomas Breloff 228ce768e0 pyplot annotations and ioff fix 2015-09-28 12:09:52 -04:00
Thomas Breloff eafc1a784c working on annotations 2015-09-28 11:09:45 -04:00
Thomas Breloff fd3a4d0a0e working on annotations 2015-09-28 10:46:53 -04:00
Thomas Breloff 77d679b63b working on handling string input data 2015-09-27 23:46:42 -04:00
Thomas Breloff 730d764c6e gadfly color group 2015-09-25 17:03:57 -04:00
Thomas Breloff 6f1ef82e89 gadfly color groups for line segments 2015-09-25 16:47:17 -04:00
Thomas Breloff d4da910f19 call Gadfly.get_stroke_vector; added getindex/setindex to qwt 2015-09-25 13:33:06 -04:00
Thomas Breloff a0a0773bd2 fix push warning 2015-09-25 09:42:16 -04:00
Thomas Breloff 9ce8d138d5 working on data updating and notebooks 2015-09-25 01:00:22 -04:00
Thomas Breloff 0041b034e0 working on various things 2015-09-24 16:47:30 -04:00
Thomas Breloff d781c437de started real time updates; fixes #29 (pyplot ijulia display) 2015-09-24 13:01:31 -04:00
Thomas Breloff 2fe21b8547 fixes, gadfly star shape, readme example 2015-09-24 00:35:18 -04:00
Thomas Breloff dbc90c9417 working on groups and iris notebook 2015-09-23 23:34:54 -04:00
Thomas Breloff 431d60a58a working on groups and iris notebook 2015-09-23 23:15:15 -04:00
Thomas Breloff fcfcc9a94a working on groups and iris notebook 2015-09-23 17:13:20 -04:00
Thomas Breloff 870fb4e2df working on groups and iris notebook 2015-09-23 17:12:26 -04:00
Thomas Breloff e3612b973f working on groups and iris notebook 2015-09-23 16:48:49 -04:00
Thomas Breloff 75f4ca928f working on auto-labels from dataframes meta info, and started group arg 2015-09-23 15:34:42 -04:00
Thomas Breloff 22fd25b30f readme 2015-09-23 13:36:51 -04:00
Thomas Breloff 610ddb09ff readme and cleanup 2015-09-23 13:33:12 -04:00
Thomas Breloff 33602aca11 fixed tests 2015-09-23 13:03:37 -04:00
Thomas Breloff a207856e99 lims/ticks for gadfly/immerse 2015-09-23 12:45:24 -04:00
Thomas Breloff 80e8b5b5e4 fixes 2015-09-23 11:35:08 -04:00
Thomas Breloff 273996aa91 working on ticks/lims 2015-09-23 00:14:32 -04:00
Thomas Breloff 646b88c5be working on ticks 2015-09-22 23:30:20 -04:00
Thomas Breloff 88cfd82a25 unicodeplots fixes 2015-09-22 17:19:37 -04:00
Thomas Breloff 2f8436be87 winston examples 2015-09-22 17:04:51 -04:00
Thomas Breloff a6237cdda3 qwt examples 2015-09-22 17:02:19 -04:00
Thomas Breloff eb93a767fc pyplot fixes and examples 2015-09-22 16:57:23 -04:00
Thomas Breloff 41852412ca gadfly/immerse fixes and examples 2015-09-22 16:46:54 -04:00
Thomas Breloff 224b2b7323 various fixes; readme 2015-09-22 16:30:40 -04:00
Thomas Breloff ec939f9f9a more display fun 2015-09-22 14:56:37 -04:00
Thomas Breloff eced15b712 working on display/writemime overhaul 2015-09-22 12:54:55 -04:00
Thomas Breloff f8fb473981 reexport Colors; move default backend choice into __init__; fixed pickDefaultBackend 2015-09-22 08:10:30 -04:00
Thomas Breloff f2bcb77cfb trying fgcolor gadfly 2015-09-22 00:04:35 -04:00
Thomas Breloff dc5da28176 initial dataframes support 2015-09-21 23:36:07 -04:00
Thomas Breloff be43e64743 foreground_color fixes and added support in pyplot 2015-09-21 23:04:37 -04:00
Thomas Breloff 454630dace qwt fixes 2015-09-21 22:14:59 -04:00
Thomas Breloff c9df244333 qwt fixes 2015-09-21 17:38:27 -04:00
Thomas Breloff 2649d97d69 readme 2015-09-21 17:31:36 -04:00
Thomas Breloff bcb9ac5501 readme 2015-09-21 17:29:51 -04:00
Thomas Breloff 76d1e26f7d readme 2015-09-21 17:04:20 -04:00
Thomas Breloff ba13ab9926 readme 2015-09-21 16:59:15 -04:00
Thomas Breloff c43b34b67f auto-generated readme 2015-09-21 16:49:45 -04:00
Thomas Breloff 3739c2881b fixed linetype aliases; for qwt as well 2015-09-21 16:18:58 -04:00
Thomas Breloff bc13b81351 working on readme_template; added path linetype and sorting logic for line 2015-09-21 16:03:59 -04:00
Thomas Breloff 86a56e487a working on aliases 2015-09-21 14:38:03 -04:00
Thomas Breloff 031058cd0c cleanup and renaming constants 2015-09-21 12:15:40 -04:00
Thomas Breloff 85040d6104 fix creating empty plot with plot() 2015-09-21 12:05:17 -04:00
Thomas Breloff efbf74b44c working on auto generated color palette 2015-09-21 11:45:37 -04:00
Thomas Breloff 774fc42e8e replaced creation of kw args 2015-09-20 23:48:45 -04:00
Thomas Breloff 692cfe4f52 added srand calls to tests and examples 2015-09-20 22:59:25 -04:00
Thomas Breloff 8b34d4417b gadfly display size and fix: check for line_style 2015-09-20 21:12:56 -04:00
Thomas Breloff c20a7b3c69 added fillto for gadfly/immerse 2015-09-19 17:23:18 -04:00
Thomas Breloff ff6e83c55e working on new plot logic 2015-09-19 14:39:25 -04:00
Thomas Breloff 6aa56fd30f fixed subplot bug 2015-09-19 00:38:50 -04:00
Thomas Breloff f5a6f129dd foreground_color 2015-09-19 00:05:24 -04:00
Thomas Breloff 5405c1d240 working on colors 2015-09-18 17:20:35 -04:00
Thomas Breloff 455f85e230 working on colors, args 2015-09-18 16:53:43 -04:00
Thomas Breloff fa899a67a7 added support for gadfly linestyles... depends on gadfly PR 695 2015-09-18 12:26:18 -04:00
Thomas Breloff 56cf25eebf gadfly shapes 2015-09-18 01:55:16 -04:00
Thomas Breloff 1a490551e6 working on arg aliases and reorg 2015-09-18 01:48:00 -04:00
Thomas Breloff 4514802e35 replacing String with AbstractString 2015-09-17 21:32:27 -04:00
Thomas Breloff a9062b0611 examples 2015-09-17 17:11:25 -04:00
Thomas Breloff eb6b30aab1 markers, fixes, and rerun examples 2015-09-17 17:05:08 -04:00
Thomas Breloff 922abd7ad3 examples 2015-09-17 16:21:13 -04:00
Thomas Breloff c4596dda75 more shape fun... completed gadfly's set 2015-09-17 16:18:48 -04:00
Thomas Breloff 168b77326b fixing markers 2015-09-17 15:44:01 -04:00
Thomas Breloff f674bba861 added gadfly shapes utriangle, dtriangle, and xcross 2015-09-17 15:28:54 -04:00
Thomas Breloff 6b2e65e738 ohlc example 2015-09-17 14:49:37 -04:00
Thomas Breloff 51021c799d accept vectors of tuples; implement ohlc for gadfly with an example 2015-09-17 14:44:29 -04:00
Thomas Breloff 01cb036976 added fillto support for winston 2015-09-17 11:26:26 -04:00
Thomas Breloff ea7867429f added hline/vline support in gadfly 2015-09-17 10:14:13 -04:00
Thomas Breloff c5300cd7be notes 2015-09-17 08:32:32 -04:00
Thomas Breloff ea34c7d226 working on winston and examples 2015-09-16 17:17:05 -04:00
Thomas Breloff 537e04989f working on winston 2015-09-16 16:34:40 -04:00
Thomas Breloff d447c5dc25 moved exports into backend files 2015-09-16 15:11:44 -04:00
Thomas Breloff 73944fc9bf first draft winston 2015-09-16 15:08:50 -04:00
Thomas Breloff 01e994e0f1 regenerate markdown 2015-09-16 14:32:36 -04:00
Thomas Breloff af3f3f436c improving markdown 2015-09-16 14:24:58 -04:00
Thomas Breloff 56e90a266a examples 2015-09-16 14:16:58 -04:00
Thomas Breloff 1fc59ed522 fix ALL_ARGS; unicodeplots examples 2015-09-16 14:01:53 -04:00
Thomas Breloff 3a3b1a6c17 examples 2015-09-16 13:40:23 -04:00
Thomas Breloff 242ce807df examples 2015-09-16 13:32:43 -04:00
Thomas Breloff 734967152c fixed suppported typo 2015-09-16 13:29:51 -04:00
Thomas Breloff 8023e15bb9 examples 2015-09-16 13:17:06 -04:00
Thomas Breloff ceb58a6fe1 supported functions; renamed a few things package to backend 2015-09-16 13:03:37 -04:00
Thomas Breloff ae48bd1abb working on big set of fixes/reorg; better example code, supportedArgs and subplotSupported, change dots to scatter and handle better 2015-09-16 12:07:08 -04:00
Thomas Breloff d27fe2fbad examples 2015-09-16 01:47:17 -04:00
Thomas Breloff 970686efaf supported functions and gadfly shapes 2015-09-16 01:42:47 -04:00
Thomas Breloff 8df46ec0e9 gadfly_shapes.jl 2015-09-16 00:25:51 -04:00
Thomas Breloff 864057a6ed unicode examples 2015-09-16 00:12:14 -04:00
Thomas Breloff 92ca4cb1d1 examples 2015-09-16 00:01:38 -04:00
Thomas Breloff aabf811fe7 working on Immerse subplot support 2015-09-15 23:27:00 -04:00
Thomas Breloff 9b0a645a05 working on immerse subplot window 2015-09-15 17:54:40 -04:00
Thomas Breloff 24b5e7ff09 pushtomaster 2015-09-15 17:11:36 -04:00
Thomas Breloff 48969ecee3 added sticks and sticksHack for Gadfly 2015-09-15 16:49:56 -04:00
Thomas Breloff 6ae0912e0e readme 2015-09-15 13:24:14 -04:00
Thomas Breloff e8062d544d readme 2015-09-15 13:18:49 -04:00
Thomas Breloff 07332aa4ac readme 2015-09-15 13:16:19 -04:00
Thomas Breloff 1e2b3559f2 gadfly marker fixes and examples 2015-09-15 13:08:04 -04:00
Thomas Breloff 8bfaa14edb added backend template 2015-09-15 10:03:56 -04:00
Thomas Breloff d4e87d3fe8 working on gadfly and immerse 2015-09-14 23:44:28 -04:00
Thomas Breloff 460348c0b6 working on gadfly and immerse 2015-09-14 23:28:47 -04:00
Thomas Breloff 264e8c735e working on gadfly markers 2015-09-14 17:27:37 -04:00
Thomas Breloff c4b04e2327 immerse final fixes and examples 2015-09-14 15:30:09 -04:00
Thomas Breloff dc1389bd1a working on immerse 2015-09-14 15:24:18 -04:00
Thomas Breloff 51e38c80d8 working on immerse 2015-09-14 15:08:25 -04:00
Thomas Breloff a33f5fabd1 working on immerse 2015-09-14 14:41:13 -04:00
Thomas Breloff e6c8344970 working on immerse 2015-09-14 13:44:58 -04:00
Thomas Breloff 2207ae6617 working on immerse 2015-09-14 12:35:24 -04:00
Thomas Breloff cdc6707ad3 pyplot bg color 2015-09-14 10:46:33 -04:00
Thomas Breloff 448fdaecbd readme 2015-09-14 00:45:55 -04:00
Thomas Breloff 4aa79aed6f readme 2015-09-14 00:45:00 -04:00
Thomas Breloff c8fcc373f8 examples and readme 2015-09-14 00:28:08 -04:00
Thomas Breloff 1410141b46 examples 2015-09-14 00:16:24 -04:00
Thomas Breloff 76efabb5f3 heatmaps, fixes 2015-09-14 00:15:51 -04:00
Thomas Breloff 36effc6dc4 working on pyplot 2015-09-13 22:20:15 -04:00
Thomas Breloff 8d5412a38f working on pyplot 2015-09-13 21:34:28 -04:00
Thomas Breloff 465b5d20b4 working on pyplot 2015-09-13 18:39:56 -04:00
Thomas Breloff 60a22b1897 working on pyplot 2015-09-13 17:02:03 -04:00
Thomas Breloff 09a685e977 gitignore 2015-09-13 16:36:32 -04:00
Thomas Breloff 31da90865c added args and kwargs to PLOT_DEFAULTS 2015-09-13 14:06:33 -04:00
Thomas Breloff 9ea98bcb56 readme 2015-09-12 22:54:16 -04:00
Thomas Breloff 4f9804d24e readme 2015-09-12 22:48:07 -04:00
Thomas Breloff 0a88518188 readme 2015-09-12 22:38:56 -04:00
Thomas Breloff ccae6373be readme 2015-09-12 22:31:05 -04:00
Thomas Breloff 31e5d07a45 readme 2015-09-12 22:24:44 -04:00
Thomas Breloff 056fee4b28 added parametric plot with example 2015-09-12 22:02:17 -04:00
Thomas Breloff 5a47864d73 readme 2015-09-12 21:46:30 -04:00
Thomas Breloff 1c7da432f1 readme 2015-09-12 21:40:25 -04:00
Thomas Breloff 3d3ceacaab pickDefaultBackend with warning; readme improvements 2015-09-12 21:33:38 -04:00
Thomas Breloff 35df39a44b readme and shorthands for switching plotters 2015-09-12 21:12:18 -04:00
Thomas Breloff c23d6d62e7 examples, fixes, and reorg img dir 2015-09-12 20:47:09 -04:00
Thomas Breloff 243d4a7ac3 working on unicodeplots savepng and examples 2015-09-12 20:28:40 -04:00
Thomas Breloff 50254d06d7 working on unicodeplots savepng and examples 2015-09-12 20:23:36 -04:00
Thomas Breloff 475b46f516 working on unicodeplots 2015-09-12 16:49:02 -04:00
Thomas Breloff f4573f7e87 added unicodeplots to examples 2015-09-12 16:37:34 -04:00
Thomas Breloff 7fb1a4ec0f better error when importing a package that is not installed; xmin/xmax functions example 2015-09-12 16:10:42 -04:00
Thomas Breloff 4299aa0ff8 more ways to plot Funtions 2015-09-12 15:59:45 -04:00
Thomas Breloff 4eb7e3810e unicodeplots 2015-09-12 14:58:15 -04:00
Thomas Breloff 3e7e7e9ac0 working on unicodeplots 2015-09-12 00:57:58 -04:00
Thomas Breloff 9e1058ec0a working on unicodeplots 2015-09-12 00:54:41 -04:00
Thomas Breloff 426a9eb6d3 working on unicodeplots 2015-09-12 00:31:48 -04:00
Thomas Breloff f322b9ac1c working on unicodeplots; added args to Plot; some reorg 2015-09-12 00:24:35 -04:00
Thomas Breloff e13fec08ca readme 2015-09-11 22:12:33 -04:00
112 changed files with 6954 additions and 1272 deletions
+3
View File
@@ -1,3 +1,6 @@
*.jl.cov
*.jl.*.cov
*.jl.mem
.DS_Store
examples/.ipynb_checkpoints/*
examples/meetup/.ipynb_checkpoints/*
+9 -2
View File
@@ -5,9 +5,16 @@ os:
- osx
julia:
- 0.4
#- nightly
notifications:
email: false
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"); Pkg.add("Gadfly"); Pkg.test("Plots"; coverage=true)'
- julia -e 'Pkg.clone("https://github.com/tbreloff/Images.jl.git"); Pkg.checkout("Images","tom_imgcompare");'
- julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
- julia -e 'Pkg.clone("Cairo"); Pkg.build("Cairo")'
- julia -e 'ENV["PYTHON"] = ""; Pkg.clone("PyPlot"); Pkg.build("PyPlot")'
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")'
- julia -e 'Pkg.test("Plots"; coverage=false)'
# - julia -e 'cd(Pkg.dir("Plots")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())'
+326 -144
View File
@@ -1,224 +1,406 @@
# Plots
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/Plots.jl)
[![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3)
[![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4)
<!-- [![Coverage Status](https://coveralls.io/repos/tbreloff/Plots.jl/badge.svg?branch=master)](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
<!-- [![codecov.io](http://codecov.io/github/tbreloff/Plots.jl/coverage.svg?branch=master)](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
Plotting interface and wrapper for several plotting packages.
#### Author: Thomas Breloff (@tbreloff)
Plots is a plotting API and toolset. My goals with the package are:
- **Intuitive**. Start generating complex plots without reading volumes of documentation. Commands should "just work".
- **Concise**. Less code means fewer mistakes and more efficient development/analysis.
- **Flexible**. Produce your favorite plots from your favorite package, but quicker and simpler.
- **Consistent**. Don't commit to one graphics package. Use the same code and access the strengths of all backends.
- **Lightweight**. Very few dependencies, since backends are loaded and initialized dynamically.
Use the preprocessing pipeline in Plots to fully describe your visualization before it calls the backend code. This maintains modularity and allows for efficient separation of front end code, algorithms, and backend graphics. New graphical backends can be added with minimal effort.
Check out the [summary graphs](https://github.com/tbreloff/ExamplePlots.jl/tree/master/img/supported/supported.md) for the features that each backend supports.
Please add wishlist items, bugs, or any other comments/questions to the issues list.
## Examples
## Examples for each implemented backend:
- [Qwt.jl](docs/qwt_examples.md)
- [Gadfly.jl](docs/gadfly_examples.md)
- [Gadfly.jl/Immerse.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/gadfly_examples.md)
- [PyPlot.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/pyplot_examples.md)
- [UnicodePlots.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/unicodeplots_examples.md)
- [Qwt.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/qwt_examples.md)
Also check out the many [IJulia notebooks](http://nbviewer.ipython.org/github/tbreloff/ExamplePlots.jl/tree/master/examples/) with many examples.
## Installation
First, clone the package, and get any plotting packages you need:
First, add the package
```julia
Pkg.add("Plots")
# if you want the latest features:
Pkg.checkout("Plots")
# or for the bleeding edge:
Pkg.checkout("Plots", "dev")
```
Pkg.clone("https://github.com/tbreloff/Plots.jl.git")
Pkg.add("Gadfly") # [optional]
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git") # [optional] requires pyqt and pyqwt
then get any plotting packages you need (obviously, you should get at least one backend).
```julia
Pkg.add("Gadfly")
Pkg.add("Immerse")
Pkg.add("PyPlot")
Pkg.add("UnicodePlots")
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git")
```
## Use
Load it in. The underlying plotting backends are not imported until `plotter()` is called (which happens
on your first call to `plot`). This means that you don't need any backends to be installed when you call `using Plots`.
For now, the default backend is Gadfly.
Load it in. The underlying plotting backends are not imported until `backend()` is called (which happens
on your first call to `plot` or `subplot`). This means that you don't need any backends to be installed when you call `using Plots`.
```
Plots will try to figure out a good default backend for you automatically based on what backends are installed.
```julia
using Plots
```
Do a plot in Gadfly, then save a png:
Do a plot in Gadfly (inspired by [this example](http://gadflyjl.org/geom_point.html)), then save a png:
```
plot(rand(10,2); marker = :rect)
savepng(Plots.IMG_DIR * "gadfly1.png")
```
```julia
gadfly() # switch to Gadfly as a backend
dataframes() # turn on support for DataFrames inputs
which saves:
# load some data
using RDatasets
iris = dataset("datasets", "iris");
# This will bring up a browser window with the plot. Add a semicolon at the end to skip display.
scatter(iris, :SepalLength, :SepalWidth, group=:Species, m=([:+ :d :s], 12), smooth=0.99, bg=:black)
# save a png (equivalent to png("gadfly1.png") and savefig("gadfly1.png"))
png("gadfly1")
```
![gadfly_plt](img/gadfly1.png)
## API
Do a plot in Qwt, then save a png:
Call `backend(backend::Symbol)` or the shorthands (`gadfly()`, `qwt()`, `unicodeplots()`, etc) to set the current plotting backend.
Subsequent commands are converted into the relevant plotting commands for that package:
```
plotter!(:qwt) # switches the backend to Qwt
plot(rand(10,2); marker = :rect)
savepng(Plots.IMG_DIR * "qwt1.png")
```
which saves:
![qwt_plt](img/qwt1.png)
## plot and plotter! interface (WIP)
The main plot command. Call `plotter!(:module)` to set the current plotting backend.
Commands are converted into the relevant plotting commands for that package:
```
plotter!(:gadfly)
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
plotter!(:qwt)
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```julia
gadfly()
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
qwt()
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
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 `currentPlot`
plot!(args...; kw...) # adds to the `currentPlot`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```julia
plot(args...; kw...) # creates a new plot window, and sets it to be the `current`
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
Here are some various args to supply, and the implicit mapping (AVec == AbstractVector and AMat == AbstractMatrix):
There are many ways to pass in data to the plot functions... some examples:
```
plot(y::AVec; kw...) # one line... x = 1:length(y)
plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
plot(x::AVec, f::Function; kw...) # one line, y = f(x)
plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
plot(x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
plot(x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
plot(n::Integer; kw...) # n lines, all empty (for updating plots)
- Vector-like (subtypes of AbstractArray{T,1})
- Matrix-like (subtypes of AbstractArray{T,2})
- Vectors of Vectors
- Functions
- Vectors of Functions
- DataFrames with column symbols (initialize with `dataframes()`)
# TODO: how do we handle NA values in dataframes?
plot(df::DataFrame; kw...) # one line per DataFrame column, labels == names(df)
plot(df::DataFrame, columns; kw...) # one line per column, but on a subset of column names
In general, you can pass in a `y` only, or an `x` and `y`, both of whatever type(s) you want, and Plots will slice up the data as needed.
For matrices, data is split by columns. For functions, data is mapped. For DataFrames, a Symbol/Symbols in place of x/y will map to
the relevant column(s).
Here are some example usages... remember you can always use `plot!` to update an existing plot, and that, unless specified, you will update the `current()`.
```julia
plot() # empty plot object
plot(4) # initialize with 4 empty series
plot(rand(10)) # plot 1 series... x = 1:10
plot(rand(10,5)) # plot 5 series... x = 1:10
plot(rand(10), rand(10)) # plot 1 series
plot(rand(10,5), rand(10)) # plot 5 series... y is the same for all
plot(sin, rand(10)) # y = sin(x)
plot(rand(10), sin) # same... y = sin(x)
plot([sin,cos], 0:0.1:π) # plot 2 series, sin(x) and cos(x)
plot([sin,cos], 0, π) # plot sin and cos on the range [0, π]
plot(1:10, Any[rand(10), sin]) # plot 2 series, y = rand(10) for the first, y = sin(x) for the second... x = 1:10 for both
plot(dataset("Ecdat", "Airline"), :Cost) # plot from a DataFrame (call `dataframes()` first to import DataFrames and initialize)
```
[TODO] You can swap out `plot` for `subplot`. Each line will go into a separate plot. Use the layout keyword:
All plot methods accept a number of keyword arguments (see the tables below), which follow some rules:
- Many arguments have aliases which are replaced during preprocessing. `c` is the same as `color`, `m` is the same as `marker`, etc. You can choose how verbose you'd like to be. (see the tables below)
- There are some special arguments (`xaxis`, `yaxis`, `line`, `marker`, `fill` and the aliases `l`, `m`, `f`) which magically set many related things at once. (see the __Tip__ below)
- If the argument is a "matrix-type", then each column will map to a series, cycling through columns if there are fewer columns than series. Anything else will apply the argument value to every series.
- Many arguments accept many different types... for example the `color` (also `markercolor`, `fillcolor`, etc) argument will accept strings or symbols with a color name, or any `Colors.Colorant`, or a `ColorScheme`, or a symbol representing a `ColorGradient`, or an AbstractVector of colors/symbols/etc...
You can update certain plot settings after plot creation (not supported on all backends):
```julia
plot!(title = "New Title", xlabel = "New xlabel", ylabel = "New ylabel")
plot!(xlims = (0, 5.5), ylims = (-2.2, 6), xticks = 0:0.5:10, yticks = [0,1,5,10])
# using shorthands:
xaxis!("mylabel", :log10, :flip)
```
y = rand(100,3)
subplot(y; layout=(2,2), kw...) # creates 3 lines going into 3 separate plots, laid out on a 2x2 grid (last row is filled with plot #3)
subplot(y; layout=(1,3), kw...) # again 3 plots, all in the same row
subplot(y; layout=[1,[2,3]]) # pass a nested Array to fully specify the layout. here the first plot will take up the first row,
# and the others will share the second row
With `subplot`, create multiple plots at once, with flexible layout options:
```julia
y = rand(100,3)
subplot(y; n = 3) # create an automatic grid, and let it figure out the shape
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns
subplot(y; layout = [1, 2]) # explicit layout. Lists the number of plots in each row
```
__Tip__: You can call `subplot!(args...; kw...)` to add to an existing subplot.
__Tip__: Calling `subplot!` on a `Plot` object, or `plot!` on a `Subplot` object will throw an error.
Shorthands:
```
scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
```julia
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
title!(s::AbstractString) = plot!(title = s)
xlabel!(s::AbstractString) = plot!(xlabel = s)
ylabel!(s::AbstractString) = plot!(ylabel = s)
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(xlims = lims)
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(ylims = lims)
xticks!{T<:Real}(v::AVec{T}) = plot!(xticks = v)
yticks!{T<:Real}(v::AVec{T}) = plot!(yticks = v)
xflip!(flip::Bool = true) = plot!(xflip = flip)
yflip!(flip::Bool = true) = plot!(yflip = flip)
xaxis!(args...) = plot!(xaxis = args)
yaxis!(args...) = plot!(yaxis = args)
annotate!(anns) = plot!(annotation = anns)
```
Some keyword arguments you can set:
### Keyword arguments:
Keyword | Default | Type | Aliases
---- | ---- | ---- | ----
`:annotation` | `nothing` | Series | `:ann`, `:annotate`, `:annotations`, `:anns`
`:axis` | `left` | Series | `:axiss`
`:background_color` | `RGB{U8}(1.0,1.0,1.0)` | Plot | `:background`, `:bg`, `:bg_color`, `:bgcolor`
`:color` | `auto` | Series | `:c`, `:colors`
`:color_palette` | `auto` | Plot | `:palette`
`:fill` | `nothing` | Series | `:area`, `:f`
`:fillcolor` | `match` | Series | `:fc`, `:fcolor`, `:fillcolors`
`:fillopacity` | `nothing` | Series | `:fillopacitys`, `:fo`
`:fillrange` | `nothing` | Series | `:fillranges`, `:fillrng`
`:foreground_color` | `auto` | Plot | `:fg`, `:fg_color`, `:fgcolor`, `:foreground`
`:grid` | `true` | Plot |
`:group` | `nothing` | Series | `:g`, `:groups`
`:guidefont` | `Plots.Font("Helvetica",11,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
`:label` | `AUTO` | Series | `:lab`, `:labels`
`:layout` | `nothing` | Plot |
`:legend` | `true` | Plot | `:leg`
`:legendfont` | `Plots.Font("Helvetica",8,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
`:line` | `nothing` | Series | `:l`
`:lineopacity` | `nothing` | Series | `:lineopacitys`, `:lo`
`:linestyle` | `solid` | Series | `:linestyles`, `:ls`, `:s`, `:style`
`:linetype` | `path` | Series | `:linetypes`, `:lt`, `:t`, `:type`
`:linewidth` | `1` | Series | `:linewidths`, `:lw`, `:w`, `:width`
`:link` | `false` | Plot |
`:linkfunc` | `nothing` | Plot |
`:linkx` | `false` | Plot | `:xlink`
`:linky` | `false` | Plot | `:ylink`
`:marker` | `nothing` | Series | `:m`, `:mark`
`:markercolor` | `match` | Series | `:markercolors`, `:mc`, `:mcolor`
`:markeropacity` | `nothing` | Series | `:alpha`, `:markeropacitys`, `:mo`, `:opacity`
`:markershape` | `none` | Series | `:markershapes`, `:shape`
`:markersize` | `6` | Series | `:markersizes`, `:ms`, `:msize`
`:n` | `-1` | Plot |
`:nbins` | `100` | Series | `:nb`, `:nbin`, `:nbinss`
`:nc` | `-1` | Plot |
`:nr` | `-1` | Plot |
`:pos` | `(0,0)` | Plot |
`:show` | `false` | Plot | `:display`, `:gui`
`:size` | `(500,300)` | Plot | `:windowsize`, `:wsize`
`:smooth` | `false` | Series | `:reg`, `:regression`, `:smooths`
`:tickfont` | `Plots.Font("Helvetica",8,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
`:title` | `` | Plot |
`:windowtitle` | `Plots.jl` | Plot | `:wtitle`
`:xaxis` | `nothing` | Plot |
`:xflip` | `false` | Plot |
`:xlabel` | `` | Plot | `:xlab`
`:xlims` | `auto` | Plot | `:xlim`, `:xlimit`, `:xlimits`
`:xscale` | `identity` | Plot |
`:xticks` | `auto` | Plot | `:xtick`
`:yaxis` | `nothing` | Plot |
`:yflip` | `false` | Plot |
`:ylabel` | `` | Plot | `:ylab`
`:ylims` | `auto` | Plot | `:ylim`, `:ylimit`, `:ylimits`
`:yrightlabel` | `` | Plot | `:y2lab`, `:y2label`, `:ylab2`, `:ylabel2`, `:ylabelright`, `:ylabr`, `:yrlab`
`:yscale` | `identity` | Plot |
`:yticks` | `auto` | Plot | `:ytick`
`:z` | `nothing` | Series | `:zs`
### Plot types:
Type | Desc | Aliases
---- | ---- | ----
`:none` | No line | `:n`, `:no`
`:line` | Lines with sorted x-axis | `:l`
`:path` | Lines | `:p`
`:steppre` | Step plot (vertical then horizontal) | `:stepinv`, `:stepinverted`, `:stepsinv`, `:stepsinverted`
`:steppost` | Step plot (horizontal then vertical) | `:stair`, `:stairs`, `:step`, `:steps`
`:sticks` | Vertical lines | `:stem`, `:stems`
`:scatter` | Points, no lines | `:dots`
`:heatmap` | Colored regions by density |
`:hexbin` | Similar to heatmap |
`:hist` | Histogram (doesn't use x) | `:histogram`
`:bar` | Bar plot (centered on x values) |
`:hline` | Horizontal line (doesn't use x) |
`:vline` | Vertical line (doesn't use x) |
`:ohlc` | Open/High/Low/Close chart (expects y is AbstractVector{Plots.OHLC}) |
### Line styles:
Type | Aliases
---- | ----
`:auto` | `:a`
`:solid` | `:s`
`:dash` | `:d`
`:dot` |
`:dashdot` | `:dd`
`:dashdotdot` | `:ddd`
### Markers:
Type | Aliases
---- | ----
`:none` | `:n`, `:no`
`:auto` | `:a`
`:cross` | `:+`, `:plus`
`:diamond` | `:d`
`:dtriangle` | `:V`, `:downtri`, `:downtriangle`, `:dt`, `:dtri`, `:v`
`:ellipse` | `:c`, `:circle`
`:heptagon` | `:hep`
`:hexagon` | `:h`, `:hex`
`:octagon` | `:o`, `:oct`
`:pentagon` | `:p`, `:pent`
`:rect` | `:r`, `:sq`, `:square`
`:star4` |
`:star5` | `:s`, `:star`, `:star1`
`:star6` |
`:star7` |
`:star8` | `:s2`, `:star2`
`:utriangle` | `:^`, `:uptri`, `:uptriangle`, `:ut`, `:utri`
`:xcross` | `:X`, `:x`
__Tip__: With supported backends, you can pass a `Plots.Shape` object for the `marker`/`markershape` arguments. `Shape` takes a vector of 2-tuples in the constructor, defining the points of the polygon's shape in a unit-scaled coordinate space. To make a square, for example, you could do `Shape([(1,1),(1,-1),(-1,-1),(-1,1)])`
__Tip__: You can see the default value for a given argument with `default(arg::Symbol)`, and set the default value with `default(arg::Symbol, value)` or `default(; kw...)`. For example set the default window size and whether we should show a legend with `default(size=(600,400), leg=false)`.
__Tip__: There are some helper arguments you can set: `xaxis`, `yaxis`, `line`, `marker`, `fill`. These go through special preprocessing to extract values into individual arguments. The order doesn't matter, and if you pass a single value it's equivalent to wrapping it in a Tuple. Examples:
```
axis # :left or :right
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
label # string or symbol, applies to that line, may go in a legend
width # width of a line
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
markercolor # same choices as `color`, or :match will set the color to be the same as `color`
markersize # size of the marker
nbins # number of bins for heatmap/hexbin and histograms
heatmap_c # color cutoffs for Qwt heatmaps
fillto # fillto value for area plots
title # string or symbol, title of the plot
xlabel # string or symbol, label on the bottom (x) axis
ylabel # string or symbol, label on the left (y) axis
yrightlabel # string or symbol, label on the right (y) axis
reg # true or false, add a regression line for each line
size # (Int,Int), resize the enclosing window
pos # (Int,Int), move the enclosing window to this position
windowtitle # string or symbol, set the title of the enclosing windowtitle
screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
show # true or false, show the plot (in case you don't want the window to pop up right away)
plot(y, xaxis = ("mylabel", :log, :flip, (-1,1))) # this sets the `xlabel`, `xscale`, `xflip`, and `xlims` arguments automatically
plot(y, line = (:bar, :blue, :dot, 10)) # this sets the `linetype`, `color`, `linestyle`, and `linewidth` arguments automatically
plot(y, marker = (:rect, :red, 10)) # this sets the `markershape`, `markercolor`, and `markersize` arguments automatically
plot(y, fill = (:green, 10)) # this sets the `fillcolor` and `fillrange` arguments automatically
# Note: `fillrange` can be:
a number (fill to horizontal line)
a vector of numbers (different for each data point)
a tuple of vectors (fill a band)
```
If you don't include a keyword argument, these are the defaults:
```
axis = :left
color = :auto
label = automatically generated (y1, y2, ...., or y1 (R), y2 (R) for the right axis)
width = 1
linetype = :line
linestype = :solid
marker = :none
markercolor = :match
markersize = 3
nbins = 100
heatmap_c = (0.15, 0.5)
fillto = nothing
title = ""
xlabel = ""
ylabel = ""
yrightlabel = ""
reg = false
size = (800,600)
pos = (0,0)
windowtitle = ""
screen = 1
show = true
__Tip__: When plotting multiple lines, you can set all series to use the same value, or pass in a matrix to cycle through values. Example:
```julia
plot(rand(100,4); color = [:red RGB(0,0,1)], # (Matrix) lines 1 and 3 are red, lines 2 and 4 are blue
axis = :auto, # lines 1 and 3 are on the left axis, lines 2 and 4 are on the right
markershape = [:rect, :star] # (Vector) ALL lines are passed the vector [:rect, :star1]
width = 5) # all lines have a width of 5
```
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
(yes I know it's not gramatically correct, but it's easy to use and implement)
__Tip__: Not all features are supported for each backend, but you can see what's supported by calling the functions: `supportedArgs()`, `supportedAxes()`, `supportedTypes()`, `supportedStyles()`, `supportedMarkers()`, `subplotSupported()`
```
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
```
__Tip__: Call `gui()` to display the plot in a window. Interactivity depends on backend. Plotting at the REPL (without semicolon) implicitly calls `gui()`.
# TODO
### Animations
Animations are created in 3 steps (see example #2):
- Initialize an `Animation` object.
- Save each frame of the animation with `frame(anim)`.
- Convert the frames to an animated gif with `gif(anim, filename, fps=15)`
## TODO features:
- [x] Plot vectors/matrices/functions
- [ ] Plot DataFrames
- [ ] Subplots
- [ ] Histograms
- [x] Plot DataFrames
- [x] Histograms
- [x] Grouping
- [x] Annotations
- [x] Scales
- [x] Categorical Inputs (strings, etc... for hist, bar? or can split one series into multiple?)
- [x] Custom markers
- [x] Animations
- [x] Subplots
- [ ] Contours
- [ ] Boxplots
- [ ] 3D plotting
- [ ] Scenes/Drawing
- [ ] Graphs
- [ ] Interactivity (GUIs)
## TODO backends:
- [x] Gadfly.jl
- [ ] PyPlot.jl
- [ ] Winston.jl
- [ ] Gaston.jl
- [ ] GLPlot.jl
- [x] Immerse.jl
- [x] PyPlot.jl
- [x] UnicodePlots.jl
- [x] Qwt.jl
- [x] Winston.jl (deprecated)
- [ ] GLPlot.jl
- [ ] Bokeh.jl
- [ ] Vega.jl
- [ ] Gaston.jl
- [ ] Plotly.jl
- [ ] GoogleCharts.jl
- [ ] Vega.jl
- [ ] PLplot.jl
- [ ] TextPlots.jl
- [ ] ASCIIPlots.jl
- [ ] Sparklines.jl
- [ ] UnicodePlots.jl
- [ ] Hinton.jl
- [ ] ImageTerm.jl
- [ ] GraphViz.jl
- [ ] TikzGraphs.jl
- [ ] GraphLayout.jl
# Backends
## More information on backends (both supported and unsupported)
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
# Author
Thomas Breloff (@tbreloff)
+3 -2
View File
@@ -1,4 +1,5 @@
julia 0.4-
julia 0.3
Colors
FactCheck
Reexport
Compat
+304 -39
View File
@@ -3,100 +3,223 @@ module PlotExamples
using Plots
using Colors
using Compat
const DOCDIR = Pkg.dir("Plots") * "/docs"
const IMGDIR = Pkg.dir("Plots") * "/img"
doc"""
"""
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
"""
type PlotExample
header::String
desc::String
header::@compat(AbstractString)
desc::@compat(AbstractString)
exprs::Vector{Expr}
end
# the examples we'll run for each
const examples = PlotExample[
PlotExample("Lines",
"A simple line plot of the 3 columns.",
[:(plot(rand(100,3)))]),
PlotExample("Functions",
"Plot multiple functions",
[:(plot(0:0.01:4π, [sin,cos]))]),
"A simple line plot of the columns.",
[
:(plot(Plots.fakedata(50,5), w=3))
]),
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.",
[
:(p = plot([sin,cos], zeros(0), leg=false)),
:(anim = Animation()),
:(for x in linspace(0, 10π, 200)
push!(p, x, Float64[sin(x), cos(x)])
frame(anim)
end)
]),
PlotExample("Parametric plots",
"Plot function pair (x(u), y(u)).",
[
:(plot(sin, x->sin(2x), 0, 2π, line=4, leg=false, fill=(0,:orange)))
]),
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.",
[
:(y = rand(100)),
:(plot(0:10:100,rand(11,4),lab="lines",w=3, palette=:grays, fill=(0.5,:auto))),
:(scatter!(y, z=abs(y-.5), m=(10,:heat), lab="grad"))
]),
PlotExample("Global",
"Change the guides/background without a separate call.",
[:(plot(rand(10); title="TITLE", xlabel="XLABEL", ylabel="YLABEL", background_color = RGB(0.5,0.5,0.5)))]),
"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!`",
[
:(plot(rand(20,3), xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)),
:(title!("TITLE")),
:(yaxis!("YLABEL", :log10))
]),
PlotExample("Two-axis",
"Use the `axis` or `axiss` arguments.\n\nNote: This is only supported with Qwt right now",
[:(plot(Vector[randn(100), randn(100)*100]; axiss = [:left,:right]))]),
PlotExample("Vectors w/ pluralized args",
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).",
[:(plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8, colors=[:red,:blue]))]),
"Use the `axis` arguments.\n\nNote: Currently only supported with Qwt and PyPlot",
[
:(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT"))
]),
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(Vector[rand(10), rand(20)], marker=(:ellipse,8), line=(:dot,3,[:black :orange])))
]),
PlotExample("Build plot in pieces",
"Start with a base plot...",
[:(plot(rand(100)/3; reg=true, fillto=0))]),
[
:(plot(rand(100)/3, reg=true, fill=(0,:green)))
]),
PlotExample("",
"and add to it later.",
[:(scatter!(rand(100); markersize=6, color=:blue))]),
[
:(scatter!(rand(100), markersize=6, c=:orange))
]),
PlotExample("Heatmaps",
"",
[:(heatmap(randn(10000),randn(10000); nbins=200))]),
PlotExample("Lots of line types",
"Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar) \nNote: some may not work with all backends",
[:(plot(rand(20,4); linetypes=[:line, :step, :sticks, :dots]))]),
[
:(heatmap(randn(10000),randn(10000), nbins=100))
]),
PlotExample("Line types",
"",
[
:(types = intersect(supportedTypes(), [:line, :path, :steppre, :steppost, :sticks, :scatter])'),
:(n = length(types)),
:(x = Vector[sort(rand(20)) for i in 1:n]),
:(y = rand(20,n)),
:(plot(x, y, line=(types,3), lab=map(string,types), ms=15))
]),
PlotExample("Line styles",
"",
[
:(styles = setdiff(supportedStyles(), [:auto])'),
:(plot(cumsum(randn(20,length(styles)),1), style=:auto, label=map(string,styles), w=5))
]),
PlotExample("Marker types",
"",
[
:(markers = setdiff(supportedMarkers(), [:none,:auto,Shape])'),
:(n = length(markers)),
:(x = linspace(0,10,n+2)[2:end-1]),
:(y = repmat(reverse(x)', n, 1)),
:(scatter(x, y, m=(8,:auto), lab=map(string,markers), bg=:linen))
]),
PlotExample("Bar",
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
[:(bar(randn(1000)))]),
[
:(bar(randn(999)))
]),
PlotExample("Histogram",
"note: fillto isn't supported on all backends",
[:(histogram(randn(1000); nbins=50, fillto=20))]),
"",
[
:(histogram(randn(1000), nbins=50))
]),
PlotExample("Subplots",
"""
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
number of columns `nc`), or you can set the layout directly with `layout`.
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
number of columns `nc`), or you can set the layout directly with `layout`.
""",
[:(subplot(randn(100,5); layout=[1,1,3], linetypes=[:line,:hist,:dots,:step,:bar], nbins=10, legend=false))]),
[
:(subplot(randn(100,5), layout=[1,1,3], t=[:line :hist :scatter :step :bar], nbins=10, leg=false))
]),
PlotExample("Adding to subplots",
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
[:(subplot(randn(100,5); n=4))]),
[
:(subplot(Plots.fakedata(100,10), n=4, palette=[:grays :blues :heat :lightrainbow], bg=[:orange :pink :darkblue :black]))
]),
PlotExample("",
"",
[:(subplot!(randn(100,3)))]),
[
:(subplot!(Plots.fakedata(100,10)))
]),
PlotExample("Open/High/Low/Close",
"Create an OHLC chart. Pass in a vector of OHLC objects as your `y` argument. Adjust the tick width with arg `markersize`.",
[
:(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; markersize=8))
]),
PlotExample("Annotations",
"Currently only text annotations are supported. Pass in a tuple or vector-of-tuples: (x,y,text). `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`",
[
:(y = rand(10)),
:(plot(y, ann=(3,y[3],text("this is #3",:left)))),
:(annotate!([(5,y[5],text("this is #5",16,:red,:center)),
(10,y[10],text("this is #10",:right,20,"courier"))]))
]),
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.",
[
:(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)])
:(plot(0.1:0.2:0.9, 0.7rand(5)+0.15,
l=(3,:dash,:lightblue),
m=(Shape(verts),30,RGBA(0,0,0,0.2)),
bg=:pink, fg=:darkblue,
xlim = (0,1), ylim=(0,1), leg=false))
])
]
function createStringOfMarkDownCodeValues(arr, prefix = "")
string("`", prefix, join(sort(map(string, arr)), "`, `$prefix"), "`")
end
createStringOfMarkDownSymbols(arr) = isempty(arr) ? "" : createStringOfMarkDownCodeValues(arr, ":")
function generate_markdown(pkgname::Symbol)
# set up the plotter, and don't show the plots by default
plotter!(pkgname)
plotDefault!(:show, false)
# set up the backend, and don't show the plots by default
pkg = backend(pkgname)
# default(:show, false)
# mkdir if necessary
try
mkdir("$IMGDIR/$pkgname")
end
# open the markdown file
md = open("$DOCDIR/$(pkgname)_examples.md", "w")
write(md, "## Examples for backend: $pkgname\n\n")
write(md, "### Initialize\n\n```julia\nusing Plots\n$(pkgname)()\n```\n\n")
for (i,example) in enumerate(examples)
try
# we want to always produce consistent results
srand(1234)
# run the code
map(eval, example.exprs)
# save the png
imgname = "$(pkgname)_example_$i.png"
savepng("$IMGDIR/$imgname")
# # save the png
# imgname = "$(pkgname)_example_$i.png"
# NOTE: uncomment this to overwrite the images as well
if i == 2
imgname = "$(pkgname)_example_$i.gif"
gif(anim, "$IMGDIR/$pkgname/$imgname", fps=15)
else
imgname = "$(pkgname)_example_$i.png"
png("$IMGDIR/$pkgname/$imgname")
end
# write out the header, description, code block, and image link
write(md, "### $(example.header)\n\n")
write(md, "$(example.desc)\n\n")
write(md, "```julia\n$(join(map(string, example.exprs), "\n"))\n```\n\n")
write(md, "![](../img/$imgname)\n\n")
write(md, "![](../img/$pkgname/$imgname)\n\n")
catch ex
# TODO: put error info into markdown?
@@ -106,12 +229,154 @@ function generate_markdown(pkgname::Symbol)
#
end
write(md, "- Supported arguments: $(createStringOfMarkDownCodeValues(supportedArgs(pkg)))\n")
write(md, "- Supported values for axis: $(createStringOfMarkDownSymbols(supportedAxes(pkg)))\n")
write(md, "- Supported values for linetype: $(createStringOfMarkDownSymbols(supportedTypes(pkg)))\n")
write(md, "- Supported values for linestyle: $(createStringOfMarkDownSymbols(supportedStyles(pkg)))\n")
write(md, "- Supported values for marker: $(createStringOfMarkDownSymbols(supportedMarkers(pkg)))\n")
write(md, "- Is `subplot`/`subplot!` supported? $(subplotSupported(pkg) ? "Yes" : "No")\n\n")
write(md, "(Automatically generated: $(now()))")
close(md)
end
# make and display one plot
function test_examples(pkgname::Symbol, idx::Int; debug = true)
Plots._debugMode.on = debug
println("Testing plot: $pkgname:$idx:$(examples[idx].header)")
backend(pkgname)
backend()
map(eval, examples[idx].exprs)
plt = current()
gui(plt)
plt
end
# generate all plots and create a dict mapping idx --> plt
function test_examples(pkgname::Symbol; debug = false)
Plots._debugMode.on = debug
plts = Dict()
for i in 1:length(examples)
# if examples[i].header == "Subplots" && !subplotSupported()
# break
# end
try
plt = test_examples(pkgname, i, debug=debug)
plts[i] = plt
catch ex
# TODO: put error info into markdown?
warn("Example $pkgname:$i:$(examples[i].header) failed with: $ex")
end
end
plts
end
# axis # :left or :right
# color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl
# # Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
# label # string or symbol, applies to that line, may go in a legend
# width # width of a line
# linetype # :line, :step, :stepinverted, :sticks, :scatter, :none, :heatmap, :hexbin, :hist, :bar
# linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
# marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle,
# # :cross, :xcross, :star1, :star2, :hexagon
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
# markersize # size of the marker
# nbins # number of bins for heatmap/hexbin and histograms
# heatmap_c # color cutoffs for Qwt heatmaps
# fill # fill value for area plots
# title # string or symbol, title of the plot
# xlabel # string or symbol, label on the bottom (x) axis
# ylabel # string or symbol, label on the left (y) axis
# yrightlabel # string or symbol, label on the right (y) axis
# reg # true or false, add a regression line for each line
# size # (Int,Int), resize the enclosing window
# pos # (Int,Int), move the enclosing window to this position
# windowtitle # string or symbol, set the title of the enclosing windowtitle
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
@compat const _ltdesc = Dict(
:none => "No line",
:line => "Lines with sorted x-axis",
:path => "Lines",
:steppre => "Step plot (vertical then horizontal)",
:steppost => "Step plot (horizontal then vertical)",
:sticks => "Vertical lines",
:scatter => "Points, no lines",
:heatmap => "Colored regions by density",
:hexbin => "Similar to heatmap",
:hist => "Histogram (doesn't use x)",
:bar => "Bar plot (centered on x values)",
:hline => "Horizontal line (doesn't use x)",
:vline => "Vertical line (doesn't use x)",
:ohlc => "Open/High/Low/Close chart (expects y is AbstractVector{Plots.OHLC})",
)
function buildReadme()
readme = readall("$DOCDIR/readme_template.md")
# build keyword arg table
table = "Keyword | Default | Type | Aliases \n---- | ---- | ---- | ----\n"
allseries = merge(Plots._seriesDefaults, @compat(Dict(:line=>nothing, :marker=>nothing, :fill=>nothing)))
allplots = merge(Plots._plotDefaults, @compat(Dict(:xaxis=>nothing, :yaxis=>nothing)))
alldefs = merge(allseries, allplots)
for k in Plots.sortedkeys(alldefs)
# for d in (Plots._seriesDefaults, Plots._plotDefaults)
# for k in Plots.sortedkeys(d)
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._keyAliases, k))
table = string(table, "`:$k` | `$(alldefs[k])` | $(haskey(allseries,k) ? "Series" : "Plot") | $aliasstr \n")
# end
end
readme = replace(readme, "[[KEYWORD_ARGS_TABLE]]", table)
# build linetypes table
table = "Type | Desc | Aliases\n---- | ---- | ----\n"
for lt in Plots._allTypes
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._typeAliases, lt))
table = string(table, "`:$lt` | $(_ltdesc[lt]) | $aliasstr \n")
end
readme = replace(readme, "[[LINETYPES_TABLE]]", table)
# build linestyles table
table = "Type | Aliases\n---- | ----\n"
for s in Plots._allStyles
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._styleAliases, s))
table = string(table, "`:$s` | $aliasstr \n")
end
readme = replace(readme, "[[LINESTYLES_TABLE]]", table)
# build markers table
table = "Type | Aliases\n---- | ----\n"
for s in Plots._allMarkers
aliasstr = createStringOfMarkDownSymbols(aliases(Plots._markerAliases, s))
table = string(table, "`:$s` | $aliasstr \n")
end
readme = replace(readme, "[[MARKERS_TABLE]]", table)
readme_fn = Pkg.dir("Plots") * "/README.md"
f = open(readme_fn, "w")
write(f, readme)
close(f)
gadfly()
Plots.dumpSupportGraphs()
end
default(size=(500,300))
# run it!
map(generate_markdown, (:qwt, :gadfly))
# note: generate separately so it's easy to comment out
# @osx_only generate_markdown(:unicodeplots)
# generate_markdown(:qwt)
# generate_markdown(:gadfly)
# generate_markdown(:pyplot)
# generate_markdown(:immerse)
# generate_markdown(:winston)
end # module
-148
View File
@@ -1,148 +0,0 @@
### Lines
A simple line plot of the 3 columns.
```julia
plot(rand(100,3))
```
![](../img/gadfly_example_1.png)
### Functions
Plot multiple functions
```julia
plot(0:0.01:4π,[sin,cos])
```
![](../img/gadfly_example_2.png)
### Global
Change the guides/background without a separate call.
```julia
plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5))
```
![](../img/gadfly_example_3.png)
### Two-axis
Use the `axis` or `axiss` arguments.
Note: This is only supported with Qwt right now
```julia
plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right])
```
![](../img/gadfly_example_4.png)
### Vectors w/ pluralized args
Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).
```julia
plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue])
```
![](../img/gadfly_example_5.png)
### Build plot in pieces
Start with a base plot...
```julia
plot(rand(100) / 3; reg=true,fillto=0)
```
![](../img/gadfly_example_6.png)
###
and add to it later.
```julia
scatter!(rand(100); markersize=6,color=:blue)
```
![](../img/gadfly_example_7.png)
### Heatmaps
```julia
heatmap(randn(10000),randn(10000); nbins=200)
```
![](../img/gadfly_example_8.png)
### Lots of line types
Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
Note: some may not work with all backends
```julia
plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots])
```
![](../img/gadfly_example_9.png)
### Bar
x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)
```julia
bar(randn(1000))
```
![](../img/gadfly_example_10.png)
### Histogram
note: fillto isn't supported on all backends
```julia
histogram(randn(1000); nbins=50,fillto=20)
```
![](../img/gadfly_example_11.png)
### Subplots
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
number of columns `nc`), or you can set the layout directly with `layout`.
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
```julia
subplot(randn(100,5); layout=[1,1,3],linetypes=[:line,:hist,:dots,:step,:bar],nbins=10,legend=false)
```
![](../img/gadfly_example_12.png)
### Adding to subplots
Note here the automatic grid layout, as well as the order in which new series are added to the plots.
```julia
subplot(randn(100,5); n=4)
```
![](../img/gadfly_example_13.png)
###
```julia
subplot!(randn(100,3))
```
![](../img/gadfly_example_14.png)
-148
View File
@@ -1,148 +0,0 @@
### Lines
A simple line plot of the 3 columns.
```julia
plot(rand(100,3))
```
![](../img/qwt_example_1.png)
### Functions
Plot multiple functions
```julia
plot(0:0.01:4π,[sin,cos])
```
![](../img/qwt_example_2.png)
### Global
Change the guides/background without a separate call.
```julia
plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5))
```
![](../img/qwt_example_3.png)
### Two-axis
Use the `axis` or `axiss` arguments.
Note: This is only supported with Qwt right now
```julia
plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right])
```
![](../img/qwt_example_4.png)
### Vectors w/ pluralized args
Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`).
```julia
plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue])
```
![](../img/qwt_example_5.png)
### Build plot in pieces
Start with a base plot...
```julia
plot(rand(100) / 3; reg=true,fillto=0)
```
![](../img/qwt_example_6.png)
###
and add to it later.
```julia
scatter!(rand(100); markersize=6,color=:blue)
```
![](../img/qwt_example_7.png)
### Heatmaps
```julia
heatmap(randn(10000),randn(10000); nbins=200)
```
![](../img/qwt_example_8.png)
### Lots of line types
Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
Note: some may not work with all backends
```julia
plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots])
```
![](../img/qwt_example_9.png)
### Bar
x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)
```julia
bar(randn(1000))
```
![](../img/qwt_example_10.png)
### Histogram
note: fillto isn't supported on all backends
```julia
histogram(randn(1000); nbins=50,fillto=20)
```
![](../img/qwt_example_11.png)
### Subplots
subplot and subplot! are distinct commands which create many plots and add series to them in a circular fashion.
You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or
number of columns `nc`), or you can set the layout directly with `layout`.
Note: Gadfly is not very friendly here, and although you can create a plot and save a PNG, I haven't been able to actually display it.
```julia
subplot(randn(100,5); layout=[1,1,3],linetypes=[:line,:hist,:dots,:step,:bar],nbins=10,legend=false)
```
![](../img/qwt_example_12.png)
### Adding to subplots
Note here the automatic grid layout, as well as the order in which new series are added to the plots.
```julia
subplot(randn(100,5); n=4)
```
![](../img/qwt_example_13.png)
###
```julia
subplot!(randn(100,3))
```
![](../img/qwt_example_14.png)
+304
View File
@@ -0,0 +1,304 @@
# Plots
[![Build Status](https://travis-ci.org/tbreloff/Plots.jl.svg?branch=master)](https://travis-ci.org/tbreloff/Plots.jl)
[![Plots](http://pkg.julialang.org/badges/Plots_0.3.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.3)
[![Plots](http://pkg.julialang.org/badges/Plots_0.4.svg)](http://pkg.julialang.org/?pkg=Plots&ver=0.4)
<!-- [![Coverage Status](https://coveralls.io/repos/tbreloff/Plots.jl/badge.svg?branch=master)](https://coveralls.io/r/tbreloff/Plots.jl?branch=master) -->
<!-- [![codecov.io](http://codecov.io/github/tbreloff/Plots.jl/coverage.svg?branch=master)](http://codecov.io/github/tbreloff/Plots.jl?branch=master) -->
#### Author: Thomas Breloff (@tbreloff)
Plots is a plotting API and toolset. My goals with the package are:
- **Intuitive**. Start generating complex plots without reading volumes of documentation. Commands should "just work".
- **Concise**. Less code means fewer mistakes and more efficient development/analysis.
- **Flexible**. Produce your favorite plots from your favorite package, but quicker and simpler.
- **Consistent**. Don't commit to one graphics package. Use the same code and access the strengths of all backends.
- **Lightweight**. Very few dependencies, since backends are loaded and initialized dynamically.
Use the preprocessing pipeline in Plots to fully describe your visualization before it calls the backend code. This maintains modularity and allows for efficient separation of front end code, algorithms, and backend graphics. New graphical backends can be added with minimal effort.
Check out the [summary graphs](https://github.com/tbreloff/ExamplePlots.jl/tree/master/img/supported/supported.md) for the features that each backend supports.
Please add wishlist items, bugs, or any other comments/questions to the issues list.
## Examples for each implemented backend:
- [Gadfly.jl/Immerse.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/gadfly_examples.md)
- [PyPlot.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/pyplot_examples.md)
- [UnicodePlots.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/unicodeplots_examples.md)
- [Qwt.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/qwt_examples.md)
Also check out the many [IJulia notebooks](http://nbviewer.ipython.org/github/tbreloff/ExamplePlots.jl/tree/master/examples/) with many examples.
## Installation
First, add the package
```julia
Pkg.add("Plots")
# if you want the latest features:
Pkg.checkout("Plots")
# or for the bleeding edge:
Pkg.checkout("Plots", "dev")
```
then get any plotting packages you need (obviously, you should get at least one backend).
```julia
Pkg.add("Gadfly")
Pkg.add("Immerse")
Pkg.add("PyPlot")
Pkg.add("UnicodePlots")
Pkg.clone("https://github.com/tbreloff/Qwt.jl.git")
```
## Use
Load it in. The underlying plotting backends are not imported until `backend()` is called (which happens
on your first call to `plot` or `subplot`). This means that you don't need any backends to be installed when you call `using Plots`.
Plots will try to figure out a good default backend for you automatically based on what backends are installed.
```julia
using Plots
```
Do a plot in Gadfly (inspired by [this example](http://gadflyjl.org/geom_point.html)), then save a png:
```julia
gadfly() # switch to Gadfly as a backend
dataframes() # turn on support for DataFrames inputs
# load some data
using RDatasets
iris = dataset("datasets", "iris");
# This will bring up a browser window with the plot. Add a semicolon at the end to skip display.
scatter(iris, :SepalLength, :SepalWidth, group=:Species, m=([:+ :d :s], 12), smooth=0.99, bg=:black)
# save a png (equivalent to png("gadfly1.png") and savefig("gadfly1.png"))
png("gadfly1")
```
![gadfly_plt](img/gadfly1.png)
## API
Call `backend(backend::Symbol)` or the shorthands (`gadfly()`, `qwt()`, `unicodeplots()`, etc) to set the current plotting backend.
Subsequent commands are converted into the relevant plotting commands for that package:
```julia
gadfly()
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
qwt()
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```julia
plot(args...; kw...) # creates a new plot window, and sets it to be the `current`
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
There are many ways to pass in data to the plot functions... some examples:
- Vector-like (subtypes of AbstractArray{T,1})
- Matrix-like (subtypes of AbstractArray{T,2})
- Vectors of Vectors
- Functions
- Vectors of Functions
- DataFrames with column symbols (initialize with `dataframes()`)
In general, you can pass in a `y` only, or an `x` and `y`, both of whatever type(s) you want, and Plots will slice up the data as needed.
For matrices, data is split by columns. For functions, data is mapped. For DataFrames, a Symbol/Symbols in place of x/y will map to
the relevant column(s).
Here are some example usages... remember you can always use `plot!` to update an existing plot, and that, unless specified, you will update the `current()`.
```julia
plot() # empty plot object
plot(4) # initialize with 4 empty series
plot(rand(10)) # plot 1 series... x = 1:10
plot(rand(10,5)) # plot 5 series... x = 1:10
plot(rand(10), rand(10)) # plot 1 series
plot(rand(10,5), rand(10)) # plot 5 series... y is the same for all
plot(sin, rand(10)) # y = sin(x)
plot(rand(10), sin) # same... y = sin(x)
plot([sin,cos], 0:0.1:π) # plot 2 series, sin(x) and cos(x)
plot([sin,cos], 0, π) # plot sin and cos on the range [0, π]
plot(1:10, Any[rand(10), sin]) # plot 2 series, y = rand(10) for the first, y = sin(x) for the second... x = 1:10 for both
plot(dataset("Ecdat", "Airline"), :Cost) # plot from a DataFrame (call `dataframes()` first to import DataFrames and initialize)
```
All plot methods accept a number of keyword arguments (see the tables below), which follow some rules:
- Many arguments have aliases which are replaced during preprocessing. `c` is the same as `color`, `m` is the same as `marker`, etc. You can choose how verbose you'd like to be. (see the tables below)
- There are some special arguments (`xaxis`, `yaxis`, `line`, `marker`, `fill` and the aliases `l`, `m`, `f`) which magically set many related things at once. (see the __Tip__ below)
- If the argument is a "matrix-type", then each column will map to a series, cycling through columns if there are fewer columns than series. Anything else will apply the argument value to every series.
- Many arguments accept many different types... for example the `color` (also `markercolor`, `fillcolor`, etc) argument will accept strings or symbols with a color name, or any `Colors.Colorant`, or a `ColorScheme`, or a symbol representing a `ColorGradient`, or an AbstractVector of colors/symbols/etc...
You can update certain plot settings after plot creation (not supported on all backends):
```julia
plot!(title = "New Title", xlabel = "New xlabel", ylabel = "New ylabel")
plot!(xlims = (0, 5.5), ylims = (-2.2, 6), xticks = 0:0.5:10, yticks = [0,1,5,10])
# using shorthands:
xaxis!("mylabel", :log10, :flip)
```
With `subplot`, create multiple plots at once, with flexible layout options:
```julia
y = rand(100,3)
subplot(y; n = 3) # create an automatic grid, and let it figure out the shape
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns
subplot(y; layout = [1, 2]) # explicit layout. Lists the number of plots in each row
```
__Tip__: You can call `subplot!(args...; kw...)` to add to an existing subplot.
__Tip__: Calling `subplot!` on a `Plot` object, or `plot!` on a `Subplot` object will throw an error.
Shorthands:
```julia
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
title!(s::AbstractString) = plot!(title = s)
xlabel!(s::AbstractString) = plot!(xlabel = s)
ylabel!(s::AbstractString) = plot!(ylabel = s)
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(xlims = lims)
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(ylims = lims)
xticks!{T<:Real}(v::AVec{T}) = plot!(xticks = v)
yticks!{T<:Real}(v::AVec{T}) = plot!(yticks = v)
xflip!(flip::Bool = true) = plot!(xflip = flip)
yflip!(flip::Bool = true) = plot!(yflip = flip)
xaxis!(args...) = plot!(xaxis = args)
yaxis!(args...) = plot!(yaxis = args)
annotate!(anns) = plot!(annotation = anns)
```
### Keyword arguments:
[[KEYWORD_ARGS_TABLE]]
### Plot types:
[[LINETYPES_TABLE]]
### Line styles:
[[LINESTYLES_TABLE]]
### Markers:
[[MARKERS_TABLE]]
__Tip__: With supported backends, you can pass a `Plots.Shape` object for the `marker`/`markershape` arguments. `Shape` takes a vector of 2-tuples in the constructor, defining the points of the polygon's shape in a unit-scaled coordinate space. To make a square, for example, you could do `Shape([(1,1),(1,-1),(-1,-1),(-1,1)])`
__Tip__: You can see the default value for a given argument with `default(arg::Symbol)`, and set the default value with `default(arg::Symbol, value)` or `default(; kw...)`. For example set the default window size and whether we should show a legend with `default(size=(600,400), leg=false)`.
__Tip__: There are some helper arguments you can set: `xaxis`, `yaxis`, `line`, `marker`, `fill`. These go through special preprocessing to extract values into individual arguments. The order doesn't matter, and if you pass a single value it's equivalent to wrapping it in a Tuple. Examples:
```
plot(y, xaxis = ("mylabel", :log, :flip, (-1,1))) # this sets the `xlabel`, `xscale`, `xflip`, and `xlims` arguments automatically
plot(y, line = (:bar, :blue, :dot, 10)) # this sets the `linetype`, `color`, `linestyle`, and `linewidth` arguments automatically
plot(y, marker = (:rect, :red, 10)) # this sets the `markershape`, `markercolor`, and `markersize` arguments automatically
plot(y, fill = (:green, 10)) # this sets the `fillcolor` and `fillrange` arguments automatically
# Note: `fillrange` can be:
a number (fill to horizontal line)
a vector of numbers (different for each data point)
a tuple of vectors (fill a band)
```
__Tip__: When plotting multiple lines, you can set all series to use the same value, or pass in a matrix to cycle through values. Example:
```julia
plot(rand(100,4); color = [:red RGB(0,0,1)], # (Matrix) lines 1 and 3 are red, lines 2 and 4 are blue
axis = :auto, # lines 1 and 3 are on the left axis, lines 2 and 4 are on the right
markershape = [:rect, :star] # (Vector) ALL lines are passed the vector [:rect, :star1]
width = 5) # all lines have a width of 5
```
__Tip__: Not all features are supported for each backend, but you can see what's supported by calling the functions: `supportedArgs()`, `supportedAxes()`, `supportedTypes()`, `supportedStyles()`, `supportedMarkers()`, `subplotSupported()`
__Tip__: Call `gui()` to display the plot in a window. Interactivity depends on backend. Plotting at the REPL (without semicolon) implicitly calls `gui()`.
### Animations
Animations are created in 3 steps (see example #2):
- Initialize an `Animation` object.
- Save each frame of the animation with `frame(anim)`.
- Convert the frames to an animated gif with `gif(anim, filename, fps=15)`
## TODO features:
- [x] Plot vectors/matrices/functions
- [x] Plot DataFrames
- [x] Histograms
- [x] Grouping
- [x] Annotations
- [x] Scales
- [x] Categorical Inputs (strings, etc... for hist, bar? or can split one series into multiple?)
- [x] Custom markers
- [x] Animations
- [x] Subplots
- [ ] Contours
- [ ] Boxplots
- [ ] 3D plotting
- [ ] Scenes/Drawing
- [ ] Graphs
- [ ] Interactivity (GUIs)
## TODO backends:
- [x] Gadfly.jl
- [x] Immerse.jl
- [x] PyPlot.jl
- [x] UnicodePlots.jl
- [x] Qwt.jl
- [x] Winston.jl (deprecated)
- [ ] GLPlot.jl
- [ ] Bokeh.jl
- [ ] Vega.jl
- [ ] Gaston.jl
- [ ] Plotly.jl
- [ ] GoogleCharts.jl
- [ ] PLplot.jl
- [ ] TextPlots.jl
- [ ] ASCIIPlots.jl
- [ ] Sparklines.jl
- [ ] Hinton.jl
- [ ] ImageTerm.jl
- [ ] GraphViz.jl
- [ ] TikzGraphs.jl
- [ ] GraphLayout.jl
## More information on backends (both supported and unsupported)
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+23
View File
@@ -0,0 +1,23 @@
## Supported keyword arguments
![args](Plots.supportGraphArgs.png)
## Supported plot types
![types](Plots.supportGraphTypes.png)
## Supported markers
![markers](Plots.supportGraphMarkers.png)
## Supported line styles
![styles](Plots.supportGraphStyles.png)
## Supported scales
![scales](Plots.supportGraphScales.png)
## Supported axes
![axes](Plots.supportGraphAxes.png)
+4
View File
@@ -0,0 +1,4 @@
git checkout master
git merge --ff-only dev
git push origin master
git checkout dev
+162 -27
View File
@@ -1,33 +1,97 @@
__precompile__()
if VERSION >= v"0.4-"
__precompile__()
end
module Plots
using Colors
using Compat
using Reexport
@reexport using Colors
export
plotter,
plot,
subplot,
plotter!,
plot!,
# plot_display,
# plot_display!,
subplot,
subplot!,
currentPlot,
plotDefault,
current,
default,
scatter,
bar,
histogram,
heatmap,
currentPlot!,
plotDefault!,
scatter!,
bar,
bar!,
histogram,
histogram!,
heatmap,
heatmap!,
sticks,
sticks!,
hline,
hline!,
vline,
vline!,
ohlc,
ohlc!,
savepng
title!,
xlabel!,
ylabel!,
xlims!,
ylims!,
xticks!,
yticks!,
annotate!,
xflip!,
yflip!,
xaxis!,
yaxis!,
savefig,
png,
gui,
backend,
backends,
aliases,
dataframes,
Shape,
text,
font,
OHLC,
colorscheme,
ColorScheme,
ColorGradient,
ColorVector,
ColorWrapper,
ColorFunction,
ColorZFunction,
getColor,
getColorZ,
debugplots,
supportedArgs,
supportedAxes,
supportedTypes,
supportedStyles,
supportedMarkers,
subplotSupported,
Animation,
frame,
gif,
# recipes
PlotRecipe,
EllipseRecipe,
spy,
corrplot
# ---------------------------------------------------------
@@ -39,38 +103,109 @@ const IMG_DIR = Pkg.dir("Plots") * "/img/"
include("types.jl")
include("utils.jl")
# ---------------------------------------------------------
include("qwt.jl")
include("gadfly.jl")
include("colors.jl")
include("plotter.jl")
# ---------------------------------------------------------
include("args.jl")
include("plot.jl")
include("subplot.jl")
include("recipes.jl")
include("animation.jl")
include("output.jl")
# ---------------------------------------------------------
scatter(args...; kw...) = plot(args...; kw..., linetype = :none, marker = :hexagon)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :none, marker = :hexagon)
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
title!(s::@compat(AbstractString); kw...) = plot!(; title = s, kw...)
xlabel!(s::@compat(AbstractString); kw...) = plot!(; xlabel = s, kw...)
ylabel!(s::@compat(AbstractString); kw...) = plot!(; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(lims::@compat(Tuple{T,S}); kw...) = plot!(; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(lims::@compat(Tuple{T,S}); kw...) = plot!(; ylims = lims, kw...)
xlims!(xmin::Real, xmax::Real; kw...) = plot!(; xlims = (xmin,xmax), kw...)
ylims!(ymin::Real, ymax::Real; kw...) = plot!(; ylims = (ymin,ymax), kw...)
xticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; xticks = v, kw...)
yticks!{T<:Real}(v::AVec{T}; kw...) = plot!(; yticks = v, kw...)
xticks!{T<:Real,S<:@compat(AbstractString)}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:@compat(AbstractString)}(
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(; yticks = (ticks,labels), kw...)
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
annotate!{T<:Tuple}(anns::AVec{T}; kw...) = plot!(; annotation = anns, kw...)
xflip!(flip::Bool = true; kw...) = plot!(; xflip = flip, kw...)
yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip, kw...)
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
title!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; title = s, kw...)
xlabel!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; xlabel = s, kw...)
ylabel!(plt::Plot, s::@compat(AbstractString); kw...) = plot!(plt; ylabel = s, kw...)
xlims!{T<:Real,S<:Real}(plt::Plot, lims::@compat(Tuple{T,S}); kw...) = plot!(plt; xlims = lims, kw...)
ylims!{T<:Real,S<:Real}(plt::Plot, lims::@compat(Tuple{T,S}); kw...) = plot!(plt; ylims = lims, kw...)
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
xticks!{T<:Real,S<:@compat(AbstractString)}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
yticks!{T<:Real,S<:@compat(AbstractString)}(plt::Plot,
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
xflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
yflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
xaxis!(plt::Plot, args...; kw...) = plot!(plt; xaxis = args, kw...)
yaxis!(plt::Plot, args...; kw...) = plot!(plt; yaxis = args, kw...)
# ---------------------------------------------------------
savepng(args...; kw...) = savepng(currentPlot(), args...; kw...)
savepng(plt::PlottingObject, args...; kw...) = savepng(plt.plotter, plt, args...; kw...)
try
import DataFrames
dataframes()
end
# const CURRENT_BACKEND = pickDefaultBackend()
# for be in backends()
# try
# backend(be)
# backend()
# catch err
# @show err
# end
# end
function __init__()
global const CURRENT_BACKEND = pickDefaultBackend()
# global CURRENT_BACKEND
# println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym)
# # auto init dataframes if the import statement doesn't error out
# try
# @eval import DataFrames
# dataframes()
# end
end
# ---------------------------------------------------------
+54
View File
@@ -0,0 +1,54 @@
immutable Animation{P<:PlottingObject}
plt::P
dir::ASCIIString
frames::Vector{ASCIIString}
end
function Animation(plt::PlottingObject)
Animation(plt, mktempdir(), ASCIIString[])
end
Animation() = Animation(current())
function frame(anim::Animation)
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(anim.plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
end
# -----------------------------------------------
"Wraps the location of an animated gif so that it can be displayed"
immutable AnimatedGif
filename::ASCIIString
end
function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Integer = 20)
fn = abspath(fn)
try
# high quality
speed = round(Int, 100 / fps)
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png $fn`)
catch err
warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)")
# low quality
run(`ffmpeg -v 0 -framerate $fps -i $(anim.dir)/%06d.png -y $fn`)
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
end
info("Saved animation to ", fn)
AnimatedGif(fn)
end
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
end
+642 -98
View File
@@ -1,133 +1,677 @@
# const COLORS = [:black, :blue, :green, :red, :darkGray, :darkCyan, :darkYellow, :darkMagenta,
# :darkBlue, :darkGreen, :darkRed, :gray, :cyan, :yellow, :magenta]
const COLORS = distinguishable_colors(20)
const NUMCOLORS = length(COLORS)
const _allAxes = [:auto, :left, :right]
@compat const _axesAliases = Dict(
:a => :auto,
:l => :left,
:r => :right
)
# these are valid choices... first one is default value if unset
const LINE_AXES = (:left, :right)
const LINE_TYPES = (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
const LINE_STYLES = (:solid, :dash, :dot, :dashdot, :dashdotdot)
const LINE_MARKERS = (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon)
const _allTypes = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
:heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc, :contour]
@compat const _typeAliases = Dict(
:n => :none,
:no => :none,
:l => :line,
:p => :path,
:stepinv => :steppre,
:stepsinv => :steppre,
:stepinverted => :steppre,
:stepsinverted => :steppre,
:step => :steppost,
:steps => :steppost,
:stair => :steppost,
:stairs => :steppost,
:stem => :sticks,
:stems => :sticks,
:dots => :scatter,
:histogram => :hist,
:contours => :contours,
)
const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
@compat const _styleAliases = Dict(
:a => :auto,
:s => :solid,
:d => :dash,
:dd => :dashdot,
:ddd => :dashdotdot,
)
# const _allMarkers = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle,
# :cross, :xcross, :star5, :star8, :hexagon, :octagon, Shape]
const _allMarkers = vcat(:none, :auto, sort(collect(keys(_shapes))))
@compat const _markerAliases = Dict(
:n => :none,
:no => :none,
:a => :auto,
:circle => :ellipse,
:c => :ellipse,
:square => :rect,
:sq => :rect,
:r => :rect,
:d => :diamond,
:^ => :utriangle,
:ut => :utriangle,
:utri => :utriangle,
:uptri => :utriangle,
:uptriangle => :utriangle,
:v => :dtriangle,
:V => :dtriangle,
:dt => :dtriangle,
:dtri => :dtriangle,
:downtri => :dtriangle,
:downtriangle => :dtriangle,
:+ => :cross,
:plus => :cross,
:x => :xcross,
:X => :xcross,
:star => :star5,
:s => :star5,
:star1 => :star5,
:s2 => :star8,
:star2 => :star8,
:p => :pentagon,
:pent => :pentagon,
:h => :hexagon,
:hex => :hexagon,
:hep => :heptagon,
:o => :octagon,
:oct => :octagon,
)
const _allScales = [:identity, :log, :log2, :log10, :asinh, :sqrt]
@compat const _scaleAliases = Dict(
:none => :identity,
:ln => :log,
)
supportedAxes(::PlottingPackage) = _allAxes
supportedTypes(::PlottingPackage) = _allTypes
supportedStyles(::PlottingPackage) = _allStyles
supportedMarkers(::PlottingPackage) = _allMarkers
supportedScales(::PlottingPackage) = _allScales
subplotSupported(::PlottingPackage) = true
supportedAxes() = supportedAxes(backend())
supportedTypes() = supportedTypes(backend())
supportedStyles() = supportedStyles(backend())
supportedMarkers() = supportedMarkers(backend())
supportedScales() = supportedScales(backend())
subplotSupported() = subplotSupported(backend())
# -----------------------------------------------------------------------------
const PLOT_DEFAULTS = Dict{Symbol, Any}()
const _seriesDefaults = Dict{Symbol, Any}()
# series-specific
PLOT_DEFAULTS[:axis] = :left
PLOT_DEFAULTS[:color] = :auto
PLOT_DEFAULTS[:label] = "AUTO"
PLOT_DEFAULTS[:width] = 1
PLOT_DEFAULTS[:linetype] = :line
PLOT_DEFAULTS[:linestyle] = :solid
PLOT_DEFAULTS[:marker] = :none
PLOT_DEFAULTS[:markercolor] = :match
PLOT_DEFAULTS[:markersize] = 3
PLOT_DEFAULTS[:nbins] = 100 # number of bins for heatmaps and hists
PLOT_DEFAULTS[:heatmap_c] = (0.15, 0.5)
PLOT_DEFAULTS[:fillto] = nothing # fills in the area
PLOT_DEFAULTS[:reg] = false # regression line?
_seriesDefaults[:axis] = :left
_seriesDefaults[:color] = :auto
_seriesDefaults[:label] = "AUTO"
_seriesDefaults[:linetype] = :path
_seriesDefaults[:linestyle] = :solid
_seriesDefaults[:linewidth] = 1
_seriesDefaults[:lineopacity] = nothing
_seriesDefaults[:markershape] = :none
_seriesDefaults[:markercolor] = :match
_seriesDefaults[:markeropacity] = nothing
_seriesDefaults[:markersize] = 6
_seriesDefaults[:fillrange] = nothing # ribbons, areas, etc
_seriesDefaults[:fillcolor] = :match
_seriesDefaults[:fillopacity] = nothing
# _seriesDefaults[:ribbon] = nothing
# _seriesDefaults[:ribboncolor] = :match
_seriesDefaults[:nbins] = 30 # number of bins for heatmaps and hists
# _seriesDefaults[:heatmap_c] = (0.15, 0.5) # TODO: this should be replaced with a ColorGradient
# _seriesDefaults[:fill] = nothing # fills in the area
_seriesDefaults[:smooth] = false # regression line?
_seriesDefaults[:group] = nothing # groupby vector
_seriesDefaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation)
_seriesDefaults[:z] = nothing # depth for contour, color scale, etc
# _seriesDefaults[:args] = [] # additional args to pass to the backend
# _seriesDefaults[:kwargs] = [] # additional keyword args to pass to the backend
# # note: can be Vector{Dict} or Vector{Tuple}
_seriesDefaults[:surface] = nothing
_seriesDefaults[:nlevels] = 15
const _plotDefaults = Dict{Symbol, Any}()
# plot globals
PLOT_DEFAULTS[:title] = ""
PLOT_DEFAULTS[:xlabel] = ""
PLOT_DEFAULTS[:ylabel] = ""
PLOT_DEFAULTS[:yrightlabel] = ""
PLOT_DEFAULTS[:legend] = true
# PLOT_DEFAULTS[:background_color] = nothing
PLOT_DEFAULTS[:xticks] = true
PLOT_DEFAULTS[:yticks] = true
PLOT_DEFAULTS[:size] = (600,400)
_plotDefaults[:title] = ""
_plotDefaults[:xlabel] = ""
_plotDefaults[:ylabel] = ""
_plotDefaults[:yrightlabel] = ""
_plotDefaults[:legend] = true
_plotDefaults[:background_color] = colorant"white"
_plotDefaults[:foreground_color] = :auto
_plotDefaults[:xlims] = :auto
_plotDefaults[:ylims] = :auto
_plotDefaults[:xticks] = :auto
_plotDefaults[:yticks] = :auto
_plotDefaults[:xscale] = :identity
_plotDefaults[:yscale] = :identity
_plotDefaults[:xflip] = false
_plotDefaults[:yflip] = false
_plotDefaults[:size] = (600,400)
_plotDefaults[:pos] = (0,0)
_plotDefaults[:windowtitle] = "Plots.jl"
_plotDefaults[:show] = false
_plotDefaults[:layout] = nothing
_plotDefaults[:n] = -1
_plotDefaults[:nr] = -1
_plotDefaults[:nc] = -1
_plotDefaults[:color_palette] = :auto
_plotDefaults[:link] = false
_plotDefaults[:linkx] = false
_plotDefaults[:linky] = false
_plotDefaults[:linkfunc] = nothing
_plotDefaults[:tickfont] = font(8)
_plotDefaults[:guidefont] = font(11)
_plotDefaults[:legendfont] = font(8)
_plotDefaults[:grid] = true
# TODO: x/y scales
const _allArgs = sort(collect(union(keys(_seriesDefaults), keys(_plotDefaults))))
supportedArgs(::PlottingPackage) = _allArgs
supportedArgs() = supportedArgs(backend())
@compat const _argNotes = Dict(
:color => "Series color. To have different marker and/or fill colors, optionally set the markercolor and fillcolor args.",
:z => "Determines the depth. For color gradients, we expect 0 ≤ z ≤ 1.",
# :heatmap_c => "For Qwt heatmaps only... will be deprecated eventually.",
)
# -----------------------------------------------------------------------------
plotDefault(sym::Symbol) = PLOT_DEFAULTS[sym]
function plotDefault!(sym::Symbol, val)
PLOT_DEFAULTS[sym] = val
makeplural(s::Symbol) = symbol(string(s,"s"))
autopick(arr::AVec, idx::Integer) = arr[mod1(idx,length(arr))]
autopick(notarr, idx::Integer) = notarr
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
function aliasesAndAutopick(d::Dict, sym::Symbol, aliases::Dict, options::AVec, plotIndex::Int)
if d[sym] == :auto
d[sym] = autopick_ignore_none_auto(options, plotIndex)
elseif haskey(aliases, d[sym])
d[sym] = aliases[d[sym]]
end
end
function aliases(aliasMap::Dict, val)
# sort(vcat(val, collect(keys(filter((k,v)-> v==val, aliasMap)))))
sortedkeys(filter((k,v)-> v==val, aliasMap))
end
# -----------------------------------------------------------------------------
makeplural(s::Symbol) = Symbol(string(s,"s"))
autocolor(idx::Integer) = COLORS[mod1(idx,NUMCOLORS)]
# Alternate args
@compat const _keyAliases = Dict(
:c => :color,
:lab => :label,
:l => :line,
:w => :linewidth,
:width => :linewidth,
:lw => :linewidth,
:lo => :lineopacity,
:type => :linetype,
:lt => :linetype,
:t => :linetype,
:style => :linestyle,
:s => :linestyle,
:ls => :linestyle,
:m => :marker,
:mark => :marker,
:shape => :markershape,
:mc => :markercolor,
:mcolor => :markercolor,
:ms => :markersize,
:msize => :markersize,
:mo => :markeropacity,
:opacity => :markeropacity,
:alpha => :markeropacity,
:f => :fill,
:area => :fill,
:fillrng => :fillrange,
:fc => :fillcolor,
:fcolor => :fillcolor,
:fo => :fillopacity,
:g => :group,
:nb => :nbins,
:nbin => :nbins,
:rib => :ribbon,
:ann => :annotation,
:anns => :annotation,
:annotate => :annotation,
:annotations => :annotation,
:xlab => :xlabel,
:ylab => :ylabel,
:yrlab => :yrightlabel,
:ylabr => :yrightlabel,
:y2lab => :yrightlabel,
:ylab2 => :yrightlabel,
:ylabelright => :yrightlabel,
:ylabel2 => :yrightlabel,
:y2label => :yrightlabel,
:leg => :legend,
:bg => :background_color,
:bgcolor => :background_color,
:bg_color => :background_color,
:background => :background_color,
:fg => :foreground_color,
:fgcolor => :foreground_color,
:fg_color => :foreground_color,
:foreground => :foreground_color,
:regression => :smooth,
:reg => :smooth,
:xlim => :xlims,
:xlimit => :xlims,
:xlimits => :xlims,
:ylim => :ylims,
:ylimit => :ylims,
:ylimits => :ylims,
:xtick => :xticks,
:ytick => :yticks,
:windowsize => :size,
:wsize => :size,
:wtitle => :windowtitle,
:gui => :show,
:display => :show,
:palette => :color_palette,
:xlink => :linkx,
:ylink => :linky,
)
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
# note: if plt is nothing, we aren't doing anything with the color anyways
function getRGBColor(c, n::Int = 0)
# auto-assign a color based on plot index
if c == :auto && n > 0
c = autocolor(n)
end
# convert it from a symbol/string
if isa(c, Symbol)
c = string(c)
end
if isa(c, String)
c = parse(Colorant, c)
end
# should be a RGB now... either it was passed in, generated automatically, or created from a string
@assert isa(c, RGB)
# return the RGB
c
# add all pluralized forms to the _keyAliases dict
for arg in keys(_seriesDefaults)
_keyAliases[makeplural(arg)] = arg
end
# note: idx is the index of this series within this call, n is the index of the series from all calls to plot/subplot
function getPlotKeywordArgs(kw, idx::Int, n::Int)
d = Dict(kw)
# default to a white background, but only on the initial call (so we don't change the background automatically)
if haskey(d, :background_color)
d[:background_color] = getRGBColor(d[:background_color])
elseif n == 0
d[:background_color] = colorant"white"
end
# -----------------------------------------------------------------------------
# fill in d with either 1) plural value, 2) value, 3) default
for k in keys(PLOT_DEFAULTS)
plural = makeplural(k)
# if haskey(d, plural)
# d[k] = d[plural][idx]
if !haskey(d, k)
if n == 0 || k != :size
d[k] = haskey(d, plural) ? d[plural][idx] : PLOT_DEFAULTS[k]
end
end
delete!(d, plural)
end
# update the defaults globally
"""
`default(key)` returns the current default value for that key
`default(key, value)` sets the current default value for that key
`default(; kw...)` will set the current default value for each key/value pair
"""
# handle plot initialization differently
if n == 0
delete!(d, :x)
delete!(d, :y)
function default(k::Symbol)
k = get(_keyAliases, k, k)
if haskey(_seriesDefaults, k)
return _seriesDefaults[k]
elseif haskey(_plotDefaults, k)
return _plotDefaults[k]
else
# once the plot is created, we can get line/marker colors
# update color
d[:color] = getRGBColor(d[:color], n)
# update markercolor
mc = d[:markercolor]
mc = (mc == :match ? d[:color] : getRGBColor(mc, n))
d[:markercolor] = mc
# set label
label = d[:label]
d[:label] = string(label == "AUTO" ? "y_$n" : label, d[:axis] == :left ? "" : " (R)")
error("Unknown key: ", k)
end
end
function default(k::Symbol, v)
k = get(_keyAliases, k, k)
if haskey(_seriesDefaults, k)
_seriesDefaults[k] = v
elseif haskey(_plotDefaults, k)
_plotDefaults[k] = v
else
error("Unknown key: ", k)
end
end
function default(; kw...)
for (k,v) in kw
default(k, v)
end
end
# -----------------------------------------------------------------------------
wraptuple(x::@compat(Tuple)) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
function handleColors!(d::Dict, arg, csym::Symbol)
try
if arg == :auto
d[csym] = :auto
else
c = colorscheme(arg)
d[csym] = c
end
return true
end
false
end
# given one value (:log, or :flip, or (-1,1), etc), set the appropriate arg
# TODO: use trueOrAllTrue for subplots which can pass vectors for these
function processAxisArg(d::Dict, axisletter::@compat(AbstractString), arg)
T = typeof(arg)
# if T <: Symbol
arg = get(_scaleAliases, arg, arg)
if arg in _allScales
d[symbol(axisletter * "scale")] = arg
elseif arg in (:flip, :invert, :inverted)
d[symbol(axisletter * "flip")] = true
elseif T <: @compat(AbstractString)
d[symbol(axisletter * "label")] = arg
# xlims/ylims
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
d[symbol(axisletter * "lims")] = arg
# xticks/yticks
elseif T <: AVec
d[symbol(axisletter * "ticks")] = arg
elseif arg == nothing
d[symbol(axisletter * "ticks")] = []
else
warn("Skipped $(axisletter)axis arg $arg")
end
end
function processLineArg(d::Dict, arg)
# linetype
if trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
d[:linetype] = arg
# linestyle
elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
d[:linestyle] = arg
# linewidth
elseif trueOrAllTrue(a -> typeof(a) <: Integer, arg)
d[:linewidth] = arg
# lineopacity
elseif trueOrAllTrue(a -> typeof(a) <: Real && a >= 0 && a <= 1, arg)
d[:lineopacity] = arg
# color
elseif !handleColors!(d, arg, :color)
warn("Skipped line arg $arg.")
end
end
function processMarkerArg(d::Dict, arg)
# markershape
if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg)
d[:markershape] = arg
elseif trueOrAllTrue(a -> isa(a, Shape), arg)
d[:markershape] = arg
# markersize
elseif trueOrAllTrue(a -> typeof(a) <: Integer, arg)
d[:markersize] = arg
# lineopacity
elseif trueOrAllTrue(a -> typeof(a) <: Real && a >= 0 && a <= 1, arg)
d[:markeropacity] = arg
# markercolor
elseif !handleColors!(d, arg, :markercolor)
warn("Skipped marker arg $arg.")
end
end
function processFillArg(d::Dict, arg)
if !handleColors!(d, arg, :fillcolor)
d[:fillrange] = arg
end
end
"Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
function preprocessArgs!(d::Dict)
replaceAliases!(d, _keyAliases)
# handle axis args
for axisletter in ("x", "y")
asym = symbol(axisletter * "axis")
for arg in wraptuple(get(d, asym, ()))
processAxisArg(d, axisletter, arg)
end
delete!(d, asym)
end
# handle line args
for arg in wraptuple(get(d, :line, ()))
processLineArg(d, arg)
end
delete!(d, :line)
# handle marker args... default to ellipse if shape not set
anymarker = false
for arg in wraptuple(get(d, :marker, ()))
processMarkerArg(d, arg)
anymarker = true
end
delete!(d, :marker)
if anymarker && !haskey(d, :markershape)
d[:markershape] = :ellipse
end
# handle fill
for arg in wraptuple(get(d, :fill, ()))
processFillArg(d, arg)
end
delete!(d, :fill)
# handle subplot links
if haskey(d, :link)
l = d[:link]
if isa(l, Bool)
d[:linkx] = l
d[:linky] = l
elseif isa(l, Function)
d[:linkx] = true
d[:linky] = true
d[:linkfunc] = l
else
warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)")
end
delete!(d, :link)
end
return
end
# -----------------------------------------------------------------------------
"A special type that will break up incoming data into groups, and allow for easier creation of grouped plots"
type GroupBy
groupLabels::Vector{UTF8String} # length == numGroups
groupIds::Vector{Vector{Int}} # list of indices for each group
end
# this is when given a vector-type of values to group by
function extractGroupArgs(v::AVec, args...)
groupLabels = sort(collect(unique(v)))
n = length(groupLabels)
if n > 20
error("Too many group labels. n=$n Is that intended?")
end
groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels]
GroupBy(map(string, groupLabels), groupIds)
end
# expecting a mapping of "group label" to "group indices"
function extractGroupArgs{T, V<:AVec{Int}}(idxmap::Dict{T,V}, args...)
groupLabels = sortedkeys(idxmap)
groupIds = VecI[collect(idxmap[k]) for k in groupLabels]
GroupBy(groupLabels, groupIds)
end
# -----------------------------------------------------------------------------
function warnOnUnsupportedArgs(pkg::PlottingPackage, d::Dict)
for k in sortedkeys(d)
if (!(k in supportedArgs(pkg))
&& k != :subplot
&& d[k] != default(k))
warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))")
end
end
end
function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
(d[:axis] in supportedAxes(pkg)
|| warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))"))
(d[:linetype] == :none
|| d[:linetype] in supportedTypes(pkg)
|| warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))"))
(d[:linestyle] in supportedStyles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))"))
(d[:markershape] == :none
|| d[:markershape] in supportedMarkers(pkg)
|| (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape)
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
end
function warnOnUnsupportedScales(pkg::PlottingPackage, d::Dict)
for k in (:xscale, :yscale)
if haskey(d, k)
d[k] in supportedScales(pkg) || warn("scale $(d[k]) is unsupported with $pkg. Choose from: $(supportedScales(pkg))")
end
end
end
# -----------------------------------------------------------------------------
# 1-row matrices will give an element
# multi-row matrices will give a column
# anything else is returned as-is
# getArgValue(v::Tuple, idx::Int) = v[mod1(idx, length(v))]
function getArgValue(v::AMat, idx::Int)
c = mod1(idx, size(v,2))
size(v,1) == 1 ? v[1,c] : v[:,c]
end
getArgValue(v, idx) = v
# given an argument key (k), we want to extract the argument value for this index.
# if nothing is set (or container is empty), return the default.
function setDictValue(d_in::Dict, d_out::Dict, k::Symbol, idx::Int, defaults::Dict)
if haskey(d_in, k) && !(typeof(d_in[k]) <: @compat(Union{AbstractArray, Tuple}) && isempty(d_in[k]))
d_out[k] = getArgValue(d_in[k], idx)
else
d_out[k] = defaults[k]
end
end
# -----------------------------------------------------------------------------
# build the argument dictionary for the plot
function getPlotArgs(pkg::PlottingPackage, kw, idx::Int; set_defaults = true)
kwdict = Dict(kw)
d = Dict()
# add defaults?
if set_defaults
for k in keys(_plotDefaults)
setDictValue(kwdict, d, k, idx, _plotDefaults)
end
end
for k in (:xscale, :yscale)
if haskey(_scaleAliases, d[k])
d[k] = _scaleAliases[d[k]]
end
end
# convert color
handlePlotColors(pkg, d)
# no need for these
delete!(d, :x)
delete!(d, :y)
d
end
# build the argument dictionary for a series
function getSeriesArgs(pkg::PlottingPackage, initargs::Dict, kw, commandIndex::Int, plotIndex::Int, globalIndex::Int) # TODO, pass in initargs, not plt
kwdict = Dict(kw)
d = Dict()
# add defaults?
for k in keys(_seriesDefaults)
setDictValue(kwdict, d, k, commandIndex, _seriesDefaults)
end
# groupby args?
for k in (:idxfilter, :numUncounted, :dataframe)
if haskey(kwdict, k)
d[k] = kwdict[k]
end
end
if haskey(_typeAliases, d[:linetype])
d[:linetype] = _typeAliases[d[:linetype]]
end
aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex)
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex)
# update color
d[:color] = getSeriesRGBColor(d[:color], initargs, plotIndex)
# update markercolor
mc = d[:markercolor]
mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, plotIndex))
d[:markercolor] = mc
# update fillcolor
mc = d[:fillcolor]
mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, plotIndex))
d[:fillcolor] = mc
# set label
label = d[:label]
label = (label == "AUTO" ? "y$globalIndex" : label)
if d[:axis] == :right && !(length(label) >= 4 && label[end-3:end] != " (R)")
label = string(label, " (R)")
end
d[:label] = label
warnOnUnsupported(pkg, d)
d
end
+642
View File
@@ -0,0 +1,642 @@
# https://github.com/dcjones/Gadfly.jl
# ---------------------------------------------------------------------------
function createGadflyPlotObject(d::Dict)
gplt = Gadfly.Plot()
gplt.mapping = Dict()
gplt.data_source = DataFrames.DataFrame()
gplt.layers = gplt.layers[1:0]
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
Gadfly.Guide.ylabel(d[:ylabel]),
Gadfly.Guide.title(d[:title])]
gplt
end
# ---------------------------------------------------------------------------
function getLineGeom(d::Dict)
lt = d[:linetype]
xbins, ybins = maketuple(d[:nbins])
if lt == :hexbin
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
elseif lt == :heatmap
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
elseif lt == :hist
Gadfly.Geom.histogram(bincount = xbins)
elseif lt == :path
Gadfly.Geom.path
elseif lt in (:bar, :sticks)
Gadfly.Geom.bar
elseif lt == :steppost
Gadfly.Geom.step
elseif lt == :steppre
Gadfly.Geom.step(direction = :vh)
elseif lt == :hline
Gadfly.Geom.hline(color = getColor(d[:color]), size = d[:linewidth] * Gadfly.px)
elseif lt == :vline
Gadfly.Geom.vline(color = getColor(d[:color]), size = d[:linewidth] * Gadfly.px)
elseif lt == :contour
Gadfly.Geom.contour(levels = d[:nlevels])
else
nothing
end
end
function getGadflyLineTheme(d::Dict)
lc = getColor(d[:color])
α = d[:lineopacity]
if α != nothing
lc = RGBA(lc, α)
end
fc = getColor(d[:fillcolor])
α = d[:fillopacity]
if α != nothing
fc = RGBA(fc, α)
end
Gadfly.Theme(;
default_color = lc,
line_width = (d[:linetype] == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
line_style = Gadfly.get_stroke_vector(d[:linestyle]),
lowlight_color = x->RGB(fc), # fill/ribbon
lowlight_opacity = alpha(fc), # fill/ribbon
bar_highlight = RGB(lc), # bars
)
end
# add a line as a new layer
function addGadflyLine!(plt::Plot, d::Dict, geoms...)
gplt = getGadflyContext(plt)
gfargs = vcat(geoms...,
getGadflyLineTheme(d))
kwargs = Dict()
lt = d[:linetype]
# add a fill?
if d[:fillrange] != nothing && lt != :contour
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
nmin, nmax = length(fillmin), length(fillmax)
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
kwargs[:ymax] = Float64[max(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
push!(gfargs, Gadfly.Geom.ribbon)
end
# h/vlines
if lt == :hline
kwargs[:yintercept] = d[:y]
elseif lt == :vline
kwargs[:xintercept] = d[:y]
elseif lt == :sticks
w = 0.01 * mean(diff(d[:x]))
kwargs[:xmin] = d[:x] - w
kwargs[:xmax] = d[:x] + w
elseif lt == :contour
d[:y] = reverse(d[:y])
kwargs[:z] = d[:surface]
end
# add the layer
x = d[d[:linetype] == :hist ? :y : :x]
Gadfly.layer(gfargs...; x = x, y = d[:y], kwargs...)
end
# ---------------------------------------------------------------------------
function getMarkerGeom(d::Dict)
shape = d[:markershape]
gadflyshape(isa(shape, Shape) ? shape : _shapes[shape])
end
function getGadflyMarkerTheme(d::Dict, initargs::Dict)
c = getColor(d[:markercolor])
α = d[:markeropacity]
if α != nothing
c = RGBA(RGB(c), α)
end
fg = getColor(initargs[:foreground_color])
Gadfly.Theme(
default_color = c,
default_point_size = d[:markersize] * Gadfly.px,
discrete_highlight_color = c -> RGB(fg),
highlight_width = d[:linewidth] * Gadfly.px,
)
end
function addGadflyMarker!(plt::Plot, d::Dict, initargs::Dict, geoms...)
gfargs = vcat(geoms...,
getGadflyMarkerTheme(d, initargs),
getMarkerGeom(d))
kwargs = Dict()
# handle continuous color scales for the markers
z = d[:z]
if z != nothing && typeof(z) <: AVec
kwargs[:color] = z
if !isa(d[:markercolor], ColorGradient)
d[:markercolor] = colorscheme(:bluesreds)
end
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:markercolor], p))))
end
Gadfly.layer(gfargs...; x = d[:x], y = d[:y], kwargs...)
end
# ---------------------------------------------------------------------------
function addToGadflyLegend(plt::Plot, d::Dict)
# add the legend?
if plt.initargs[:legend]
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("", @compat(AbstractString)[], Color[]))
end
# now add the series to the legend
for guide in gplt.guides
if isa(guide, Gadfly.Guide.ManualColorKey)
# TODO: there's a BUG in gadfly if you pass in the same color more than once,
# since gadfly will call unique(colors), but doesn't also merge the rows that match
# Should ensure from this side that colors which are the same are merged together
c = getColor(d[d[:markershape] == :none ? :color : :markercolor])
foundit = false
# extend the label if we found this color
for i in 1:length(guide.colors)
if RGB(c) == guide.colors[i]
guide.labels[i] *= ", " * d[:label]
foundit = true
end
end
# didn't find the color, so add a new entry into the legend
if !foundit
push!(guide.labels, d[:label])
push!(guide.colors, c)
end
end
end
end
end
getGadflySmoothing(smooth::Bool) = smooth ? [Gadfly.Geom.smooth(method=:lm)] : Any[]
getGadflySmoothing(smooth::Real) = [Gadfly.Geom.smooth(method=:loess, smoothing=float(smooth))]
function addGadflySeries!(plt::Plot, d::Dict)
layers = Gadfly.Layer[]
# add a regression line?
# TODO: make more flexible
smooth = getGadflySmoothing(d[:smooth])
# lines
geom = getLineGeom(d)
if geom != nothing
prepend!(layers, addGadflyLine!(plt, d, geom, smooth...))
# don't add a regression for markers too
smooth = Any[]
end
# special handling for ohlc and scatter
lt = d[:linetype]
if lt == :ohlc
error("Haven't re-implemented after refactoring")
elseif lt == :scatter && d[:markershape] == :none
d[:markershape] = :ellipse
end
# markers
if d[:markershape] != :none
prepend!(layers, addGadflyMarker!(plt, d, plt.initargs, smooth...))
end
lt in (:hist, :heatmap, :hexbin, :contour) || addToGadflyLegend(plt, d)
# now save the layers that apply to this series
d[:gadflylayers] = layers
prepend!(getGadflyContext(plt).layers, layers)
end
# ---------------------------------------------------------------------------
# NOTE: I'm leaving this here and commented out just in case I want to implement again... it was hacky code to create multi-colored line segments
# # colorgroup
# z = d[:z]
# # handle line segments of different colors
# cscheme = d[:color]
# if isa(cscheme, ColorVector)
# # create a color scale, and set the color group to the index of the color
# push!(gplt.scales, Gadfly.Scale.color_discrete_manual(cscheme.v...))
# # this is super weird, but... oh well... for some reason this creates n separate line segments...
# # create a list of vertices that go: [x1,x2,x2,x3,x3, ... ,xi,xi, ... xn,xn] (same for y)
# # then the vector passed to the "color" keyword should be a vector: [1,1,2,2,3,3,4,4, ..., i,i, ... , n,n]
# csindices = Int[mod1(i,length(cscheme.v)) for i in 1:length(d[:y])]
# cs = collect(repmat(csindices', 2, 1))[1:end-1]
# grp = collect(repmat((1:length(d[:y]))', 2, 1))[1:end-1]
# d[:x], d[:y] = map(createSegments, (d[:x], d[:y]))
# colorgroup = [(:color, cs), (:group, grp)]
# ---------------------------------------------------------------------------
function addGadflyTicksGuide(gplt, ticks, isx::Bool)
ticks == :auto && return
# remove the ticks?
if ticks in (:none, false, nothing)
return addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
end
ttype = ticksType(ticks)
# just the values... put ticks here, but use standard labels
if ttype == :ticks
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
replaceType(gplt.guides, gtype(ticks = collect(ticks)))
# set the ticks and the labels
elseif ttype == :ticks_and_labels
gtype = isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks
replaceType(gplt.guides, gtype(ticks = collect(ticks[1])))
# TODO add xtick_label function (given tick, return label??)
# Scale.x_discrete(; labels=nothing, levels=nothing, order=nothing)
filterGadflyScale(gplt, isx)
gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
labelmap = Dict(zip(ticks...))
labelfunc = val -> labelmap[val]
push!(gplt.scales, gfunc(levels = ticks[1], labels = labelfunc))
else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
end
end
continuousAndSameAxis(scale, isx::Bool) = isa(scale, Gadfly.Scale.ContinuousScale) && scale.vars[1] == (isx ? :x : :y)
filterGadflyScale(gplt, isx::Bool) = filter!(scale -> !continuousAndSameAxis(scale, isx), gplt.scales)
function getGadflyScaleFunction(d::Dict, isx::Bool)
scalekey = isx ? :xscale : :yscale
hasScaleKey = haskey(d, scalekey)
if hasScaleKey
scale = d[scalekey]
scale == :log && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey
scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey
scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey
scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey
scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey
end
isx ? Gadfly.Scale.x_continuous : Gadfly.Scale.y_continuous, hasScaleKey
end
function addGadflyLimitsScale(gplt, d::Dict, isx::Bool)
# get the correct scale function
gfunc, hasScaleKey = getGadflyScaleFunction(d, isx)
# do we want to add min/max limits for the axis?
limsym = isx ? :xlims : :ylims
limargs = Any[]
lims = get(d, limsym, :auto)
lims == :auto && return
if limsType(lims) == :limits
push!(limargs, (:minvalue, min(lims...)))
push!(limargs, (:maxvalue, max(lims...)))
else
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
end
# replace any current scales with this one
if hasScaleKey || !isempty(limargs)
filterGadflyScale(gplt, isx)
push!(gplt.scales, gfunc(; limargs...))
end
lims
end
function updateGadflyAxisFlips(gplt, d::Dict, xlims, ylims)
if isa(gplt.coord, Gadfly.Coord.Cartesian)
gplt.coord = Gadfly.Coord.cartesian(
gplt.coord.xvars,
gplt.coord.yvars;
xmin = xlims == nothing ? gplt.coord.xmin : minimum(xlims),
xmax = xlims == nothing ? gplt.coord.xmax : maximum(xlims),
ymin = ylims == nothing ? gplt.coord.ymin : minimum(ylims),
ymax = ylims == nothing ? gplt.coord.ymax : maximum(ylims),
xflip = get(d, :xflip, gplt.coord.xflip),
yflip = get(d, :yflip, gplt.coord.yflip),
fixed = gplt.coord.fixed,
aspect_ratio = gplt.coord.aspect_ratio,
raster = gplt.coord.raster
)
else
gplt.coord = Gadfly.Coord.Cartesian(
xflip = get(d, :xflip, false),
yflip = get(d, :yflip, false)
)
end
end
function findGuideAndSet(gplt, t::DataType, args...; kw...) #s::@compat(AbstractString))
for (i,guide) in enumerate(gplt.guides)
if isa(guide, t)
gplt.guides[i] = t(args...; kw...)
end
end
end
function updateGadflyGuides(plt::Plot, d::Dict)
gplt = getGadflyContext(plt)
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
haskey(d, :xlabel) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xlabel]))
haskey(d, :ylabel) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:ylabel]))
xlims = addGadflyLimitsScale(gplt, d, true)
ylims = addGadflyLimitsScale(gplt, d, false)
ticks = get(d, :xticks, :auto)
if ticks == :none
handleLinkInner(plt, true)
else
addGadflyTicksGuide(gplt, ticks, true)
end
ticks = get(d, :yticks, :auto)
if ticks == :none
handleLinkInner(plt, false)
else
addGadflyTicksGuide(gplt, ticks, false)
end
# haskey(d, :yticks) && addGadflyTicksGuide(gplt, d[:yticks], false)
updateGadflyAxisFlips(gplt, d, xlims, ylims)
end
function updateGadflyPlotTheme(plt::Plot, d::Dict)
kwargs = Dict()
# # get the full initargs, overriding any new settings
# # TODO: should this be part of the main `plot` command in plot.jl???
# d = merge!(plt.initargs, d)
# hide the legend?
if !get(d, :legend, true)
kwargs[:key_position] = :none
end
if !get(d, :grid, true)
kwargs[:grid_color] = getColor(d[:background_color])
end
# fonts
tfont, gfont, lfont = d[:tickfont], d[:guidefont], d[:legendfont]
fg = getColor(d[:foreground_color])
getGadflyContext(plt).theme = Gadfly.Theme(;
background_color = getColor(d[:background_color]),
minor_label_color = fg,
minor_label_font = tfont.family,
minor_label_font_size = tfont.pointsize * Gadfly.pt,
major_label_color = fg,
major_label_font = gfont.family,
major_label_font_size = gfont.pointsize * Gadfly.pt,
key_title_color = fg,
key_title_font = gfont.family,
key_title_font_size = gfont.pointsize * Gadfly.pt,
key_label_color = fg,
key_label_font = lfont.family,
key_label_font_size = lfont.pointsize * Gadfly.pt,
plot_padding = 1 * Gadfly.mm,
kwargs...
)
end
# ----------------------------------------------------------------
function createGadflyAnnotationObject(x, y, val::@compat(AbstractString))
Gadfly.Guide.annotation(Compose.compose(
Compose.context(),
Compose.text(x, y, val)
))
end
function createGadflyAnnotationObject(x, y, txt::PlotText)
halign = (txt.font.halign == :hcenter ? Compose.hcenter : (txt.font.halign == :left ? Compose.hleft : Compose.hright))
valign = (txt.font.valign == :vcenter ? Compose.vcenter : (txt.font.valign == :top ? Compose.vtop : Compose.vbottom))
rotations = (txt.font.rotation == 0.0 ? [] : [Compose.Rotation(txt.font.rotation, Compose.Point(Compose.x_measure(x), Compose.y_measure(y)))])
Gadfly.Guide.annotation(Compose.compose(
Compose.context(),
Compose.text(x, y, txt.str, halign, valign, rotations...),
Compose.font(string(txt.font.family)),
Compose.fontsize(txt.font.pointsize * Gadfly.pt),
Compose.stroke(txt.font.color),
Compose.fill(txt.font.color)
))
end
function addAnnotations{X,Y,V}(plt::Plot{GadflyPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
push!(plt.o.guides, createGadflyAnnotationObject(ann...))
end
end
# ---------------------------------------------------------------------------
# create a blank Gadfly.Plot object
function plot(pkg::GadflyPackage; kw...)
d = Dict(kw)
gplt = createGadflyPlotObject(d)
Plot(gplt, pkg, 0, d, Dict[])
end
# plot one data series
function plot!(::GadflyPackage, plt::Plot; kw...)
d = Dict(kw)
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
end
function updatePlotItems(plt::Plot{GadflyPackage}, d::Dict)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
# ----------------------------------------------------------------
# accessors for x/y data
# TODO: need to save all the layer indices which apply to this series
function getGadflyMappings(plt::Plot, i::Integer)
@assert i > 0 && i <= plt.n
mappings = [l.mapping for l in plt.seriesargs[i][:gadflylayers]]
end
function Base.getindex(plt::Plot{GadflyPackage}, i::Integer)
mapping = getGadflyMappings(plt, i)[1]
mapping[:x], mapping[:y]
end
function Base.setindex!(plt::Plot{GadflyPackage}, xy::Tuple, i::Integer)
for mapping in getGadflyMappings(plt, i)
mapping[:x], mapping[:y] = xy
end
plt
end
# ----------------------------------------------------------------
# create the underlying object (each backend will do this differently)
function buildSubplotObject!(subplt::Subplot{GadflyPackage}, isbefore::Bool)
isbefore && return false # wait until after plotting to create the subplots
subplt.o = nothing
true
end
function handleLinkInner(plt::Plot{GadflyPackage}, isx::Bool)
gplt = getGadflyContext(plt)
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
end
function expandLimits!(lims, plt::Plot{GadflyPackage}, isx::Bool)
for l in getGadflyContext(plt).layers
expandLimits!(lims, l.mapping[isx ? :x : :y])
end
end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{GadflyPackage}) = plt.o
getGadflyContext(subplt::Subplot{GadflyPackage}) = buildGadflySubplotContext(subplt)
# create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
function buildGadflySubplotContext(subplt::Subplot)
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
# add the Plot object to the row
push!(row, getGadflyContext(subplt.plts[i]))
# add the row
if c == ncols(subplt.layout, r)
push!(rows, Gadfly.hstack(row...))
row = Any[]
end
end
# stack the rows
Gadfly.vstack(rows...)
end
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.initargs[:size]...)
setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getinitargs(subplt, 1)[:size]...)
# -------------------------------------------------------------------------
function dowritemime{P<:GadflyOrImmerse}(io::IO, func, plt::PlottingObject{P})
gplt = getGadflyContext(plt)
setGadflyDisplaySize(plt)
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
end
getGadflyWriteFunc(::MIME"image/png") = Gadfly.PNG
getGadflyWriteFunc(::MIME"image/svg+xml") = Gadfly.SVG
# getGadflyWriteFunc(::MIME"text/html") = Gadfly.SVGJS
getGadflyWriteFunc(::MIME"application/pdf") = Gadfly.PDF
getGadflyWriteFunc(::MIME"application/postscript") = Gadfly.PS
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript")
@eval function Base.writemime{P<:GadflyOrImmerse}(io::IO, ::$mime, plt::PlottingObject{P})
func = getGadflyWriteFunc($mime())
dowritemime(io, func, plt)
end
end
function Base.display(::PlotsDisplay, plt::Plot{GadflyPackage})
setGadflyDisplaySize(plt.initargs[:size]...)
display(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{GadflyPackage})
setGadflyDisplaySize(getinitargs(subplt,1)[:size]...)
ctx = buildGadflySubplotContext(subplt)
# taken from Gadfly since I couldn't figure out how to do it directly
filename = string(Gadfly.tempname(), ".html")
output = open(filename, "w")
plot_output = IOBuffer()
Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
Compose.default_graphic_height, false), ctx)
plotsvg = takebuf_string(plot_output)
write(output,
"""
<!DOCTYPE html>
<html>
<head>
<title>Gadfly Plot</title>
<meta charset="utf-8">
</head>
<body>
<script charset="utf-8">
$(readall(Compose.snapsvgjs))
</script>
<script charset="utf-8">
$(readall(Gadfly.gadflyjs))
</script>
$(plotsvg)
</body>
</html>
""")
close(output)
Gadfly.open_file(filename)
end
+89
View File
@@ -0,0 +1,89 @@
# Geometry which displays arbitrary shapes at given (x, y) positions.
immutable ShapeGeometry <: Gadfly.GeometryElement
vertices::AbstractVector{@compat(Tuple{Float64,Float64})}
tag::Symbol
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
new(shape, tag)
end
end
# TODO: add for PR
# const shape = ShapeGeometry
function Gadfly.element_aesthetics(::ShapeGeometry)
[:x, :y, :size, :color]
end
# Generate a form for a shape geometry.
#
# Args:
# geom: shape geometry.
# theme: the plot's theme.
# aes: aesthetics.
#
# Returns:
# A compose Form.
#
function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics)
# TODO: add for PR
# Gadfly.assert_aesthetics_defined("Geom.shape", aes, :x, :y)
# Gadfly.assert_aesthetics_equal_length("Geom.shape", aes,
# element_aesthetics(geom)...)
default_aes = Gadfly.Aesthetics()
default_aes.color = Gadfly.DataFrames.PooledDataArray(RGBA{Float32}[theme.default_color])
default_aes.size = Compose.Measure[theme.default_point_size]
aes = Gadfly.inherit(aes, default_aes)
lw_hover_scale = 10
lw_ratio = theme.line_width / aes.size[1]
aes_x, aes_y = Gadfly.concretize(aes.x, aes.y)
ctx = Compose.compose!(
Compose.context(),
make_polygon(geom, aes.x, aes.y, aes.size),
Compose.fill(aes.color),
Compose.linewidth(theme.highlight_width))
if aes.color_key_continuous != nothing && aes.color_key_continuous
Compose.compose!(ctx,
Compose.stroke(map(theme.continuous_highlight_color, aes.color)))
else
Compose.compose!(ctx,
Compose.stroke(map(theme.discrete_highlight_color, aes.color)),
Compose.svgclass([Gadfly.svg_color_class_from_label(Gadfly.escape_id(aes.color_label([c])[1]))
for c in aes.color]))
end
return Compose.compose!(Compose.context(order=4), Compose.svgclass("geometry"), ctx)
end
function gadflyshape(sv::Shape)
ShapeGeometry([(x,-y) for (x,y) in sv.vertices])
end
# create a Compose context given a ShapeGeometry and the xs/ys/sizes
function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
n = max(length(xs), length(ys), length(rs))
T = @compat(Tuple{Compose.Measure, Compose.Measure})
polys = Array(Vector{T}, n)
for i in 1:n
x = Compose.x_measure(xs[mod1(i, length(xs))])
y = Compose.y_measure(ys[mod1(i, length(ys))])
r = rs[mod1(i, length(rs))]
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in geom.vertices]
end
Gadfly.polygon(polys, geom.tag)
end
# ---------------------------------------------------------------------------------------------
+177
View File
@@ -0,0 +1,177 @@
# https://github.com/JuliaGraphics/Immerse.jl
# immutable ImmersePackage <: PlottingPackage end
# export immerse
# immerse() = backend(:immerse)
# supportedArgs(::ImmersePackage) = supportedArgs(GadflyPackage())
# supportedAxes(::ImmersePackage) = supportedAxes(GadflyPackage())
# supportedTypes(::ImmersePackage) = supportedTypes(GadflyPackage())
# supportedStyles(::ImmersePackage) = supportedStyles(GadflyPackage())
# supportedMarkers(::ImmersePackage) = supportedMarkers(GadflyPackage())
# supportedScales(::ImmersePackage) = supportedScales(GadflyPackage())
function createImmerseFigure(d::Dict)
w,h = d[:size]
figidx = Immerse.figure(; name = d[:windowtitle], width = w, height = h)
Immerse.Figure(figidx)
end
# create a blank Gadfly.Plot object
function plot(pkg::ImmersePackage; kw...)
d = Dict(kw)
# create the underlying Gadfly.Plot object
gplt = createGadflyPlotObject(d)
# save both the Immerse.Figure and the Gadfly.Plot
Plot((nothing,gplt), pkg, 0, d, Dict[])
end
# plot one data series
function plot!(::ImmersePackage, plt::Plot; kw...)
d = Dict(kw)
addGadflySeries!(plt, d)
push!(plt.seriesargs, d)
plt
end
function updatePlotItems(plt::Plot{ImmersePackage}, d::Dict)
updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d)
end
# ----------------------------------------------------------------
function addAnnotations{X,Y,V}(plt::Plot{ImmersePackage}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
push!(getGadflyContext(plt).guides, createGadflyAnnotationObject(ann...))
end
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{ImmersePackage}, i::Integer)
mapping = getGadflyMappings(plt, i)[1]
mapping[:x], mapping[:y]
end
function Base.setindex!(plt::Plot{ImmersePackage}, xy::Tuple, i::Integer)
for mapping in getGadflyMappings(plt, i)
mapping[:x], mapping[:y] = xy
end
plt
end
# ----------------------------------------------------------------
function buildSubplotObject!(subplt::Subplot{ImmersePackage}, isbefore::Bool)
return false
# isbefore && return false
end
function showSubplotObject(subplt::Subplot{ImmersePackage})
# create the Gtk window with vertical box vsep
d = getinitargs(subplt,1)
w,h = d[:size]
vsep = Gtk.GtkBoxLeaf(:v)
win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
figindices = []
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
for (i,(r,c)) in enumerate(subplt.layout)
plt = subplt.plts[i]
# get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
box, toolbar, canvas = Immerse.createPlotGuiComponents()
# add the plot's box to the row
push!(row, box)
# create the figure and store the index returned for destruction later
figidx = Immerse.figure(canvas)
push!(figindices, figidx)
fig = Immerse.figure(figidx)
plt.o = (fig, plt.o[2])
# add the row
if c == ncols(subplt.layout, r)
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
end
end
# destructor... clean up plots
Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
subplt.o = win
true
end
function handleLinkInner(plt::Plot{ImmersePackage}, isx::Bool)
gplt = getGadflyContext(plt)
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xticks : Gadfly.Guide.yticks; label=false)
addOrReplace(gplt.guides, isx ? Gadfly.Guide.xlabel : Gadfly.Guide.ylabel, "")
end
function expandLimits!(lims, plt::Plot{ImmersePackage}, isx::Bool)
for l in getGadflyContext(plt).layers
expandLimits!(lims, l.mapping[isx ? :x : :y])
end
end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{ImmersePackage}) = plt.o[2]
getGadflyContext(subplt::Subplot{ImmersePackage}) = buildGadflySubplotContext(subplt)
function Base.display(::PlotsDisplay, plt::Plot{ImmersePackage})
fig, gplt = plt.o
if fig == nothing
fig = createImmerseFigure(plt.initargs)
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
plt.o = (fig, gplt)
end
Immerse.figure(fig.figno; displayfig = false)
display(gplt)
end
function Base.display(::PlotsDisplay, subplt::Subplot{ImmersePackage})
# if we haven't created the window yet, do it
if subplt.o == nothing
showSubplotObject(subplt)
end
# display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
for plt in subplt.plts
fig, gplt = plt.o
Immerse.figure(fig.figno; displayfig = false)
display(gplt)
end
# o is the window... show it
showall(subplt.o)
end
+672
View File
@@ -0,0 +1,672 @@
# https://github.com/stevengj/PyPlot.jl
# -------------------------------
# convert colorant to 4-tuple RGBA
getPyPlotColor(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α))
getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
# getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha))
function getPyPlotColorMap(c::ColorGradient, α=nothing)
# c = ColorGradient(c.colors, c.values, alpha=α)
# pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c)))
pyvals = [(c.values[i], getPyPlotColor(c.colors[i], α)) for i in 1:length(c.colors)]
pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
end
# anything else just gets a redsblue gradient
getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:redsblues), α)
# get the style (solid, dashed, etc)
function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol)
linetype == :none && return " "
linestyle == :solid && return "-"
linestyle == :dash && return "--"
linestyle == :dot && return ":"
linestyle == :dashdot && return "-."
warn("Unknown linestyle $linestyle")
return "-"
end
function getPyPlotMarker(marker::Shape)
n = length(marker.vertices)
mat = zeros(n+1,2)
for (i,vert) in enumerate(marker.vertices)
mat[i,1] = vert[1]
mat[i,2] = vert[2]
end
mat[n+1,:] = mat[1,:]
pypath.pymember("Path")(mat)
# marker.vertices
end
# get the marker shape
function getPyPlotMarker(marker::Symbol)
marker == :none && return " "
marker == :ellipse && return "o"
marker == :rect && return "s"
marker == :diamond && return "D"
marker == :utriangle && return "^"
marker == :dtriangle && return "v"
marker == :cross && return "+"
marker == :xcross && return "x"
marker == :star5 && return "*"
marker == :pentagon && return "p"
marker == :hexagon && return "h"
marker == :octagon && return "8"
haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker])
warn("Unknown marker $marker")
return "o"
end
# pass through
function getPyPlotMarker(marker::@compat(AbstractString))
@assert length(marker) == 1
marker
end
function getPyPlotStepStyle(linetype::Symbol)
linetype == :steppost && return "steps-post"
linetype == :steppre && return "steps-pre"
return "default"
end
immutable PyPlotFigWrapper
fig
end
immutable PyPlotAxisWrapper
ax
fig
end
getfig(wrap::@compat(Union{PyPlotAxisWrapper,PyPlotFigWrapper})) = wrap.fig
# get a reference to the correct axis
function getLeftAxis(wrap::PyPlotFigWrapper)
axes = wrap.fig.o[:axes]
if isempty(axes)
return wrap.fig.o[:add_subplot](111)
end
axes[1]
end
getLeftAxis(wrap::PyPlotAxisWrapper) = wrap.ax
getLeftAxis(plt::Plot{PyPlotPackage}) = getLeftAxis(plt.o)
getRightAxis(x) = getLeftAxis(x)[:twinx]()
getAxis(plt::Plot{PyPlotPackage}, axis::Symbol) = (axis == :right ? getRightAxis : getLeftAxis)(plt)
# left axis is PyPlot.<func>, right axis is "f.axes[0].twinx().<func>"
function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol)
# in the 2-axis case we need to get: <rightaxis>[:<func>]
ax = getAxis(plt, axis)
ax[:set_ylabel](plt.initargs[:yrightlabel])
fmap = @compat Dict(
:hist => :hist,
:sticks => :bar,
:bar => :bar,
:heatmap => :hexbin,
:hexbin => :hexbin,
:scatter => :scatter,
:contour => :contour,
)
return ax[get(fmap, linetype, :plot)]
end
function updateAxisColors(ax, fgcolor)
for loc in ("bottom", "top", "left", "right")
ax[:spines][loc][:set_color](fgcolor)
end
for axis in ("x", "y")
ax[:tick_params](axis=axis, colors=fgcolor, which="both")
end
for axis in (:yaxis, :xaxis)
ax[axis][:label][:set_color](fgcolor)
end
ax[:title][:set_color](fgcolor)
end
function handleSmooth(plt::Plot{PyPlotPackage}, ax, d::Dict, smooth::Bool)
if smooth
xs, ys = regressionXY(d[:x], d[:y])
ax[:plot](xs, ys,
# linestyle = getPyPlotLineStyle(:path, :dashdot),
color = getPyPlotColor(d[:color]),
linewidth = 2
)
end
end
handleSmooth(plt::Plot{PyPlotPackage}, ax, d::Dict, smooth::Real) = handleSmooth(plt, ax, d, true)
nop() = nothing
makePyPlotCurrent(wrap::PyPlotFigWrapper) = PyPlot.figure(wrap.fig.o[:number])
makePyPlotCurrent(wrap::PyPlotAxisWrapper) = nothing #PyPlot.sca(wrap.ax.o)
makePyPlotCurrent(plt::Plot{PyPlotPackage}) = makePyPlotCurrent(plt.o)
function preparePlotUpdate(plt::Plot{PyPlotPackage})
makePyPlotCurrent(plt)
end
# ------------------------------------------------------------------
# TODO:
# fillto # might have to use barHack/histogramHack??
# reg # true or false, add a regression line for each line
# pos # (Int,Int), move the enclosing window to this position
# windowtitle # string or symbol, set the title of the enclosing windowtitle
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
# show # true or false, show the plot (in case you don't want the window to pop up right away)
function plot(pkg::PyPlotPackage; kw...)
# create the figure
d = Dict(kw)
# standalone plots will create a figure, but not if part of a subplot (do it later)
if haskey(d, :subplot)
wrap = nothing
else
w,h = map(px2inch, d[:size])
bgcolor = getPyPlotColor(d[:background_color])
wrap = PyPlotFigWrapper(PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = 96))
end
plt = Plot(wrap, pkg, 0, d, Dict[])
plt
end
function plot!(pkg::PyPlotPackage, plt::Plot; kw...)
d = Dict(kw)
ax = getAxis(plt, d[:axis])
lt = d[:linetype]
if !(lt in supportedTypes(pkg))
error("linetype $(lt) is unsupported in PyPlot. Choose from: $(supportedTypes(pkg))")
end
color = getPyPlotColor(d[:color], d[:lineopacity])
if lt == :sticks
d,_ = sticksHack(;d...)
elseif lt == :scatter
if d[:markershape] == :none
d[:markershape] = :ellipse
end
elseif lt in (:hline,:vline)
linewidth = d[:linewidth]
linecolor = color
linestyle = getPyPlotLineStyle(lt, d[:linestyle])
for yi in d[:y]
func = ax[lt == :hline ? :axhline : axvline]
func(yi, linewidth=d[:linewidth], color=linecolor, linestyle=linestyle)
end
end
lt = d[:linetype]
extra_kwargs = Dict()
plotfunc = getPyPlotFunction(plt, d[:axis], lt)
# we have different args depending on plot type
if lt in (:hist, :sticks, :bar)
# NOTE: this is unsupported because it does the wrong thing... it shifts the whole axis
# extra_kwargs[:bottom] = d[:fill]
if lt == :hist
extra_kwargs[:bins] = d[:nbins]
else
extra_kwargs[:linewidth] = (lt == :sticks ? 0.1 : 0.9)
end
elseif lt in (:heatmap, :hexbin)
extra_kwargs[:gridsize] = d[:nbins]
extra_kwargs[:cmap] = getPyPlotColorMap(d[:color])
elseif lt == :contour
extra_kwargs[:cmap] = getPyPlotColorMap(d[:color])
extra_kwargs[:linewidths] = d[:linewidth]
extra_kwargs[:linestyles] = getPyPlotLineStyle(lt, d[:linestyle])
# TODO: will need to call contourf to fill in the contours
else
extra_kwargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle])
extra_kwargs[:marker] = getPyPlotMarker(d[:markershape])
if lt == :scatter
extra_kwargs[:s] = d[:markersize]^2
c = d[:markercolor]
if isa(c, ColorGradient) && d[:z] != nothing
extra_kwargs[:c] = convert(Vector{Float64}, d[:z])
extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeropacity])
else
extra_kwargs[:c] = getPyPlotColor(c, d[:markeropacity])
end
else
extra_kwargs[:markersize] = d[:markersize]
extra_kwargs[:markerfacecolor] = getPyPlotColor(d[:markercolor], d[:markeropacity])
extra_kwargs[:markeredgecolor] = getPyPlotColor(plt.initargs[:foreground_color])
extra_kwargs[:markeredgewidth] = d[:linewidth]
extra_kwargs[:drawstyle] = getPyPlotStepStyle(lt)
end
end
# set these for all types
if lt != :contour
extra_kwargs[:color] = color
extra_kwargs[:linewidth] = d[:linewidth]
extra_kwargs[:label] = d[:label]
end
# do the plot
d[:serieshandle] = if lt == :hist
plotfunc(d[:y]; extra_kwargs...)[1]
elseif lt == :contour
# NOTE: x/y are backwards in pyplot, so we switch the x and y args (also y is reversed),
# and take the transpose of the surface matrix
x, y = d[:y], d[:x]
surf = d[:surface]'
handle = plotfunc(x, y, surf, d[:nlevels]; extra_kwargs...)
if d[:fillrange] != nothing
handle = ax[:contourf](x, y, surf, d[:nlevels]; cmap = getPyPlotColorMap(d[:fillcolor], d[:fillopacity]))
end
handle
elseif lt in (:scatter, :heatmap, :hexbin)
plotfunc(d[:x], d[:y]; extra_kwargs...)
else
plotfunc(d[:x], d[:y]; extra_kwargs...)[1]
end
handleSmooth(plt, ax, d, d[:smooth])
# add the colorbar legend
if plt.initargs[:legend] && haskey(extra_kwargs, :cmap)
PyPlot.colorbar(d[:serieshandle])
end
# this sets the bg color inside the grid
ax[:set_axis_bgcolor](getPyPlotColor(plt.initargs[:background_color]))
fillrange = d[:fillrange]
if fillrange != nothing && lt != :contour
fillcolor = getPyPlotColor(d[:fillcolor], d[:fillopacity])
if typeof(fillrange) <: @compat(Union{Real, AVec})
ax[:fill_between](d[:x], fillrange, d[:y], facecolor = fillcolor)
else
ax[:fill_between](d[:x], fillrange..., facecolor = fillcolor)
end
end
push!(plt.seriesargs, d)
plt
end
# -----------------------------------------------------------------
function Base.getindex(plt::Plot{PyPlotPackage}, i::Integer)
series = plt.seriesargs[i][:serieshandle]
try
return series[:get_data]()
catch
xy = series[:get_offsets]()
return vec(xy[:,1]), vec(xy[:,2])
end
# series[:relim]()
# mapping = getGadflyMappings(plt, i)[1]
# mapping[:x], mapping[:y]
end
function Base.setindex!(plt::Plot{PyPlotPackage}, xy::Tuple, i::Integer)
series = plt.seriesargs[i][:serieshandle]
try
series[:set_data](xy...)
catch
series[:set_offsets](hcat(xy...))
end
ax = series[:axes]
if plt.initargs[:xlims] == :auto
xmin, xmax = ax[:get_xlim]()
ax[:set_xlim](min(xmin, minimum(xy[1])), max(xmax, maximum(xy[1])))
end
if plt.initargs[:ylims] == :auto
ymin, ymax = ax[:get_ylim]()
ax[:set_ylim](min(ymin, minimum(xy[2])), max(ymax, maximum(xy[2])))
end
# getLeftAxis(plt)[:relim]()
# getRightAxis(plt)[:relim]()
# for mapping in getGadflyMappings(plt, i)
# mapping[:x], mapping[:y] = xy
# end
plt
end
# -----------------------------------------------------------------
function addPyPlotLims(ax, lims, isx::Bool)
lims == :auto && return
ltype = limsType(lims)
if ltype == :limits
ax[isx ? :set_xlim : :set_ylim](lims...)
else
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
end
end
function addPyPlotTicks(ax, ticks, isx::Bool)
ticks == :auto && return
if ticks == :none || ticks == nothing
ticks = zeros(0)
end
ttype = ticksType(ticks)
if ttype == :ticks
ax[isx ? :set_xticks : :set_yticks](ticks)
elseif ttype == :ticks_and_labels
ax[isx ? :set_xticks : :set_yticks](ticks...)
else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
end
end
usingRightAxis(plt::Plot{PyPlotPackage}) = any(args -> args[:axis] in (:right,:auto), plt.seriesargs)
function updatePlotItems(plt::Plot{PyPlotPackage}, d::Dict)
figorax = plt.o
ax = getLeftAxis(figorax)
# PyPlot.sca(ax)
# title and axis labels
# haskey(d, :title) && PyPlot.title(d[:title])
haskey(d, :title) && ax[:set_title](d[:title])
haskey(d, :xlabel) && ax[:set_xlabel](d[:xlabel])
if haskey(d, :ylabel)
ax[:set_ylabel](d[:ylabel])
end
if usingRightAxis(plt) && get(d, :yrightlabel, "") != ""
rightax = getRightAxis(figorax)
rightax[:set_ylabel](d[:yrightlabel])
end
# scales
haskey(d, :xscale) && applyPyPlotScale(ax, d[:xscale], true)
haskey(d, :yscale) && applyPyPlotScale(ax, d[:yscale], false)
# limits and ticks
haskey(d, :xlims) && addPyPlotLims(ax, d[:xlims], true)
haskey(d, :ylims) && addPyPlotLims(ax, d[:ylims], false)
haskey(d, :xticks) && addPyPlotTicks(ax, d[:xticks], true)
haskey(d, :yticks) && addPyPlotTicks(ax, d[:yticks], false)
if get(d, :xflip, false)
ax[:invert_xaxis]()
end
if get(d, :yflip, false)
ax[:invert_yaxis]()
end
axes = [getLeftAxis(figorax)]
if usingRightAxis(plt)
push!(axes, getRightAxis(figorax))
end
# font sizes
for ax in axes
# haskey(d, :yrightlabel) || continue
# guides
sz = get(d, :guidefont, plt.initargs[:guidefont]).pointsize
ax[:title][:set_fontsize](sz)
ax[:xaxis][:label][:set_fontsize](sz)
ax[:yaxis][:label][:set_fontsize](sz)
# ticks
sz = get(d, :tickfont, plt.initargs[:tickfont]).pointsize
for sym in (:get_xticklabels, :get_yticklabels)
for lab in ax[sym]()
lab[:set_fontsize](sz)
end
end
# grid
if get(d, :grid, false)
ax[:xaxis][:grid](true)
ax[:yaxis][:grid](true)
ax[:set_axisbelow](true)
end
end
end
function applyPyPlotScale(ax, scaleType::Symbol, isx::Bool)
func = ax[isx ? :set_xscale : :set_yscale]
scaleType == :identity && return func("linear")
scaleType == :log && return func("log", basex = e, basey = e)
scaleType == :log2 && return func("log", basex = 2, basey = 2)
scaleType == :log10 && return func("log", basex = 10, basey = 10)
warn("Unhandled scaleType: ", scaleType)
end
# -----------------------------------------------------------------
function createPyPlotAnnotationObject(plt::Plot{PyPlotPackage}, x, y, val::@compat(AbstractString))
ax = getLeftAxis(plt)
ax[:annotate](val, xy = (x,y))
end
function createPyPlotAnnotationObject(plt::Plot{PyPlotPackage}, x, y, val::PlotText)
ax = getLeftAxis(plt)
ax[:annotate](val.str,
xy = (x,y),
family = val.font.family,
color = getPyPlotColor(val.font.color),
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
rotation = val.font.rotation * 180 / π,
size = val.font.pointsize
)
end
function addAnnotations{X,Y,V}(plt::Plot{PyPlotPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createPyPlotAnnotationObject(plt, ann...)
end
end
# -----------------------------------------------------------------
# NOTE: pyplot needs to build before
function buildSubplotObject!(subplt::Subplot{PyPlotPackage}, isbefore::Bool)
l = subplt.layout
w,h = map(px2inch, getinitargs(subplt,1)[:size])
bgcolor = getPyPlotColor(getinitargs(subplt,1)[:background_color])
fig = PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = 96)
nr = nrows(l)
for (i,(r,c)) in enumerate(l)
# add the plot to the figure
nc = ncols(l, r)
fakeidx = (r-1) * nc + c
ax = fig[:add_subplot](nr, nc, fakeidx)
subplt.plts[i].o = PyPlotAxisWrapper(ax, fig)
end
subplt.o = PyPlotFigWrapper(fig)
true
end
# this will be called internally, when creating a subplot from existing plots
# NOTE: if I ever need to "Rebuild a "ubplot from individual Plot's"... this is what I should use!
function subplot(plts::AVec{Plot{PyPlotPackage}}, layout::SubplotLayout, d::Dict)
validateSubplotSupported()
p = length(layout)
n = sum([plt.n for plt in plts])
pkg = PyPlotPackage()
newplts = Plot{PyPlotPackage}[plot(pkg; subplot=true, plt.initargs...) for plt in plts]
subplt = Subplot(nothing, newplts, PyPlotPackage(), p, n, layout, d, true, false, false, (r,c) -> (nothing,nothing))
preprocessSubplot(subplt, d)
buildSubplotObject!(subplt, true)
for (i,plt) in enumerate(plts)
for seriesargs in plt.seriesargs
_plot_from_subplot!(newplts[i]; seriesargs...)
end
end
postprocessSubplot(subplt, d)
subplt
end
function handleLinkInner(plt::Plot{PyPlotPackage}, isx::Bool)
if isx
plot!(plt, xticks=zeros(0), xlabel="")
else
plot!(plt, yticks=zeros(0), ylabel="")
end
end
function expandLimits!(lims, plt::Plot{PyPlotPackage}, isx::Bool)
pltlims = plt.o.ax[isx ? :get_xbound : :get_ybound]()
expandLimits!(lims, pltlims)
end
# -----------------------------------------------------------------
# function addPyPlotLegend(plt::Plot)
function addPyPlotLegend(plt::Plot, ax)
if plt.initargs[:legend]
# gotta do this to ensure both axes are included
args = filter(x -> !(x[:linetype] in (:hist,:hexbin,:heatmap,:hline,:vline,:contour)), plt.seriesargs)
if length(args) > 0
ax[:legend]([d[:serieshandle] for d in args],
[d[:label] for d in args],
loc="best",
fontsize = plt.initargs[:legendfont].pointsize
# framealpha = 0.6
)
end
end
end
function finalizePlot(plt::Plot{PyPlotPackage})
ax = getLeftAxis(plt)
addPyPlotLegend(plt, ax)
updateAxisColors(ax, getPyPlotColor(plt.initargs[:foreground_color]))
PyPlot.draw()
end
function finalizePlot(subplt::Subplot{PyPlotPackage})
fig = subplt.o.fig
for (i,plt) in enumerate(subplt.plts)
ax = getLeftAxis(plt)
addPyPlotLegend(plt, ax)
updateAxisColors(ax, getPyPlotColor(plt.initargs[:foreground_color]))
end
PyPlot.draw()
end
# # allow for writing any supported mime
# for mime in keys(PyPlot.aggformats)
# @eval function Base.writemime(io::IO, m::MIME{symbol{$mime}}, plt::Plot{PyPlotPackage})
# finalizePlot(plt)
# writemime(io, m, getfig(plt.o))
# end
# end
# function Base.writemime(io::IO, m::@compat(Union{MIME"image/svg+xml", MIME"image/png"}, plt::Plot{PyPlotPackage})
# finalizePlot(plt)
# writemime(io, m, getfig(plt.o))
# end
# NOTE: to bring up a GUI window in IJulia, need some extra steps
function Base.display(::PlotsDisplay, plt::PlottingObject{PyPlotPackage})
finalizePlot(plt)
if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
display(getfig(plt.o))
else
# # PyPlot.ion()
# PyPlot.figure(getfig(plt.o).o[:number])
# PyPlot.draw_if_interactive()
# # PyPlot.ioff()
end
# PyPlot.plt[:show](block=false)
getfig(plt.o)[:show]()
end
# function Base.display(::PlotsDisplay, subplt::Subplot{PyPlotPackage})
# finalizePlot(subplt)
# PyPlot.ion()
# PyPlot.figure(getfig(subplt.o).o[:number])
# PyPlot.draw_if_interactive()
# PyPlot.ioff()
# # display(getfig(subplt.o))
# end
# # allow for writing any supported mime
# for mime in (MIME"image/png", MIME"application/pdf", MIME"application/postscript")
# @eval function Base.writemime(io::IO, ::$mime, plt::PlottingObject{PyPlotPackage})
# finalizePlot(plt)
# writemime(io, $mime(), getfig(plt.o))
# end
# end
const _pyplot_mimeformats = @compat Dict(
"application/eps" => "eps",
"image/eps" => "eps",
"application/pdf" => "pdf",
"image/png" => "png",
"application/postscript" => "ps",
# "image/svg+xml" => "svg"
)
for (mime, fmt) in _pyplot_mimeformats
@eval function Base.writemime(io::IO, ::MIME{symbol($mime)}, plt::PlottingObject{PyPlotPackage})
finalizePlot(plt)
fig = getfig(plt.o)
fig.o["canvas"][:print_figure](io,
format=$fmt,
bbox_inches="tight",
facecolor = fig.o["get_facecolor"](),
edgecolor = "none"
# edgecolor = fig.o["get_edgecolor"]()
)
end
end
# function Base.writemime(io::IO, m::MIME"image/png", subplt::Subplot{PyPlotPackage})
# finalizePlot(subplt)
# writemime(io, m, getfig(subplt.o))
# end
+283
View File
@@ -0,0 +1,283 @@
# https://github.com/tbreloff/Qwt.jl
# -------------------------------
@compat const _qwtAliases = Dict(
:nbins => :heatmap_n,
:fillrange => :fillto,
:linewidth => :width,
:markershape => :marker,
:hexbin => :heatmap,
:path => :line,
:steppost => :step,
:steppre => :stepinverted,
:star5 => :star1,
:star8 => :star2,
)
function fixcolors(d::Dict)
for (k,v) in d
if typeof(v) <: ColorScheme
d[k] = getColor(v)
end
end
end
function replaceQwtAliases(d, s)
if haskey(_qwtAliases, d[s])
d[s] = _qwtAliases[d[s]]
end
end
function adjustQwtKeywords(plt::Plot{QwtPackage}, iscreating::Bool; kw...)
d = Dict(kw)
lt = d[:linetype]
if lt == :scatter
d[:linetype] = :none
if d[:markershape] == :none
d[:markershape] = :ellipse
end
elseif lt in (:hline, :vline)
addLineMarker(plt, d)
d[:linetype] = :none
d[:markershape] = :ellipse
d[:markersize] = 1
if lt == :vline
d[:x], d[:y] = d[:y], d[:x]
end
elseif !iscreating && lt == :bar
d = barHack(; kw...)
elseif !iscreating && lt == :hist
d = barHack(; histogramHack(; kw...)...)
end
replaceQwtAliases(d, :linetype)
replaceQwtAliases(d, :markershape)
for k in keys(d)
if haskey(_qwtAliases, k)
d[_qwtAliases[k]] = d[k]
end
end
d[:x] = collect(d[:x])
d[:y] = collect(d[:y])
d
end
function plot(pkg::QwtPackage; kw...)
d = Dict(kw)
fixcolors(d)
dumpdict(d,"\n\n!!! plot")
o = Qwt.plot(zeros(0,0); d..., show=false)
plt = Plot(o, pkg, 0, d, Dict[])
plt
end
function plot!(::QwtPackage, plt::Plot; kw...)
d = adjustQwtKeywords(plt, false; kw...)
fixcolors(d)
dumpdict(d,"\n\n!!! plot!")
Qwt.oplot(plt.o; d...)
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
function updateLimsAndTicks(plt::Plot{QwtPackage}, d::Dict, isx::Bool)
lims = get(d, isx ? :xlims : :ylims, nothing)
ticks = get(d, isx ? :xticks : :yticks, nothing)
w = plt.o.widget
axisid = Qwt.QWT.QwtPlot[isx ? :xBottom : :yLeft]
if typeof(lims) <: @compat(Union{Tuple,AVec}) && length(lims) == 2
if isx
plt.o.autoscale_x = false
else
plt.o.autoscale_y = false
end
w[:setAxisScale](axisid, lims...)
end
if typeof(ticks) <: Range
if isx
plt.o.autoscale_x = false
else
plt.o.autoscale_y = false
end
w[:setAxisScale](axisid, float(minimum(ticks)), float(maximum(ticks)), float(step(ticks)))
elseif !(ticks in (nothing, :none, :auto))
warn("Only Range types are supported for Qwt xticks/yticks. typeof(ticks)=$(typeof(ticks))")
end
# change the scale
scalesym = isx ? :xscale : :yscale
if haskey(d, scalesym)
scaletype = d[scalesym]
scaletype == :identity && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLinearScaleEngine())
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
scaletype in supportedScales() || warn("Unsupported scale type: ", scaletype)
end
end
function updatePlotItems(plt::Plot{QwtPackage}, d::Dict)
haskey(d, :title) && Qwt.title(plt.o, d[:title])
haskey(d, :xlabel) && Qwt.xlabel(plt.o, d[:xlabel])
haskey(d, :ylabel) && Qwt.ylabel(plt.o, d[:ylabel])
updateLimsAndTicks(plt, d, true)
updateLimsAndTicks(plt, d, false)
end
function updatePositionAndSize(plt::PlottingObject{QwtPackage}, d::Dict)
haskey(d, :size) && Qwt.resizewidget(plt.o, d[:size]...)
haskey(d, :pos) && Qwt.movewidget(plt.o, d[:pos]...)
end
# ----------------------------------------------------------------
# curve.setPen(Qt.QPen(Qt.QColor(color), linewidth, self.getLineStyle(linestyle)))
function addLineMarker(plt::Plot{QwtPackage}, d::Dict)
for yi in d[:y]
marker = Qwt.QWT.QwtPlotMarker()
ishorizontal = (d[:linetype] == :hline)
marker[:setLineStyle](ishorizontal ? 1 : 2)
marker[ishorizontal ? :setYValue : :setXValue](yi)
qcolor = Qwt.convertRGBToQColor(getColor(d[:color]))
linestyle = plt.o.widget[:getLineStyle](string(d[:linestyle]))
marker[:setLinePen](Qwt.QT.QPen(qcolor, d[:linewidth], linestyle))
marker[:attach](plt.o.widget)
end
# marker[:setValue](x, y)
# marker[:setLabel](Qwt.QWT.QwtText(val))
# marker[:attach](plt.o.widget)
end
function createQwtAnnotation(plt::Plot, x, y, val::PlotText)
marker = Qwt.QWT.QwtPlotMarker()
marker[:setValue](x, y)
qwttext = Qwt.QWT.QwtText(val.str)
qwttext[:setFont](Qwt.QT.QFont(val.font.family, val.font.pointsize))
qwttext[:setColor](Qwt.convertRGBToQColor(getColor(val.font.color)))
marker[:setLabel](qwttext)
marker[:attach](plt.o.widget)
end
function createQwtAnnotation(plt::Plot, x, y, val::@compat(AbstractString))
marker = Qwt.QWT.QwtPlotMarker()
marker[:setValue](x, y)
marker[:setLabel](Qwt.QWT.QwtText(val))
marker[:attach](plt.o.widget)
end
function addAnnotations{X,Y,V}(plt::Plot{QwtPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createQwtAnnotation(plt, ann...)
end
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{QwtPackage}, i::Int)
series = plt.o.lines[i]
series.x, series.y
end
function Base.setindex!(plt::Plot{QwtPackage}, xy::Tuple, i::Integer)
series = plt.o.lines[i]
series.x, series.y = xy
plt
end
# -------------------------------
# savepng(::QwtPackage, plt::PlottingObject, fn::@compat(AbstractString), args...) = Qwt.savepng(plt.o, fn)
# -------------------------------
# create the underlying object (each backend will do this differently)
function buildSubplotObject!(subplt::Subplot{QwtPackage}, isbefore::Bool)
isbefore && return false
i = 0
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
push!(row, subplt.plts[i].o)
if c == ncols(subplt.layout, r)
push!(rows, Qwt.hsplitter(row...))
row = Any[]
end
end
# for rowcnt in subplt.layout.rowcounts
# push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
# i += rowcnt
# end
subplt.o = Qwt.vsplitter(rows...)
# Qwt.resizewidget(subplt.o, getinitargs(subplt,1)[:size]...)
# Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
true
end
function handleLinkInner(plt::Plot{QwtPackage}, isx::Bool)
warn("handleLinkInner isn't implemented for qwt")
end
function expandLimits!(lims, plt::Plot{QwtPackage}, isx::Bool)
for series in plt.o.lines
expandLimits!(lims, isx ? series.x : series.y)
end
end
function handleLinkInner(plt::Plot{QwtPackage}, isx::Bool)
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtPackage})
Qwt.refresh(plt.o)
Qwt.savepng(plt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtPackage})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
function Base.display(::PlotsDisplay, plt::Plot{QwtPackage})
Qwt.refresh(plt.o)
Qwt.showwidget(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{QwtPackage})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
# iargs = getinitargs(subplt,1)
# # iargs = subplt.initargs
# Qwt.resizewidget(subplt.o, iargs[:size]...)
# Qwt.movewidget(subplt.o, iargs[:pos]...)
Qwt.showwidget(subplt.o)
end
+313
View File
@@ -0,0 +1,313 @@
supportedArgs(::GadflyPackage) = [
:annotation,
# :axis,
:background_color,
:color,
:color_palette,
:fillrange,
:fillcolor,
:fillopacity,
:foreground_color,
:group,
:label,
:layout,
:legend,
:linestyle,
:linetype,
:linewidth,
:lineopacity,
:markershape,
:markercolor,
:markersize,
:markeropacity,
:n,
:nbins,
:nc,
:nr,
# :pos,
:smooth,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:tickfont,
:guidefont,
:legendfont,
:grid,
:surface,
:nlevels,
]
supportedAxes(::GadflyPackage) = [:auto, :left]
supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GadflyPackage) = vcat(_allMarkers, Shape)
supportedScales(::GadflyPackage) = [:identity, :log, :log2, :log10, :asinh, :sqrt]
# --------------------------------------------------------------------------------------
supportedArgs(::ImmersePackage) = supportedArgs(GadflyPackage())
supportedAxes(::ImmersePackage) = supportedAxes(GadflyPackage())
supportedTypes(::ImmersePackage) = supportedTypes(GadflyPackage())
supportedStyles(::ImmersePackage) = supportedStyles(GadflyPackage())
supportedMarkers(::ImmersePackage) = supportedMarkers(GadflyPackage())
supportedScales(::ImmersePackage) = supportedScales(GadflyPackage())
# --------------------------------------------------------------------------------------
supportedArgs(::PyPlotPackage) = [
:annotation,
:axis,
:background_color,
:color,
:color_palette,
:fillrange,
:fillcolor,
:foreground_color,
:group,
:label,
:layout,
:legend,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
:n,
:nbins,
:nc,
:nr,
# :pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:tickfont,
:guidefont,
:legendfont,
:grid,
:surface,
:nlevels,
:fillopacity,
:lineopacity,
:markeropacity,
]
supportedAxes(::PyPlotPackage) = _allAxes
supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedStyles(::PyPlotPackage) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PyPlotPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon]
supportedMarkers(::PyPlotPackage) = vcat(_allMarkers, Shape)
supportedScales(::PyPlotPackage) = [:identity, :log, :log2, :log10]
subplotSupported(::PyPlotPackage) = true
# --------------------------------------------------------------------------------------
supportedArgs(::QwtPackage) = [
:annotation,
# :args,
:axis,
:background_color,
:color,
:color_palette,
:fillrange,
:fillcolor,
:foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
:layout,
:legend,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
:n,
:nbins,
:nc,
:nr,
:pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
# :xflip,
# :yflip,
# :z,
]
supportedTypes(::QwtPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
supportedMarkers(::QwtPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supportedScales(::QwtPackage) = [:identity, :log10]
# --------------------------------------------------------------------------------------
supportedArgs(::UnicodePlotsPackage) = [
# :annotation,
# :args,
# :axis,
# :background_color,
# :color,
# :fill,
# :foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
# :layout,
:legend,
:linestyle,
:linetype,
# :linewidth,
:markershape,
# :markercolor,
# :markersize,
# :n,
:nbins,
# :nc,
# :nr,
# :pos,
# :reg,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
# :xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::UnicodePlotsPackage) = [:auto, :left]
supportedTypes(::UnicodePlotsPackage) = [:none, :line, :path, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
supportedStyles(::UnicodePlotsPackage) = [:auto, :solid]
supportedMarkers(::UnicodePlotsPackage) = [:none, :auto, :ellipse]
supportedScales(::UnicodePlotsPackage) = [:identity]
# --------------------------------------------------------------------------------------
supportedArgs(::WinstonPackage) = [
:annotation,
# :args,
# :axis,
# :background_color,
:color,
:color_palette,
:fillrange,
:fillcolor,
# :foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
# :layout,
:legend,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
# :n,
:nbins,
# :nc,
# :nr,
# :pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
# :xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
# :yticks,
:xscale,
:yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::WinstonPackage) = [:auto, :left]
supportedTypes(::WinstonPackage) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
supportedStyles(::WinstonPackage) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::WinstonPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
supportedScales(::WinstonPackage) = [:identity, :log10]
subplotSupported(::WinstonPackage) = false
# --------------------------------------------------------------------------------------
+104
View File
@@ -0,0 +1,104 @@
# TODO: find/replace all [PkgName] with CamelCase, all [pkgname] with lowercase
# [WEBSITE]
# ---------------------------------------------------------------------------
# supportedArgs(::[PkgName]Package) = _allArgs
supportedArgs(::[PkgName]Package) = [
:annotation,
# :args,
:axis,
:background_color,
:color,
:fillrange,
:fillcolor,
:foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
:layout,
:legend,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
:n,
:nbins,
:nc,
:nr,
# :pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
:yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::[PkgName]Package) = _allAxes
supportedTypes(::[PkgName]Package) = _allTypes
supportedStyles(::[PkgName]Package) = _allStyles
supportedMarkers(::[PkgName]Package) = _allMarkers
supportedScales(::[PkgName]Package) = _allScales
subplotSupported(::[PkgName]Package) = false
# ---------------------------------------------------------------------------
function plot(pkg::[PkgName]Package; kw...)
d = Dict(kw)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(o, pkg, 0, d, Dict[])
end
function plot!(::[PkgName]Package, plt::Plot; kw...)
d = Dict(kw)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
# TODO: override this to update plot items (title, xlabel, etc) after creation
function updatePlotItems(plt::Plot{[PkgName]Package}, d::Dict)
end
# ----------------------------------------------------------------
function buildSubplotObject!(subplt::Subplot{[PkgName]Package})
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{[PkgName]Package})
# TODO: write a png to io
end
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]Package})
# TODO: display/show the plot
end
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]Package})
# TODO: display/show the subplot
end
+196
View File
@@ -0,0 +1,196 @@
# https://github.com/Evizero/UnicodePlots.jl
# -------------------------------
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
function rebuildUnicodePlot!(plt::Plot)
# figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
sargs = plt.seriesargs
iargs = plt.initargs
# get the x/y limits
if get(iargs, :xlims, :auto) == :auto
xlim = [Inf, -Inf]
for d in sargs
expandLimits!(xlim, d[:x])
end
else
xmin, xmax = iargs[:xlims]
xlim = [xmin, xmax]
end
if get(iargs, :ylims, :auto) == :auto
ylim = [Inf, -Inf]
for d in sargs
expandLimits!(ylim, d[:y])
end
else
ymin, ymax = iargs[:ylims]
ylim = [ymin, ymax]
end
# we set x/y to have a single point, since we need to create the plot with some data.
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
x = Float64[xlim[1]]
y = Float64[ylim[1]]
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
width, height = iargs[:size]
o = UnicodePlots.createPlotWindow(x, y; width = width,
height = height,
title = iargs[:title],
# labels = iargs[:legend],
xlim = xlim,
ylim = ylim)
# set the axis labels
UnicodePlots.xlabel!(o, iargs[:xlabel])
UnicodePlots.ylabel!(o, iargs[:ylabel])
# now use the ! functions to add to the plot
for d in sargs
addUnicodeSeries!(o, d, iargs[:legend], xlim, ylim)
end
# save the object
plt.o = o
end
# add a single series
function addUnicodeSeries!(o, d::Dict, addlegend::Bool, xlim, ylim)
# get the function, or special handling for step/bar/hist
lt = d[:linetype]
# handle hline/vline separately
if lt in (:hline,:vline)
for yi in d[:y]
if lt == :hline
UnicodePlots.lineplot!(o, xlim, [yi,yi])
else
UnicodePlots.lineplot!(o, [yi,yi], ylim)
end
end
return
end
stepstyle = :post
if lt == :path
func = UnicodePlots.lineplot!
elseif lt == :scatter || d[:markershape] != :none
func = UnicodePlots.scatterplot!
elseif lt == :steppost
func = UnicodePlots.stairs!
elseif lt == :steppre
func = UnicodePlots.stairs!
stepstyle = :pre
else
error("Linestyle $lt not supported by UnicodePlots")
end
# get the series data and label
x, y = [collect(float(d[s])) for s in (:x, :y)]
label = addlegend ? d[:label] : ""
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
color = d[:color] in UnicodePlots.autoColors ? d[:color] : :auto
# add the series
func(o, x, y; color = color, name = label, style = stepstyle)
end
function handlePlotColors(::UnicodePlotsPackage, d::Dict)
# TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
d[:color_palette] = [RGB(0,0,0)]
end
# -------------------------------
function plot(pkg::UnicodePlotsPackage; kw...)
plt = Plot(nothing, pkg, 0, Dict(kw), Dict[])
# do we want to give a new default size?
if !haskey(plt.initargs, :size) || plt.initargs[:size] == _plotDefaults[:size]
plt.initargs[:size] = (60,20)
end
plt
end
function plot!(::UnicodePlotsPackage, plt::Plot; kw...)
d = Dict(kw)
if d[:linetype] in (:sticks, :bar)
d = barHack(; d...)
elseif d[:linetype] == :hist
d = barHack(; histogramHack(; d...)...)
end
push!(plt.seriesargs, d)
plt
end
function updatePlotItems(plt::Plot{UnicodePlotsPackage}, d::Dict)
for k in (:title, :xlabel, :ylabel, :xlims, :ylims)
if haskey(d, k)
plt.initargs[k] = d[k]
end
end
end
# -------------------------------
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
function png(plt::PlottingObject{UnicodePlotsPackage}, fn::@compat(AbstractString))
fn = addExtension(fn, "png")
# make some whitespace and show the plot
println("\n\n\n\n\n\n")
gui(plt)
@osx_only begin
# BEGIN HACK
# wait while the plot gets drawn
sleep(0.5)
# use osx screen capture when my terminal is maximized and cursor starts at the bottom (I know, right?)
# TODO: compute size of plot to adjust these numbers (or maybe implement something good??)
run(`screencapture -R50,600,700,420 $fn`)
# END HACK (phew)
return
end
error("Can only savepng on osx with UnicodePlots (though even then I wouldn't do it)")
end
# -------------------------------
# we don't do very much for subplots... just stack them vertically
function buildSubplotObject!(subplt::Subplot{UnicodePlotsPackage}, isbefore::Bool)
isbefore && return false
true
end
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsPackage})
rebuildUnicodePlot!(plt)
show(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsPackage})
for plt in subplt.plts
gui(plt)
end
end
+237
View File
@@ -0,0 +1,237 @@
# https://github.com/nolta/Winston.jl
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
# ---------------------------------------------------------------------------
## dictionaries for conversion of Plots.jl names to Winston ones.
@compat const winston_linestyle = Dict(:solid=>"solid",
:dash=>"dash",
:dot=>"dotted",
:dashdot=>"dotdashed"
)
@compat const winston_marker = Dict(:none=>".",
:rect => "square",
:ellipse=>"circle",
:diamond=>"diamond",
:utriangle=>"triangle",
:dtriangle=>"down-triangle",
:cross => "plus",
:xcross => "cross",
:star5 => "asterisk"
)
function preparePlotUpdate(plt::Plot{WinstonPackage})
Winston.ghf(plt.o)
end
# ---------------------------------------------------------------------------
function plot(pkg::WinstonPackage; kw...)
d = Dict(kw)
wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel])
Plot(wplt, pkg, 0, d, Dict[])
end
copy_remove(d::Dict, s::Symbol) = delete!(copy(d), s)
function addRegressionLineWinston(d::Dict, wplt)
xs, ys = regressionXY(d[:x], d[:y])
Winston.add(wplt, Winston.Curve(xs, ys, kind="dotted"))
end
function getWinstonItems(plt::Plot)
if isa(plt.o, Winston.FramedPlot)
wplt = plt.o
window, canvas = nothing, nothing
else
window, canvas, wplt = plt.o
end
window, canvas, wplt
end
function plot!(::WinstonPackage, plt::Plot; kw...)
d = Dict(kw)
window, canvas, wplt = getWinstonItems(plt)
# until we call it normally, do the hack
if d[:linetype] == :bar
d = barHack(;d...)
end
e = Dict()
e[:color] = getColor(d[:color])
e[:linewidth] = d[:linewidth]
e[:kind] = winston_linestyle[d[:linestyle]]
e[:symbolkind] = winston_marker[d[:markershape]]
# markercolor # same choices as `color`, or :match will set the color to be the same as `color`
e[:symbolsize] = d[:markersize] / 5
# pos # (Int,Int), move the enclosing window to this position
# screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
if d[:linetype] == :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
elseif d[:linetype] == :path
x, y = d[:x], d[:y]
Winston.add(wplt, Winston.Curve(x, y; e...))
fillrange = d[:fillrange]
if fillrange != nothing
if isa(fillrange, AbstractVector)
y2 = fillrange
else
y2 = Float64[fillrange for yi in y]
end
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
end
elseif d[:linetype] == :scatter
if d[:markershape] == :none
d[:markershape] = :ellipse
end
# elseif d[:linetype] == :step
# fn = Winston.XXX
# elseif d[:linetype] == :stepinverted
# fn = Winston.XXX
elseif d[:linetype] == :sticks
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
# elseif d[:linetype] == :dots
# fn = Winston.XXX
# elseif d[:linetype] == :heatmap
# fn = Winston.XXX
# elseif d[:linetype] == :hexbin
# fn = Winston.XXX
elseif d[:linetype] == :hist
hst = hist(d[:y], d[:nbins])
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :nbins)...))
# elseif d[:linetype] == :bar
# # fn = Winston.XXX
else
error("linetype $(d[:linetype]) not supported by Winston.")
end
# markershape
if d[:markershape] != :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
end
# optionally add a regression line
d[:smooth] && d[:linetype] != :hist && addRegressionLineWinston(d, wplt)
push!(plt.seriesargs, d)
plt
end
# ----------------------------------------------------------------
@compat const _winstonNames = Dict(
:xlims => :xrange,
:ylims => :yrange,
:xscale => :xlog,
:yscale => :ylog,
)
function updatePlotItems(plt::Plot{WinstonPackage}, d::Dict)
window, canvas, wplt = getWinstonItems(plt)
for k in (:xlabel, :ylabel, :title, :xlims, :ylims)
if haskey(d, k)
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
end
end
for k in (:xscale, :yscale)
if haskey(d, k)
islogscale = d[k] == :log10
Winston.setattr(wplt, (k == :xscale ? :xlog : :ylog), islogscale)
end
end
end
# ----------------------------------------------------------------
function createWinstonAnnotationObject(plt::Plot{WinstonPackage}, x, y, val::@compat(AbstractString))
Winston.text(x, y, val)
end
function addAnnotations{X,Y,V}(plt::Plot{WinstonPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns
createWinstonAnnotationObject(plt, ann...)
end
end
# ----------------------------------------------------------------
function buildSubplotObject!(subplt::Subplot{WinstonPackage}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
# ----------------------------------------------------------------
function addWinstonLegend(plt::Plot, wplt)
if plt.initargs[:legend]
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
end
end
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{WinstonPackage})
window, canvas, wplt = getWinstonItems(plt)
addWinstonLegend(plt, wplt)
writemime(io, "image/png", wplt)
end
function Base.display(::PlotsDisplay, plt::Plot{WinstonPackage})
window, canvas, wplt = getWinstonItems(plt)
if window == nothing
if Winston.output_surface != :gtk
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
end
# initialize window
w,h = plt.initargs[:size]
canvas = Gtk.GtkCanvasLeaf()
window = Gtk.GtkWindowLeaf(canvas, plt.initargs[:windowtitle], w, h)
plt.o = (window, canvas, wplt)
end
addWinstonLegend(plt, wplt)
Winston.display(canvas, wplt)
Gtk.showall(window)
end
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonPackage})
# TODO: display/show the Subplot object
end
+426
View File
@@ -0,0 +1,426 @@
# note: I found this list of hex values in a comment by Tatarize here: http://stackoverflow.com/a/12224359
const _masterColorList = [
0xFFFFFF,
0x000000,
0x0000FF,
0x00FF00,
0xFF0000,
0x01FFFE,
0xFFA6FE,
0xFFDB66,
0x006401,
0x010067,
0x95003A,
0x007DB5,
0xFF00F6,
0xFFEEE8,
0x774D00,
0x90FB92,
0x0076FF,
0xD5FF00,
0xFF937E,
0x6A826C,
0xFF029D,
0xFE8900,
0x7A4782,
0x7E2DD2,
0x85A900,
0xFF0056,
0xA42400,
0x00AE7E,
0x683D3B,
0xBDC6FF,
0x263400,
0xBDD393,
0x00B917,
0x9E008E,
0x001544,
0xC28C9F,
0xFF74A3,
0x01D0FF,
0x004754,
0xE56FFE,
0x788231,
0x0E4CA1,
0x91D0CB,
0xBE9970,
0x968AE8,
0xBB8800,
0x43002C,
0xDEFF74,
0x00FFC6,
0xFFE502,
0x620E00,
0x008F9C,
0x98FF52,
0x7544B1,
0xB500FF,
0x00FF78,
0xFF6E41,
0x005F39,
0x6B6882,
0x5FAD4E,
0xA75740,
0xA5FFD2,
0xFFB167,
0x009BFF,
0xE85EBE,
]
function darken(c, v=0.1)
rgba = convert(RGBA, c)
r = max(0, min(rgba.r - v, 1))
g = max(0, min(rgba.g - v, 1))
b = max(0, min(rgba.b - v, 1))
RGBA(r,g,b,rgba.alpha)
end
function lighten(c, v=0.3)
darken(c, -v)
end
# --------------------------------------------------------------
abstract ColorScheme
getColor(scheme::ColorScheme) = getColor(scheme, 1)
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
colorscheme(scheme::ColorScheme) = scheme
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(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
colorscheme{T<:Colorant}(cs::AVec{T}; kw...) = ColorGradient(cs; kw...)
colorscheme(f::Function; kw...) = ColorFunction(f; kw...)
colorscheme(v::AVec; kw...) = ColorVector(v; kw...)
colorscheme(m::AMat; kw...) = size(m,1) == 1 ? map(c->colorscheme(c; kw...), m) : [colorscheme(m[:,i]; kw...) for i in 1:size(m,2)]'
colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
# --------------------------------------------------------------
convertColor(c::@compat(Union{AbstractString, Symbol})) = parse(Colorant, string(c))
convertColor(c::Colorant) = c
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
convertColor(c::ColorScheme) = c
function convertColor(c, α::Real)
c = convertColor(c)
RGBA(RGB(c), α)
end
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
convertColor(c, α::@compat(Void)) = convertColor(c)
# backup... try to convert
getColor(c) = convertColor(c)
# --------------------------------------------------------------
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
@compat const _gradients = Dict(
:blues => [colorant"lightblue", colorant"darkblue"],
:reds => [colorant"lightpink", colorant"darkred"],
:greens => [colorant"lightgreen", colorant"darkgreen"],
:redsblues => [colorant"darkred", RGB(0.8,0.85,0.8), colorant"darkblue"],
:bluesreds => [colorant"darkblue", RGB(0.8,0.85,0.8), colorant"darkred"],
:heat => [colorant"lightyellow", colorant"orange", colorant"darkred"],
:grays => [RGB(.95,.95,.95),RGB(.05,.05,.05)],
:rainbow => _rainbowColors,
:lightrainbow => map(lighten, _rainbowColors),
:darkrainbow => map(darken, _rainbowColors),
:darktest => _testColors,
:lighttest => map(c -> lighten(c, 0.3), _testColors),
)
# --------------------------------------------------------------
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
immutable ColorGradient <: ColorScheme
colors::Vector{Colorant}
values::Vector{Float64}
function ColorGradient{T<:Colorant,S<:Real}(cs::AVec{T}, vals::AVec{S} = 0:1; alpha = nothing)
if length(cs) == length(vals)
return new(convertColor(cs,alpha), collect(vals))
end
# otherwise interpolate evenly between the minval and maxval
minval, maxval = minimum(vals), maximum(vals)
vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
new(convertColor(cs,alpha), vs)
end
end
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:1; kw...)
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
# if we passed in the right number of values, create the gradient directly
cs = _gradients[s]
ColorGradient(cs, vals; kw...)
end
function ColorGradient{T<:Real}(cs::AVec{Symbol}, vals::AVec{T} = 0:1; kw...)
ColorGradient(map(convertColor, cs), vals; kw...)
end
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]
function getColorZ(gradient::ColorGradient, z::Real)
cs = gradient.colors
vs = gradient.values
n = length(cs)
@assert n > 0 && n == length(vs)
# can we just return the first color?
if z <= vs[1] || n == 1
return cs[1]
end
# find the bounding colors and interpolate
for i in 2:n
if z <= vs[i]
return interpolate_rgb(cs[i-1], cs[i], (z - vs[i-1]) / (vs[i] - vs[i-1]))
end
end
# if we get here, return the last color
cs[end]
end
getColorVector(gradient::ColorGradient) = gradient.colors
# for 0.3
Colors.RGBA(c::Colorant) = RGBA(red(c), green(c), blue(c), alpha(c))
Colors.RGB(c::Colorant) = RGB(red(c), green(c), blue(c))
function interpolate_rgb(c1::Colorant, c2::Colorant, w::Real)
rgb1 = RGBA(c1)
rgb2 = RGBA(c2)
r = interpolate(rgb1.r, rgb2.r, w)
g = interpolate(rgb1.g, rgb2.g, w)
b = interpolate(rgb1.b, rgb2.b, w)
a = interpolate(rgb1.alpha, rgb2.alpha, w)
RGBA(r, g, b, a)
end
function interpolate(v1::Real, v2::Real, w::Real)
(1-w) * v1 + w * v2
end
# --------------------------------------------------------------
"Wraps a function, taking an index and returning a Colorant"
immutable ColorFunction <: ColorScheme
f::Function
end
getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx)
# --------------------------------------------------------------
"Wraps a function, taking an z-value and returning a Colorant"
immutable ColorZFunction <: ColorScheme
f::Function
end
getColorZ(scheme::ColorFunction, z::Real) = scheme.f(z)
# --------------------------------------------------------------
"Wraps a vector of colors... may be vector of Symbol/String/Colorant"
immutable ColorVector <: ColorScheme
v::Vector{Colorant}
ColorVector(v::AVec; alpha = nothing) = new(convertColor(v,alpha))
end
getColor(scheme::ColorVector, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))])
getColorVector(scheme::ColorVector) = scheme.v
# --------------------------------------------------------------
"Wraps a single color"
immutable ColorWrapper <: ColorScheme
c::RGBA
ColorWrapper(c::Colorant; alpha = nothing) = new(convertColor(c, alpha))
end
ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Colorant, s), alpha))
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
# --------------------------------------------------------------
isbackgrounddark(bgcolor::Color) = Lab(bgcolor).l < 0.5
# move closer to lighter/darker depending on background value
function adjustAway(val, bgval, vmin=0., vmax=100.)
if bgval < 0.5 * (vmax+vmin)
tmp = max(val, bgval)
return 0.5 * (tmp + max(tmp, vmax))
else
tmp = min(val, bgval)
return 0.5 * (tmp + min(tmp, vmin))
end
end
# borrowed from http://stackoverflow.com/a/1855903:
lightnessLevel(c::Colorant) = 0.299 * red(c) + 0.587 * green(c) + 0.114 * blue(c)
isdark(c::Colorant) = lightnessLevel(c) < 0.5
islight(c::Colorant) = !isdark(c)
function convertHexToRGB(h::Unsigned)
mask = 0x0000FF
RGB([(x & mask) / 0xFF for x in (h >> 16, h >> 8, h)]...)
end
const _allColors = map(convertHexToRGB, _masterColorList)
const _darkColors = filter(isdark, _allColors)
const _lightColors = filter(islight, _allColors)
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
const _defaultNumColors = 17
# --------------------------------------------------------------
# Methods to automatically generate gradients for color selection based on
# background color and a short list of seed colors
# here are some magic constants that could be changed if you really want
const _lightness_darkbg = [80.0]
const _lightness_lightbg = [60.0]
const _lch_c_const = [60]
function adjust_lch(color, l, c)
lch = convert(LCHab, color)
convert(RGB, LCHab(l, c, lch.h))
end
function lightness_from_background(bgcolor)
bglight = convert(LCHab, bgcolor).l
bglight < 50.0 ? _lightness_darkbg[1] : _lightness_lightbg[1]
end
function gradient_from_list(cs)
zvalues = Plots.get_zvalues(length(cs))
indices = sortperm(zvalues)
sorted_colors = map(RGBA, cs[indices])
sorted_zvalues = zvalues[indices]
ColorGradient(sorted_colors, sorted_zvalues)
end
function generate_colorgradient(bgcolor = colorant"white";
color_bases = color_bases=[colorant"steelblue",colorant"orangered"],
lightness = lightness_from_background(bgcolor),
chroma = _lch_c_const[1],
n = _defaultNumColors)
seed_colors = vcat(bgcolor, map(c -> adjust_lch(c, lightness, chroma), color_bases))
colors = distinguishable_colors(n,
seed_colors,
lchoices=Float64[lightness],
cchoices=Float64[chroma],
hchoices=linspace(0, 340, 20)
)[2:end]
gradient_from_list(colors)
end
function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
grad = if palette == :auto
generate_colorgradient(bgcolor)
else
ColorGradient(palette)
end
zrng = get_zvalues(numcolors)
RGBA[getColorZ(grad, z) for z in zrng]
end
# ----------------------------------------------------------------------------------
function getpctrange(n::Int)
n > 0 || error()
n == 1 && return zeros(1)
zs = [0.0, 1.0]
for i in 3:n
sorted = sort(zs)
diffs = diff(sorted)
widestj = 0
widest = 0.0
for (j,d) in enumerate(diffs)
if d > widest
widest = d
widestj = j
end
end
push!(zs, sorted[widestj] + 0.5 * diffs[widestj])
end
zs
end
function get_zvalues(n::Int)
offsets = getpctrange(ceil(Int,n/4)+1)/4
offsets = vcat(offsets[1], offsets[3:end])
zvalues = Float64[]
for offset in offsets
append!(zvalues, offset + [0.0, 0.5, 0.25, 0.75])
end
vcat(zvalues[1], 1.0, zvalues[2:n-1])
end
# ----------------------------------------------------------------------------------
# TODO: try to use the algorithms from https://github.com/timothyrenner/ColorBrewer.jl
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
# function getBackgroundRGBColor(c, d::Dict)
function handlePlotColors(::PlottingPackage, d::Dict)
if :background_color in supportedArgs()
bgcolor = convertColor(d[:background_color])
else
bgcolor = _plotDefaults[:background_color]
if d[:background_color] != _plotDefaults[:background_color]
warn("Cannot set background_color with backend $(backend())")
end
end
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
# set the foreground color (text, ticks, gridlines) to be white or black depending
# on how dark the background is.
fgcolor = get(d, :foreground_color, :auto)
fgcolor = if fgcolor == :auto
isdark(bgcolor) ? colorant"white" : colorant"black"
else
convertColor(fgcolor)
end
# bgcolor
d[:background_color] = colorscheme(bgcolor)
d[:foreground_color] = colorscheme(fgcolor)
end
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
function getSeriesRGBColor(c, initargs::Dict, n::Int)
if c == :auto
c = autopick(initargs[:color_palette], n)
end
# c should now be a subtype of ColorScheme
colorscheme(c)
end
+63
View File
@@ -0,0 +1,63 @@
# TODO:
"""
- load Contours.jl similar to DataFrames
- method to build grid from x/y/z vectors
- method to wrap contours creation
- method to plot contours as custom shapes (TODO: create Stroke and Fill types and add markerstroke/markerfill args)
"""
# # ----------------------------------------------------------
# # ----------------------------------------------------------
# immutable Vertex
# x::Float64
# y::Float64
# z::Float64
# end
# immutable Edge
# v::Vertex
# u::Vertex
# end
# # ----------------------------------------------------------
# # one rectangle's z-values and the center vertex
# # z is ordered: topleft, topright, bottomright, bottomleft
# immutable GridRect
# z::Vector{Float64}
# center::Vertex
# data::Vector{Vertex}
# end
# type Grid
# xs::Vector{Float64}
# ys::Vector{Float64}
# rects::Matrix{GridRect}
# end
# function splitDataEvenly(v::AbstractVector{Float64}, n::Int)
# vs = sort(v)
# end
# # the goal here is to create the vertical and horizontal partitions
# # which define the grid, so that the data is somewhat evenly split
# function bucketData(x, y, z)
# end
# function buildGrid(x, y, z)
# # create
# end
-134
View File
@@ -1,134 +0,0 @@
# https://github.com/dcjones/Gadfly.jl
immutable GadflyPackage <: PlottingPackage end
# create a blank Gadfly.Plot object
function plot(pkg::GadflyPackage; kw...)
@eval import DataFrames
plt = Gadfly.Plot()
plt.mapping = Dict()
plt.data_source = DataFrames.DataFrame()
plt.layers = plt.layers[1:0]
# add the title, axis labels, and theme
d = Dict(kw)
plt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
Gadfly.Guide.ylabel(d[:ylabel]),
Gadfly.Guide.title(d[:title])]
# add the legend?
if d[:legend]
unshift!(plt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[]))
end
plt.theme = Gadfly.Theme(background_color = (haskey(d, :background_color) ? d[:background_color] : colorant"white"))
Plot(plt, pkg, 0)
end
function getGeomFromLineType(linetype::Symbol, nbins::Int)
linetype == :line && return Gadfly.Geom.line
linetype == :dots && return Gadfly.Geom.point
linetype == :bar && return Gadfly.Geom.bar
linetype == :step && return Gadfly.Geom.step
linetype == :hist && return Gadfly.Geom.histogram(bincount=nbins)
linetype == :none && return Gadfly.Geom.point # change this? are we usually pairing no line with scatterplots?
linetype == :sticks && return Gadfly.Geom.bar
error("linetype $linetype not currently supported with Gadfly")
end
function getGeoms(linetype::Symbol, marker::Symbol, nbins::Int)
geoms = []
# handle heatmaps (hexbins) specially
if linetype in (:heatmap,:hexbin)
push!(geoms, Gadfly.Geom.hexbin(xbincount=nbins, ybincount=nbins))
else
# for other linetypes, get the correct Geom
push!(geoms, getGeomFromLineType(linetype, nbins))
# for any marker, add Geom.point
if marker != :none
push!(geoms, Gadfly.Geom.point)
end
end
geoms
end
# plot one data series
function plot!(::GadflyPackage, plt::Plot; kw...)
d = Dict(kw)
gfargs = []
# add the Geoms
append!(gfargs, getGeoms(d[:linetype], d[:marker], d[:nbins]))
# set color, line width, and point size
theme = Gadfly.Theme(default_color = d[:color],
line_width = d[:width] * Gadfly.px,
default_point_size = d[:markersize] * Gadfly.px)
push!(gfargs, theme)
# add a regression line?
if d[:reg]
push!(gfargs, Gadfly.Geom.smooth(method=:lm))
end
# for histograms, set x=y
x = d[d[:linetype] == :hist ? :y : :x]
# add to the legend
if length(plt.o.guides) > 0 && isa(plt.o.guides[1], Gadfly.Guide.ManualColorKey)
push!(plt.o.guides[1].labels, d[:label])
push!(plt.o.guides[1].colors, d[:color])
end
if d[:axis] != :left
warn("Gadly only supports one y axis")
end
# add the layer to the Gadfly.Plot
prepend!(plt.o.layers, Gadfly.layer(unique(gfargs)...; x = x, y = d[:y]))
plt
end
function Base.display(::GadflyPackage, plt::Plot)
display(plt.o)
end
# -------------------------------
function savepng(::GadflyPackage, plt::PlottingObject, fn::String;
w = 6 * Gadfly.inch,
h = 4 * Gadfly.inch)
Gadfly.draw(Gadfly.PNG(fn, w, h), plt.o)
end
# -------------------------------
# # create the underlying object (each backend will do this differently)
# o = buildSubplotObject(plts, pkg, layout)
function buildSubplotObject!(::GadflyPackage, subplt::Subplot)
i = 0
rows = []
for rowcnt in subplt.layout.rowcounts
push!(rows, Gadfly.hstack([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
i += rowcnt
end
subplt.o = Gadfly.vstack(rows...)
end
function Base.display(::GadflyPackage, subplt::Subplot)
display(subplt.o)
end
+104
View File
@@ -0,0 +1,104 @@
defaultOutputFormat(plt::PlottingObject) = "png"
function png(plt::PlottingObject, fn::@compat(AbstractString))
fn = addExtension(fn, "png")
io = open(fn, "w")
writemime(io, MIME("image/png"), plt)
close(io)
end
png(fn::@compat(AbstractString)) = png(current(), fn)
function svg(plt::PlottingObject, fn::@compat(AbstractString))
fn = addExtension(fn, "svg")
io = open(fn, "w")
writemime(io, MIME("image/svg+xml"), plt)
close(io)
end
svg(fn::@compat(AbstractString)) = svg(current(), fn)
function pdf(plt::PlottingObject, fn::@compat(AbstractString))
fn = addExtension(fn, "pdf")
io = open(fn, "w")
writemime(io, MIME("application/pdf"), plt)
close(io)
end
pdf(fn::@compat(AbstractString)) = pdf(current(), fn)
function ps(plt::PlottingObject, fn::@compat(AbstractString))
fn = addExtension(fn, "ps")
io = open(fn, "w")
writemime(io, MIME("application/postscript"), plt)
close(io)
end
ps(fn::@compat(AbstractString)) = ps(current(), fn)
# ----------------------------------------------------------------
@compat const _savemap = Dict(
"png" => png,
"svg" => svg,
"pdf" => pdf,
"ps" => ps,
)
function getExtension(fn::@compat(AbstractString))
pieces = split(fn, ".")
length(pieces) > 1 || error("Can't extract file extension: ", fn)
ext = pieces[end]
haskey(_savemap, ext) || error("Invalid file extension: ", fn)
ext
end
function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
try
oldext = getExtension(fn)
if oldext == ext
return fn
else
return "$fn.$ext"
end
catch
return "$fn.$ext"
end
end
function savefig(plt::PlottingObject, fn::@compat(AbstractString))
# get the extension
local ext
try
ext = getExtension(fn)
catch
# if we couldn't extract the extension, add the default
ext = defaultOutputFormat(plt)
fn = addExtension(fn, ext)
end
# save it
func = get(_savemap, ext) do
error("Unsupported extension $ext with filename ", fn)
end
func(plt, fn)
end
savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
# savepng(args...; kw...) = savepng(current(), args...; kw...)
# savepng(plt::PlottingObject, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
# ---------------------------------------------------------
gui(plt::PlottingObject = current()) = display(PlotsDisplay(), plt)
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::PlottingObject) = gui(plt)
+353 -335
View File
@@ -6,94 +6,67 @@ const CURRENT_PLOT = CurrentPlot(Nullable{PlottingObject}())
isplotnull() = isnull(CURRENT_PLOT.nullableplot)
function currentPlot()
function current()
if isplotnull()
error("No current plot/subplot")
end
get(CURRENT_PLOT.nullableplot)
end
currentPlot!(plot::PlottingObject) = (CURRENT_PLOT.nullableplot = Nullable(plot))
current(plot::PlottingObject) = (CURRENT_PLOT.nullableplot = Nullable(plot))
# ---------------------------------------------------------
Base.string(plt::Plot) = "Plot{$(plt.plotter) n=$(plt.n)}"
Base.string(plt::Plot) = "Plot{$(plt.backend) n=$(plt.n)}"
Base.print(io::IO, plt::Plot) = print(io, string(plt))
Base.show(io::IO, plt::Plot) = print(io, string(plt))
getplot(plt::Plot) = plt
getinitargs(plt::Plot, idx::Int = 1) = plt.initargs
convertSeriesIndex(plt::Plot, n::Int) = n
# ---------------------------------------------------------
doc"""
The main plot command. Call `plotter!(:module)` to set the current plotting backend.
Commands are converted into the relevant plotting commands for that package:
"""
The main plot command. Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```
plotter!(:gadfly)
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
plotter!(:qwt)
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
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 currentPlot
plot!(args...; kw...) # adds to the `currentPlot`
plot(args...; kw...) # creates a new plot window, and sets it to be the current
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
There are lots of ways to pass in data... just try it and it will likely work as expected.
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.
Some keyword arguments you can set:
```
axis # :left or :right
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
label # string or symbol, applies to that line, may go in a legend
width # width of a line
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
markercolor # same choices as `color`
markersize # size of the marker
nbins # number of bins for heatmap/hexbin and histograms
heatmap_c # color cutoffs for Qwt heatmaps
fillto # fillto value for area plots
title # string or symbol, title of the plot
xlabel # string or symbol, label on the bottom (x) axis
ylabel # string or symbol, label on the left (y) axis
yrightlabel # string or symbol, label on the right (y) axis
reg # true or false, add a regression line for each line
size # (Int,Int), resize the enclosing window
pos # (Int,Int), move the enclosing window to this position
windowtitle # string or symbol, set the title of the enclosing windowtitle
screen # Integer, move enclosing window to this screen number (for multiscreen desktops)
show # true or false, show the plot (in case you don't want the window to pop up right away)
```
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
(yes I know it's not gramatically correct, but it's easy to use and implement)
```
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
```
"""
# -------------------------
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
plt = plot(plotter(); getPlotKeywordArgs(kw, 1, 0)...) # create a new, blank plot
plot!(plt, args...; kw...) # add to it
pkg = backend()
d = Dict(kw)
preprocessArgs!(d)
dumpdict(d, "After plot preprocessing")
plotargs = getPlotArgs(pkg, d, 1)
dumpdict(plotargs, "Plot args")
plt = plot(pkg; plotargs...) # create a new, blank plot
delete!(d, :background_color)
plot!(plt, args...; d...) # add to it
end
# this adds to the current plot
# this adds to the current plot, or creates a new plot if none are current
function plot!(args...; kw...)
plot!(currentPlot(), args...; kw...)
local plt
try
plt = current()
catch
return plot(args...; kw...)
end
plot!(current(), args...; kw...)
end
# not allowed:
@@ -103,312 +76,357 @@ end
# this adds to a specific plot... most plot commands will flow through here
function plot!(plt::Plot, args...; kw...)
d = Dict(kw)
preprocessArgs!(d)
# # increment n if we're going directly to the package's plot method
# if length(args) == 0
# plt.n += 1
# end
dumpdict(d, "After plot! preprocessing")
# plot!(plt.plotter, plt, args...; kw...)
warnOnUnsupportedArgs(plt.backend, d)
# grouping
groupargs = get(d, :group, nothing) == nothing ? [] : [extractGroupArgs(d[:group], args...)]
kwList = createKWargsList(plt, args...; kw...)
for (i,d) in enumerate(kwList)
# just in case the backend needs to set up the plot (make it current or something)
preparePlotUpdate(plt)
# get the list of dictionaries, one per series
kwList, xmeta, ymeta = createKWargsList(plt, groupargs..., args...; d...)
# if we were able to extract guide information from the series inputs, then update the plot
# @show xmeta, ymeta
updateDictWithMeta(d, plt.initargs, xmeta, true)
updateDictWithMeta(d, plt.initargs, ymeta, false)
# now we can plot the series
for (i,di) in enumerate(kwList)
plt.n += 1
plot!(plt.plotter, plt; d...)
setTicksFromStringVector(d, di, :x, :xticks)
setTicksFromStringVector(d, di, :y, :yticks)
# remove plot args
for k in keys(_plotDefaults)
delete!(di, k)
end
dumpdict(di, "Series $i")
plot!(plt.backend, plt; di...)
end
currentPlot!(plt)
addAnnotations(plt, d)
# do we want to show it?
d = Dict(kw)
warnOnUnsupportedScales(plt.backend, d)
# add title, axis labels, ticks, etc
if !haskey(d, :subplot)
merge!(plt.initargs, d)
dumpdict(plt.initargs, "Updating plot items")
updatePlotItems(plt, plt.initargs)
end
updatePositionAndSize(plt, d)
current(plt)
# NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
# # do we want to show it?
if haskey(d, :show) && d[:show]
display(plt)
gui()
end
plt
end
# show/update the plot
function Base.display(plt::PlottingObject)
display(plt.plotter, plt)
# --------------------------------------------------------------------
# if x or y are a vector of strings, we should create a list of unique strings,
# and map x/y to be the index of the string... then set the x/y tick labels
function setTicksFromStringVector(d::Dict, di::Dict, sym::Symbol, ticksym::Symbol)
# if the x or y values are strings, set ticks to the unique values, and x/y to the indices of the ticks
v = di[sym]
isa(v, AbstractArray) || return
T = eltype(v)
if T <: @compat(AbstractString) || (!isempty(T.types) && all(x -> x <: @compat(AbstractString), T.types))
ticks = unique(di[sym])
di[sym] = Int[findnext(ticks, v, 1) for v in di[sym]]
if !haskey(d, ticksym) || d[ticksym] == :auto
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
end
end
end
# -------------------------
# --------------------------------------------------------------------
preparePlotUpdate(plt::Plot) = nothing
# --------------------------------------------------------------------
# should we update the x/y label given the meta info during input slicing?
function updateDictWithMeta(d::Dict, initargs::Dict, meta::Symbol, isx::Bool)
lsym = isx ? :xlabel : :ylabel
if initargs[lsym] == default(lsym)
d[lsym] = string(meta)
end
end
updateDictWithMeta(d::Dict, initargs::Dict, meta, isx::Bool) = nothing
# --------------------------------------------------------------------
annotations(::@compat(Void)) = []
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t]
annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ",
"(x, y, annotation)\n got: $(typeof(anns))")
function addAnnotations(plt::Plot, d::Dict)
anns = annotations(get(d, :annotation, nothing))
if !isempty(anns)
addAnnotations(plt, anns)
end
end
# --------------------------------------------------------------------
# create a new "createKWargsList" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
# Special handling for: no args, xmin/xmax, parametric, dataframes
# Then once inputs have been converted, build the series args, map functions, etc.
# This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
# missing
convertToAnyVector(v::@compat(Void); kw...) = Any[nothing], nothing
# fixed number of blank series
convertToAnyVector(n::Integer; kw...) = Any[zeros(0) for i in 1:n], nothing
# numeric vector
convertToAnyVector{T<:Real}(v::AVec{T}; kw...) = Any[v], nothing
# string vector
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}; kw...) = Any[v], nothing
# numeric matrix
convertToAnyVector{T<:Real}(v::AMat{T}; kw...) = Any[v[:,i] for i in 1:size(v,2)], nothing
# function
convertToAnyVector(f::Function; kw...) = Any[f], nothing
# vector of OHLC
convertToAnyVector(v::AVec{OHLC}; kw...) = Any[v], nothing
# list of things (maybe other vectors, functions, or something else)
convertToAnyVector(v::AVec; kw...) = Any[vi for vi in v], nothing
# --------------------------------------------------------------------
# in computeXandY, we take in any of the possible items, convert into proper x/y vectors, then return.
# this is also where all the "set x to 1:length(y)" happens, and also where we assert on lengths.
computeX(x::@compat(Void), y) = 1:length(y)
computeX(x, y) = copy(x)
computeY(x, y::Function) = map(y, x)
computeY(x, y) = copy(y)
function computeXandY(x, y)
if x == nothing && isa(y, Function)
error("If you want to plot the function `$y`, you need to define the x values somehow!")
end
x, y = computeX(x,y), computeY(x,y)
@assert length(x) == length(y)
x, y
end
# --------------------------------------------------------------------
# create n=max(mx,my) series arguments. the shorter list is cycled through
# note: everything should flow through this
function createKWargsList(plt::PlottingObject, x, y; kw...)
xs, xmeta = convertToAnyVector(x; kw...)
ys, ymeta = convertToAnyVector(y; kw...)
# _debugMode.on && dumpcallstack()
mx = length(xs)
my = length(ys)
ret = Any[]
for i in 1:max(mx, my)
# try to set labels using ymeta
d = Dict(kw)
if !haskey(d, :label) && ymeta != nothing
if isa(ymeta, Symbol)
d[:label] = string(ymeta)
elseif isa(ymeta, AVec{Symbol})
d[:label] = string(ymeta[mod1(i,length(ymeta))])
end
end
# build the series arg dict
numUncounted = get(d, :numUncounted, 0)
n = plt.n + i + numUncounted
dumpdict(d, "before getSeriesArgs")
d = getSeriesArgs(plt.backend, getinitargs(plt, n), d, i + numUncounted, convertSeriesIndex(plt, n), n)
dumpdict(d, "after getSeriesArgs")
d[:x], d[:y] = computeXandY(xs[mod1(i,mx)], ys[mod1(i,my)])
if haskey(d, :idxfilter)
d[:x] = d[:x][d[:idxfilter]]
d[:y] = d[:y][d[:idxfilter]]
end
# for linetype `line`, need to sort by x values
if d[:linetype] == :line
# order by x
indices = sortperm(d[:x])
d[:x] = d[:x][indices]
d[:y] = d[:y][indices]
d[:linetype] = :path
end
# cleanup those fields that were used only for generating kw args
for k in (:idxfilter, :numUncounted, :dataframe)
delete!(d, k)
end
# add it to our series list
push!(ret, d)
end
ret, xmeta, ymeta
end
# handle grouping
function createKWargsList(plt::PlottingObject, groupby::GroupBy, args...; kw...)
ret = Any[]
for (i,glab) in enumerate(groupby.groupLabels)
# TODO: don't automatically overwrite labels
kwlist, xmeta, ymeta = createKWargsList(plt, args...; kw...,
idxfilter = groupby.groupIds[i],
label = string(glab),
numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
append!(ret, kwlist)
end
ret, nothing, nothing # TODO: handle passing meta through
end
# pass it off to the x/y version
function createKWargsList(plt::PlottingObject, y; kw...)
createKWargsList(plt, nothing, y; kw...)
end
# contours or surfaces... irregular data
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zvec::AVec; kw...)
error("TODO: contours or surfaces... irregular data")
end
# contours or surfaces... function grid
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...)
# only allow sorted x/y for now
# TODO: auto sort x/y/z properly
@assert x == sort(x)
@assert y == sort(y)
surface = Float64[zf(xi, yi) for xi in x, yi in y]
createKWargsList(plt, x, y, surface; kw...) # passes it to the zmat version
end
# contours or surfaces... matrix grid
function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec, y::AVec, zmat::AMat{T}; kw...)
# only allow sorted x/y for now
# TODO: auto sort x/y/z properly
@assert x == sort(x)
@assert y == sort(y)
@assert size(zmat) == (length(x), length(y))
surf = Array(Any,1,1)
surf[1,1] = convert(Matrix{Float64}, zmat)
createKWargsList(plt, x, y; kw..., surface = surf, linetype = :contour)
end
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs; kw...)
error("Can't pass a Function or Vector{Function} for y without also passing x")
end
# list of functions
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, x; kw...)
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
createKWargsList(plt, x, f; kw...)
end
# special handling... xmin/xmax with function(s)
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, xmin::Real, xmax::Real; kw...)
width = plt.initargs[:size][1]
x = collect(linspace(xmin, xmax, width)) # we don't need more than the width
createKWargsList(plt, x, f; kw...)
end
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
# special handling... xmin/xmax with parametric function(s)
createKWargsList{T<:Real}(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
createKWargsList{T<:Real}(plt::PlottingObject, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
createKWargsList(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = createKWargsList(plt, fx, fy, linspace(umin, umax, numPoints); kw...)
doc"Build a vector of dictionaries which hold the keyword arguments for a call to plot!"
# no args... 1 series
# special handling... no args... 1 series
function createKWargsList(plt::PlottingObject; kw...)
d = Dict(kw)
@assert haskey(d, :y)
if !haskey(d, :x)
d[:x] = 1:length(d[:y])
if !haskey(d, :y)
# assume we just want to create an empty plot object which can be added to later
return [], nothing, nothing
# error("Called plot/subplot without args... must set y in the keyword args. Example: plot(; y=rand(10))")
end
[getPlotKeywordArgs(d, 1, plt.n + 1)]
end
# create one series where y is vectors of numbers
function createKWargsList{T<:Real}(plt::PlottingObject, y::AVec{T}; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d[:x] = 1:length(y)
d[:y] = y
[d]
end
# create one series where x/y are vectors of numbers
function createKWargsList{T<:Real,S<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec{S}; kw...)
@assert length(x) == length(y)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d[:x] = x
d[:y] = y
[d]
end
# create m series, 1 for each column of y
function createKWargsList(plt::PlottingObject, y::AMat; kw...)
n,m = size(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = 1:n
d[:y] = y[:,i]
push!(ret, d)
if haskey(d, :x)
return createKWargsList(plt, d[:x], d[:y]; kw...)
else
return createKWargsList(plt, d[:y]; kw...)
end
ret
end
# create m series, 1 for each column of y
function createKWargsList(plt::PlottingObject, x::AVec, y::AMat; kw...)
n,m = size(y)
@assert length(x) == n
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x
d[:y] = y[:,i]
push!(ret, d)
# --------------------------------------------------------------------
"For DataFrame support. Imports DataFrames and defines the necessary methods which support them."
function dataframes()
@eval import DataFrames
@eval function createKWargsList(plt::PlottingObject, df::DataFrames.DataFrame, args...; kw...)
createKWargsList(plt, args...; kw..., dataframe = df)
end
ret
end
# create m series, 1 for each column of y
function createKWargsList(plt::PlottingObject, x::AMat, y::AMat; kw...)
@assert size(x) == size(y)
n,m = size(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x[:,i]
d[:y] = y[:,i]
push!(ret, d)
# expecting the column name of a dataframe that was passed in... anything else should error
@eval function extractGroupArgs(s::Symbol, df::DataFrames.DataFrame, args...)
if haskey(df, s)
return extractGroupArgs(df[s])
else
error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
end
end
ret
end
# create 1 series, y = f(x)
function createKWargsList(plt::PlottingObject, x::AVec, f::Function; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d[:x] = x
d[:y] = map(f, x)
[d]
end
# create m series, y = f(x), 1 for each column of x
function createKWargsList(plt::PlottingObject, x::AMat, f::Function; kw...)
n,m = size(x)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x[:,i]
d[:y] = map(f, d[:x])
push!(ret, d)
@eval function getDataFrameFromKW(; kw...)
for (k,v) in kw
if k == :dataframe
return v
end
end
error("Missing dataframe argument in arguments!")
end
ret
# the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
@eval convertToAnyVector(s::Symbol; kw...) = Any[getDataFrameFromKW(;kw...)[s]], s
@eval convertToAnyVector(v::AVec{Symbol}; kw...) = (df = getDataFrameFromKW(;kw...); Any[df[s] for s in v]), v
end
# create m series, 1 for each item in y (assumes vectors of something other than numbers... functions? vectors?)
function createKWargsList(plt::PlottingObject, y::AVec; kw...)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = 1:length(y[i])
d[:y] = y[i]
push!(ret, d)
end
ret
end
function getyvec(x::AVec, y::AVec)
@assert length(x) == length(y)
y
end
getyvec(x::AVec, f::Function) = map(f, x)
getyvec(x, y) = error("Couldn't create yvec from types: x ($(typeof(x))), y ($(typeof(y)))")
# --------------------------------------------------------------------
# same, but given an x to use for all series
function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec; kw...)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x
d[:y] = getyvec(x, y[i])
push!(ret, d)
end
ret
end
# same, but m series of (x[i],y[i])
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec; kw...)
@assert length(x) == length(y)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = x[i]
d[:y] = getyvec(x[i], y[i])
push!(ret, d)
end
ret
end
# n empty series
function createKWargsList(plt::PlottingObject, n::Integer; kw...)
ret = []
for i in 1:n
d = getPlotKeywordArgs(kw, i, plt.n + i)
d[:x] = zeros(0)
d[:y] = zeros(0)
push!(ret, d)
end
ret
end
# TODO: handle DataFrames (might have NAs!)
# -------------------------
# # most calls should flow through here now... we create a Dict with the keyword args for each series, and plot them
# function plot!(pkg::PlottingPackage, plt::Plot, args...; kw...)
# kwList = createKWargsList(plt, args...; kw...)
# for (i,d) in enumerate(kwList)
# plt.n += 1
# plot!(pkg, plt; d...)
# end
# plt
# end
# -------------------------
# # These methods are various ways to add to an existing plot
# function plot!{T<:Real}(pkg::PlottingPackage, plt::Plot, y::AVec{T}; kw...)
# plt.n += 1
# # plot!(pkg, plt; x = 1:length(y), y = y, getPlotKeywordArgs(kw, 1, plt)...)
# end
# function plot!{T<:Real,S<:Real}(pkg::PlottingPackage, plt::Plot, x::AVec{T}, y::AVec{S}; kw...) # one line (will assert length(x) == length(y))
# @assert length(x) == length(y)
# plt.n += 1
# plot!(pkg, plt; x=x, y=y, getPlotKeywordArgs(kw, 1, plt)...)
# end
# function plot!(pkg::PlottingPackage, plt::Plot, y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
# n,m = size(y)
# for i in 1:m
# plt.n += 1
# plot!(pkg, plt; x = 1:n, y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
# n,m = size(y)
# for i in 1:m
# @assert length(x) == n
# plt.n += 1
# plot!(pkg, plt; x = x, y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
# @assert size(x) == size(y)
# for i in 1:size(x,2)
# plt.n += 1
# plot!(pkg, plt; x = x[:,i], y = y[:,i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, f::Function; kw...) # one line, y = f(x)
# plt.n += 1
# plot!(pkg, plt; x = x, y = map(f,x), getPlotKeywordArgs(kw, 1, plt)...)
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
# for i in 1:size(x,2)
# xi = x[:,i]
# plt.n += 1
# plot!(pkg, plt; x = xi, y = map(f, xi), getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# # function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
# # for i in 1:length(fs)
# # plt.n += 1
# # plot!(pkg, plt; x = x, y = map(fs[i], x), getPlotKeywordArgs(kw, i, plt)...)
# # end
# # plt
# # end
# function plot!(pkg::PlottingPackage, plt::Plot, y::AVec; kw...) # multiple lines, each with x = 1:length(y[i])
# for i in 1:length(y)
# plt.n += 1
# plot!(pkg, plt; x = 1:length(y[i]), y = y[i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!{T<:Real}(pkg::PlottingPackage, plt::Plot, x::AVec{T}, y::AVec; kw...) # multiple lines, will assert length(x) == length(y[i])
# for i in 1:length(y)
# if typeof(y[i]) <: AbstractVector
# @assert length(x) == length(y[i])
# plt.n += 1
# plot!(pkg, plt; x = x, y = y[i], getPlotKeywordArgs(kw, i, plt)...)
# elseif typeof(y[i]) == Function
# plt.n += 1
# plot!(pkg, plt; x = x, y = map(y[i], x), getPlotKeywordArgs(kw, 1, plt)...)
# end
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, x::AVec, y::AVec; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
# @assert length(x) == length(y)
# for i in 1:length(x)
# @assert length(x[i]) == length(y[i])
# plt.n += 1
# plot!(pkg, plt; x = x[i], y = y[i], getPlotKeywordArgs(kw, i, plt)...)
# end
# plt
# end
# function plot!(pkg::PlottingPackage, plt::Plot, n::Integer; kw...) # n lines, all empty (for updating plots)
# for i in 1:n
# plt.n += 1
# plot(pkg, plt, x = zeros(0), y = zeros(0), getPlotKeywordArgs(kw, i, plt)...)
# end
# end
# -------------------------
# # this is the core method... add to a plot object using kwargs, with args already converted into kwargs
# function plot!(pkg::PlottingPackage, plt::Plot; kw...)
# plot!(pl, plt; kw...)
# end
+220 -29
View File
@@ -1,64 +1,255 @@
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
Base.display(pkg::PlottingPackage, plt::Plot) = error("display($pkg, plt) is not implemented")
immutable GadflyPackage <: PlottingPackage end
immutable ImmersePackage <: PlottingPackage end
immutable PyPlotPackage <: PlottingPackage end
immutable QwtPackage <: PlottingPackage end
immutable UnicodePlotsPackage <: PlottingPackage end
immutable WinstonPackage <: PlottingPackage end
typealias GadflyOrImmerse @compat(Union{GadflyPackage, ImmersePackage})
export
gadfly,
immerse,
pyplot,
qwt,
unicodeplots
# winston
gadfly() = backend(:gadfly)
immerse() = backend(:immerse)
pyplot() = backend(:pyplot)
qwt() = backend(:qwt)
unicodeplots() = backend(:unicodeplots)
# winston() = backend(:winston)
backend_name(::GadflyPackage) = :gadfly
backend_name(::ImmersePackage) = :immerse
backend_name(::PyPlotPackage) = :pyplot
backend_name(::UnicodePlotsPackage) = :unicodeplots
backend_name(::QwtPackage) = :qwt
include("backends/supported.jl")
include("backends/qwt.jl")
include("backends/gadfly.jl")
include("backends/unicodeplots.jl")
include("backends/pyplot.jl")
include("backends/immerse.jl")
include("backends/winston.jl")
# ---------------------------------------------------------
const AVAILABLE_PACKAGES = [:qwt, :gadfly]
const INITIALIZED_PACKAGES = Set{Symbol}()
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
updatePlotItems(pkg::PlottingPackage, plt::Plot, d::Dict) = error("updatePlotItems($pkg, plt, d) is not implemented")
# Base.display(pkg::PlottingPackage, plt::Plot) = error("display($pkg, plt) is not implemented")
type CurrentPackage
updatePositionAndSize{P<:PlottingPackage}(plt::PlottingObject{P}, d::Dict) = nothing #error("updatePositionAndSize(plt,d) is not implemented for $P")
subplot(pkg::PlottingPackage; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# Base.display(pkg::PlottingPackage, subplt::Subplot) = error("display($pkg, subplt) is not implemented")
# ---------------------------------------------------------
const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse]
const INITIALIZED_BACKENDS = Set{Symbol}()
backends() = BACKENDS
function backendInstance(sym::Symbol)
sym == :qwt && return QwtPackage()
sym == :gadfly && return GadflyPackage()
sym == :unicodeplots && return UnicodePlotsPackage()
sym == :pyplot && return PyPlotPackage()
sym == :immerse && return ImmersePackage()
sym == :winston && return WinstonPackage()
error("Unsupported backend $sym")
end
type CurrentBackend
sym::Symbol
pkg::PlottingPackage
end
# const CURRENT_PACKAGE = CurrentPackage(:qwt, QwtPackage())
const CURRENT_PACKAGE = CurrentPackage(:gadfly, GadflyPackage())
CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym))
# ---------------------------------------------------------
function pickDefaultBackend()
try
if Pkg.installed("Immerse") != nothing
return CurrentBackend(:immerse)
end
end
try
if Pkg.installed("Qwt") != nothing
return CurrentBackend(:qwt)
end
end
try
if Pkg.installed("PyPlot") != nothing
return CurrentBackend(:pyplot)
end
end
try
if Pkg.installed("Gadfly") != nothing
return CurrentBackend(:gadfly)
end
end
try
if Pkg.installed("UnicodePlots") != nothing
return CurrentBackend(:unicodeplots)
end
end
try
if Pkg.installed("Winston") != nothing
return CurrentBackend(:winston)
end
end
warn("You don't have any of the supported backends installed! Chose from ", backends())
return CurrentBackend(:gadfly)
end
# const CURRENT_BACKEND = pickDefaultBackend()
# println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym)
doc"""
# ---------------------------------------------------------
"""
Returns the current plotting package name. Initializes package on first call.
"""
function plotter()
function backend()
# error()
currentPackageSymbol = CURRENT_PACKAGE.sym
if !(currentPackageSymbol in INITIALIZED_PACKAGES)
currentBackendSymbol = CURRENT_BACKEND.sym
if !(currentBackendSymbol in INITIALIZED_BACKENDS)
# initialize
print("[Plots.jl] Initializing package: $CURRENT_PACKAGE... ")
if currentPackageSymbol == :qwt
@eval import Qwt
elseif currentPackageSymbol == :gadfly
@eval import Gadfly
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
if currentBackendSymbol == :qwt
try
@eval import Qwt
@eval export Qwt
catch err
warn("Couldn't import Qwt. Install it with: Pkg.clone(\"https://github.com/tbreloff/Qwt.jl.git\")\n (Note: also requires pyqt and pyqwt).")
rethrow(err)
end
elseif currentBackendSymbol == :gadfly
try
@eval import Gadfly, Compose, DataFrames
@eval export Gadfly, Compose, DataFrames
@eval include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
catch err
warn("Couldn't import Gadfly. Install it with: Pkg.add(\"Gadfly\").")
rethrow(err)
end
elseif currentBackendSymbol == :unicodeplots
try
@eval import UnicodePlots
@eval export UnicodePlots
catch err
warn("Couldn't import UnicodePlots. Install it with: Pkg.add(\"UnicodePlots\").")
rethrow(err)
end
elseif currentBackendSymbol == :pyplot
try
@eval import PyPlot
@eval export PyPlot
@eval const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
@eval const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
# @eval const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar"))
if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia
# # TODO: how the hell can I use PyQt4??
# "pyqt4"=>:qt_pyqt4
# PyPlot.backend[1] = "pyqt4"
# PyPlot.gui[1] = :qt_pyqt4
# PyPlot.switch_backend("Qt4Agg")
# only turn on the gui if we want it
if PyPlot.gui != :none
PyPlot.pygui(true)
end
end
catch err
warn("Couldn't import PyPlot. Install it with: Pkg.add(\"PyPlot\").")
rethrow(err)
end
elseif currentBackendSymbol == :immerse
try
@eval import Immerse, Gadfly, Compose, Gtk
@eval export Immerse, Gadfly, Compose, Gtk
@eval include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
catch err
# error("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\").\n Error: ", err)
warn("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\").")
rethrow(err)
end
elseif currentBackendSymbol == :winston
warn("Winston support is deprecated and broken. Try another backend: $BACKENDS")
try
@eval ENV["WINSTON_OUTPUT"] = "gtk"
@eval import Winston, Gtk
@eval export Winston, Gtk
catch err
warn("Couldn't import Winston. Install it with: Pkg.add(\"Winston\").")
rethrow(err)
end
else
error("Unknown plotter $currentPackageSymbol. Choose from: $AVAILABLE_PACKAGES")
error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS")
end
push!(INITIALIZED_PACKAGES, currentPackageSymbol)
println("done.")
push!(INITIALIZED_BACKENDS, currentBackendSymbol)
end
CURRENT_PACKAGE.pkg
CURRENT_BACKEND.pkg
end
doc"""
Set the plot backend. Choose from: :qwt, :gadfly
"""
function plotter!(modname)
Set the plot backend. Choose from: :qwt, :gadfly, :unicodeplots, :immerse, :pyplot
"""
function backend(pkg::PlottingPackage)
CURRENT_BACKEND.sym = backend_name(pkg)
CURRENT_BACKEND.pkg = pkg
end
function backend(modname)
# set the PlottingPackage
if modname == :qwt
CURRENT_PACKAGE.pkg = QwtPackage()
CURRENT_BACKEND.pkg = QwtPackage()
elseif modname == :gadfly
CURRENT_PACKAGE.pkg = GadflyPackage()
CURRENT_BACKEND.pkg = GadflyPackage()
elseif modname == :unicodeplots
CURRENT_BACKEND.pkg = UnicodePlotsPackage()
elseif modname == :pyplot
CURRENT_BACKEND.pkg = PyPlotPackage()
elseif modname == :immerse
CURRENT_BACKEND.pkg = ImmersePackage()
elseif modname == :winston
CURRENT_BACKEND.pkg = WinstonPackage()
else
error("Unknown plotter $modname. Choose from: $AVAILABLE_PACKAGES")
error("Unknown backend $modname. Choose from: $BACKENDS")
end
# update the symbol
CURRENT_PACKAGE.sym = modname
CURRENT_BACKEND.sym = modname
# println("[Plots.jl] Switched to backend: ", modname)
# return the package
CURRENT_PACKAGE.pkg
CURRENT_BACKEND.pkg
end
-67
View File
@@ -1,67 +0,0 @@
# https://github.com/tbreloff/Qwt.jl
immutable QwtPackage <: PlottingPackage end
# -------------------------------
function adjustQwtKeywords(iscreating::Bool; kw...)
d = Dict(kw)
d[:heatmap_n] = d[:nbins]
if d[:linetype] == :hexbin
d[:linetype] = :heatmap
elseif d[:linetype] == :dots
d[:linetype] = :none
d[:marker] = :hexagon
elseif !iscreating && d[:linetype] == :bar
return barHack(; kw...)
elseif !iscreating && d[:linetype] == :hist
return barHack(; histogramHack(; kw...)...)
end
d
end
function plot(pkg::QwtPackage; kw...)
kw = adjustQwtKeywords(true; kw...)
plt = Plot(Qwt.plot(zeros(0,0); kw..., show=false), pkg, 0)
plt
end
function plot!(::QwtPackage, plt::Plot; kw...)
kw = adjustQwtKeywords(false; kw...)
Qwt.oplot(plt.o; kw...)
end
function Base.display(::QwtPackage, plt::Plot)
Qwt.refresh(plt.o)
Qwt.showwidget(plt.o)
end
# -------------------------------
savepng(::QwtPackage, plt::PlottingObject, fn::String, args...) = Qwt.savepng(plt.o, fn)
# -------------------------------
# # create the underlying object (each backend will do this differently)
# o = buildSubplotObject(plts, pkg, layout)
function buildSubplotObject!(::QwtPackage, subplt::Subplot)
i = 0
rows = []
for rowcnt in subplt.layout.rowcounts
push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
i += rowcnt
end
subplt.o = Qwt.vsplitter(rows...)
end
function Base.display(::QwtPackage, subplt::Subplot)
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.showwidget(subplt.o)
end
+119
View File
@@ -0,0 +1,119 @@
# TODO: there should be a distinction between an object that will manage a full plot, vs a component of a plot.
# the PlotRecipe as currently implemented is more of a "custom component"
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
# actually... maybe those should explicitly derive from PlottingObject???
abstract PlotRecipe
getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
getRecipeArgs(recipe::PlotRecipe) = ()
plot(recipe::PlotRecipe, args...; kw...) = plot(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
plot!(recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
# -------------------------------------------------
function rotate(x::Real, y::Real, θ::Real; center = (0,0))
cx = x - center[1]
cy = y - center[2]
xrot = cx * cos(θ) - cy * sin(θ)
yrot = cy * cos(θ) + cx * sin(θ)
xrot + center[1], yrot + center[2]
end
# -------------------------------------------------
type EllipseRecipe <: PlotRecipe
w::Float64
h::Float64
x::Float64
y::Float64
θ::Float64
end
EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
# return x,y coords of a rotated ellipse, centered at the origin
function rotatedEllipse(w, h, x, y, θ, rotθ)
# # coord before rotation
xpre = w * cos(θ)
ypre = h * sin(θ)
# rotate and translate
r = rotate(xpre, ypre, rotθ)
x + r[1], y + r[2]
end
function getRecipeXY(ep::EllipseRecipe)
x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
top = rotate(0, ep.h, ep.θ)
right = rotate(ep.w, 0, ep.θ)
linex = Float64[top[1], 0, right[1]] + ep.x
liney = Float64[top[2], 0, right[2]] + ep.y
Any[x, linex], Any[y, liney]
end
function getRecipeArgs(ep::EllipseRecipe)
[(:line, (3, [:dot :solid], [:red :blue], :path))]
end
# -------------------------------------------------
"Correlation scatter matrix"
function corrplot{T<:Real,S<:Real}(mat::AMat{T}, corrmat::AMat{S} = cor(mat);
colors = :redsblues,
labels = nothing, kw...)
m = size(mat,2)
centers = Float64[mean(extrema(mat[:,i])) for i in 1:m]
# might be a mistake?
@assert m <= 20
@assert size(corrmat) == (m,m)
# create a subplot grid, and a gradient from -1 to 1
p = subplot(rand(0,m^2); n=m^2, leg=false, grid=false, kw...)
cgrad = ColorGradient(colors, [-1,1])
# make all the plots
for i in 1:m
for j in 1:m
idx = p.layout[i,j]
plt = p.plts[idx]
if i==j
# histogram on diagonal
histogram!(plt, mat[:,i], c=:black)
i > 1 && plot!(plt, yticks = :none)
elseif i < j
# annotate correlation value in upper triangle
mi, mj = centers[i], centers[j]
plot!(plt, [mj], [mi],
ann = (mj, mi, text(@sprintf("Corr:\n%0.3f", corrmat[i,j]), 15)),
yticks=:none)
else
# scatter plots in lower triangle; color determined by correlation
c = RGBA(RGB(getColorZ(cgrad, corrmat[i,j])), 0.3)
scatter!(plt, mat[:,j], mat[:,i], w=0, ms=3, c=c, smooth=true)
end
if labels != nothing && length(labels) >= m
i == m && xlabel!(plt, string(labels[j]))
j == 1 && ylabel!(plt, string(labels[i]))
end
end
end
# link the axes
subplot!(p, link = (r,c) -> (true, r!=c))
end
"Sparsity plot... heatmap of non-zero values of a matrix"
function spy{T<:Real}(y::AMat{T}; kw...)
I,J,V = findnz(y)
heatmap(J, I; leg=false, yflip=true, nbins=size(y), kw...)
end
+325 -31
View File
@@ -1,10 +1,13 @@
function subplotlayout(sz::@compat(Tuple{Int,Int}))
GridLayout(sz...)
end
# create a layout directly
SubplotLayout(rowcounts::AbstractVector{Int}) = SubplotLayout(sum(rowcounts), rowcounts)
function subplotlayout(rowcounts::AVec{Int})
FlexLayout(sum(rowcounts), rowcounts)
end
# create a layout given counts... nr/nc == -1 implies we figure out a good number automatically
function SubplotLayout(numplts::Int, nr::Int, nc::Int)
function subplotlayout(numplts::Int, nr::Int, nc::Int)
# figure out how many rows/columns we need
if nr == -1
@@ -18,6 +21,11 @@ function SubplotLayout(numplts::Int, nr::Int, nc::Int)
nc = ceil(Int, numplts / nr)
end
# if it's a perfect rectangle, just create a grid
if numplts == nr * nc
return GridLayout(nr, nc)
end
# create the rowcounts vector
i = 0
rowcounts = Int[]
@@ -27,26 +35,111 @@ function SubplotLayout(numplts::Int, nr::Int, nc::Int)
i += cnt
end
SubplotLayout(numplts, rowcounts)
FlexLayout(numplts, rowcounts)
end
Base.length(layout::SubplotLayout) = layout.numplts
Base.length(layout::FlexLayout) = layout.numplts
Base.start(layout::FlexLayout) = 1
Base.done(layout::FlexLayout, state) = state > length(layout)
function Base.next(layout::FlexLayout, state)
r = 1
c = 0
for i = 1:state
c += 1
if c > layout.rowcounts[r]
r += 1
c = 1
end
end
(r,c), state + 1
end
nrows(layout::FlexLayout) = length(layout.rowcounts)
ncols(layout::FlexLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row])
# get the plot index given row and column
Base.getindex(layout::FlexLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c
Base.length(layout::GridLayout) = layout.nr * layout.nc
Base.start(layout::GridLayout) = 1
Base.done(layout::GridLayout, state) = state > length(layout)
function Base.next(layout::GridLayout, state)
r = div(state-1, layout.nc) + 1
c = mod1(state, layout.nc)
(r,c), state + 1
end
nrows(layout::GridLayout) = layout.nr
ncols(layout::GridLayout) = layout.nc
ncols(layout::GridLayout, row::Int) = layout.nc
# get the plot index given row and column
Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]]
# handle "linking" the subplot axes together
# each backend should implement the handleLinkInner and expandLimits! methods
function linkAxis(subplt::Subplot, isx::Bool)
# collect the list of plots and the expanded limits for those plots that should be linked on this axis
includedPlots = Any[]
# lims = [Inf, -Inf]
lims = Dict{Int,Any}() # maps column to xlim
for (i,(r,c)) in enumerate(subplt.layout)
# shouldlink will be a bool or nothing. if nothing, then use linkx/y (which is true if we get to this code)
shouldlink = subplt.linkfunc(r,c)[isx ? 1 : 2]
if shouldlink == nothing || shouldlink
plt = subplt.plts[i]
# if we don't have this
k = isx ? c : r
if (firstone = !haskey(lims, k))
lims[k] = [Inf, -Inf]
end
isinner = (isx && r < nrows(subplt.layout)) || (!isx && !firstone)
push!(includedPlots, (plt, isinner, k))
expandLimits!(lims[k], plt, isx)
end
end
# do the axis adjustments
for (plt, isinner, k) in includedPlots
if isinner
handleLinkInner(plt, isx)
end
(isx ? xlims! : ylims!)(plt, lims[k]...)
end
end
# ------------------------------------------------------------
Base.string(subplt::Subplot) = "Subplot{$(subplt.plotter) p=$(subplt.p) n=$(subplt.n)}"
Base.string(subplt::Subplot) = "Subplot{$(subplt.backend) p=$(subplt.p) n=$(subplt.n)}"
Base.print(io::IO, subplt::Subplot) = print(io, string(subplt))
Base.show(io::IO, subplt::Subplot) = print(io, string(subplt))
getplot(subplt::Subplot) = subplt.plts[mod1(subplt.n, subplt.p)]
getplot(subplt::Subplot, idx::Int = subplt.n) = subplt.plts[mod1(idx, subplt.p)]
getinitargs(subplt::Subplot, idx::Int) = getplot(subplt, idx).initargs
convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p)
# ------------------------------------------------------------
function validateSubplotSupported()
if !subplotSupported()
error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(backendInstance(pkg)), backends()),", "))
end
end
doc"""
"""
Create a series of plots:
```
y = rand(100,3)
@@ -54,40 +147,180 @@ Create a series of plots:
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows to 1 (so there are n columns)
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns to 1 (so there are n rows)
subplot(y; layout = [1, 2]) # explicit layout by row... plot #1 goes by itself in the first row, plots 2 and 3 split the 2nd row (note the n kw is unnecessary)
subplot(plts, n; nr = -1, nc = -1) # build a layout from existing plots
subplot(plts, layout) # build a layout from existing plots
```
"""
function subplot(args...; kw...)
validateSubplotSupported()
d = Dict(kw)
preprocessArgs!(d)
# figure out the layout
if haskey(d, :layout)
layout = SubplotLayout(d[:layout])
layoutarg = get(d, :layout, nothing)
if layoutarg != nothing
layout = subplotlayout(layoutarg)
else
if !haskey(d, :n)
n = get(d, :n, -1)
if n < 0
error("You must specify either layout or n when creating a subplot: ", d)
end
layout = SubplotLayout(d[:n], get(d, :nr, -1), get(d, :nc, -1))
layout = subplotlayout(n, get(d, :nr, -1), get(d, :nc, -1))
end
# initialize the individual plots
pkg = plotter()
kw0 = getPlotKeywordArgs(kw, 1, 0)
plts = Plot[plot(pkg; kw0..., show=false) for i in 1:length(layout)]
pkg = backend()
plts = Plot{typeof(pkg)}[]
for i in 1:length(layout)
di = getPlotArgs(pkg, d, i)
di[:subplot] = true
dumpdict(di, "Plot args (subplot $i)")
push!(plts, plot(pkg; di...))
end
# create the object and do the plotting
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout)
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout, d, false, false, false, (r,c) -> (nothing,nothing))
subplot!(subplt, args...; kw...)
subplt
end
doc"""
# ------------------------------------------------------------------------------------------------
# NOTE: for the subplot calls building from existing plots, we need the first plot to be separate to ensure dispatch calls this instead of the more general subplot(args...; kw...)
# grid layout
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
d = Dict(kw)
layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
subplot(vcat(plt1, plts...), layout, d)
end
# explicit layout
function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}...; kw...)
layout = subplotlayout(pltsPerRow)
subplot(vcat(plt1, plts...), layout, Dict(kw))
end
# this will be called internally
function subplot{P<:PlottingPackage}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::Dict)
validateSubplotSupported()
p = length(layout)
n = sum([plt.n for plt in plts])
subplt = Subplot(nothing, collect(plts), P(), p, n, layout, Dict(), false, false, false, (r,c) -> (nothing,nothing))
# preprocessArgs!(d)
# #
# for (i,plt) in enumerate(plts)
# di = copy(plt.initargs)
# for ck in (:background_color, :foreground_color, :color_palette)
# # if we have a value to override, do it
# if haskey(d, ck)
# di[ck] = get_mod(d[ck], i)
# end
# # build a new dict from the initargs of the plots
# iargs = Dict()
# for k in keys(_plotDefaults)
# iargs[k] = Any[plt.initargs[k] for plt in plts]'
# end
# merge!(iargs, d)
preprocessSubplot(subplt, d)
postprocessSubplot(subplt, d)
subplt
end
# TODO: hcat/vcat subplots and plots together arbitrarily
# ------------------------------------------------------------------------------------------------
function preprocessSubplot(subplt::Subplot, d::Dict)
validateSubplotSupported()
preprocessArgs!(d)
dumpdict(d, "After subplot! preprocessing")
# get the full initargs, overriding any new settings
# TODO: subplt.initargs should probably be merged sooner and actually used
# for color selection, etc. (i.e. if we overwrite the subplot palettes to [:heat :rainbow])
# then we need to overwrite plt[1].initargs[:color_palette] to :heat before it's actually used
# for color selection!
# first merge the new args into the subplot's initargs. then process the plot args and merge
# those into the plot's initargs. (example... `palette = [:blues :reds]` goes into subplt.initargs,
# then the ColorGradient for :blues/:reds is merged into plot 1/2 initargs, which is then used for color selection)
for i in 1:length(subplt.layout)
# di = getPlotArgs(backend(), subplt.initargs, i)
# merge!(subplt.plts[i].initargs, di)
subplt.plts[i].initargs = getPlotArgs(backend(), merge(subplt.plts[i].initargs, d), i)
end
merge!(subplt.initargs, d)
# process links. TODO: extract to separate function
for s in (:linkx, :linky, :linkfunc)
if haskey(d, s)
setfield!(subplt, s, d[s])
delete!(d, s)
end
end
end
function postprocessSubplot(subplt::Subplot, d::Dict)
# init (after plot creation)
if !subplt.initialized
subplt.initialized = buildSubplotObject!(subplt, false)
end
# add title, axis labels, ticks, etc
for (i,plt) in enumerate(subplt.plts)
# # # get the full initargs, overriding any new settings
# # di = copy(merge(plt.initargs, d))
# di = copy(d)
# for (k,v) in di
# if typeof(v) <: AVec
# di[k] = v[mod1(i, length(v))]
# elseif typeof(v) <: AMat
# m = size(v,2)
# di[k] = (size(v,1) == 1 ? v[1, mod1(i, m)] : v[:, mod1(i, m)])
# end
# end
# di = merge!(plt.initargs, di)
di = plt.initargs
dumpdict(di, "Updating sp $i")
updatePlotItems(plt, di)
end
updatePositionAndSize(subplt, d)
# handle links
subplt.linkx && linkAxis(subplt, true)
subplt.linky && linkAxis(subplt, false)
# set this to be current
current(subplt)
end
# ------------------------------------------------------------------------------------------------
"""
Adds to a subplot.
"""
# current subplot
function subplot!(args...; kw...)
subplot!(currentPlot(), args...; kw...)
validateSubplotSupported()
subplot!(current(), args...; kw...)
end
@@ -99,26 +332,87 @@ end
# # this adds to a specific subplot... most plot commands will flow through here
function subplot!(subplt::Subplot, args...; kw...)
kwList = createKWargsList(subplt, args...; kw...)
for (i,d) in enumerate(kwList)
subplt.n += 1
plt = getplot(subplt) # get the Plot object where this series will be drawn
plot!(plt; d...)
end
# validateSubplotSupported()
d = Dict(kw)
preprocessSubplot(subplt, d)
# create the underlying object (each backend will do this differently)
buildSubplotObject!(subplt.plotter, subplt)
# note: we call it once before doing the individual plots, and once after
# this is because some backends need to set up the subplots and then plot,
# and others need to do it the other way around
if !subplt.initialized
subplt.initialized = buildSubplotObject!(subplt, true)
end
# set this to be current
currentPlot!(subplt)
# handle grouping
group = get(d, :group, nothing)
if group == nothing
groupargs = []
else
groupargs = [extractGroupArgs(d[:group], args...)]
delete!(d, :group)
end
# do we want to show it?
d = Dict(kw)
kwList, xmeta, ymeta = createKWargsList(subplt, groupargs..., args...; d...)
# TODO: something useful with meta info?
for (i,di) in enumerate(kwList)
subplt.n += 1
plt = getplot(subplt)
plt.n += 1
# # update the plot's initargs for things such as palettes, etc
# for (k,v) in subplt.initargs
# haskey(_plotDefaults, k) || continue
# if typeof(v) <: AVec
# plt.initargs[k] = v[mod1(i, length(v))]
# elseif typeof(v) <: AMat
# m = size(v,2)
# plt.initargs[k] = (size(v,1) == 1 ? v[1, mod1(i, m)] : v[:, mod1(i, m)])
# end
# end
# cleanup the dictionary that we pass into the plot! command
di[:show] = false
di[:subplot] = true
for k in (:title, :xlabel, :xticks, :xlims, :xscale, :xflip,
:ylabel, :yticks, :ylims, :yscale, :yflip)
delete!(di, k)
end
dumpdict(di, "subplot! kwList $i")
dumpdict(plt.initargs, "plt.initargs before plotting")
_plot_from_subplot!(plt; di...)
end
postprocessSubplot(subplt, d)
# show it automatically?
if haskey(d, :show) && d[:show]
draw(subplt)
gui()
end
subplt
end
function _plot_from_subplot!(plt::Plot, args...; kw...)
d = Dict(kw)
setTicksFromStringVector(d, d, :x, :xticks)
setTicksFromStringVector(d, d, :y, :yticks)
# dumpdict(d, "Plot from subplot")
plot!(plt.backend, plt; d...)
addAnnotations(plt, d)
warnOnUnsupportedScales(plt.backend, d)
end
+176 -10
View File
@@ -2,27 +2,193 @@
typealias AVec AbstractVector
typealias AMat AbstractMatrix
abstract PlottingObject
immutable PlotsDisplay <: Display end
abstract PlottingPackage
abstract PlottingObject{T<:PlottingPackage}
type Plot <: PlottingObject
type Plot{T<:PlottingPackage} <: PlottingObject{T}
o # the underlying object
plotter::PlottingPackage
n::Int # number of series
backend::T
n::Int # number of series
# store these just in case
initargs::Dict
seriesargs::Vector{Dict} # args for each series
end
type SubplotLayout
abstract SubplotLayout
immutable GridLayout <: SubplotLayout
nr::Int
nc::Int
end
immutable FlexLayout <: SubplotLayout
numplts::Int
rowcounts::AbstractVector{Int}
end
type Subplot <: PlottingObject
type Subplot{T<:PlottingPackage, L<:SubplotLayout} <: PlottingObject{T}
o # the underlying object
plts::Vector{Plot} # the individual plots
plotter::PlottingPackage
plts::Vector{Plot{T}} # the individual plots
backend::T
p::Int # number of plots
n::Int # number of series
layout::SubplotLayout
end
layout::L
# initargs::Vector{Dict}
initargs::Dict
initialized::Bool
linkx::Bool
linky::Bool
linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y
end
# -----------------------------------------------------------------------
immutable Shape
vertices::AVec
end
"get an array of tuples of points on a circle with radius `r`"
function partialcircle(start_θ, end_θ, n = 20, r=1)
@compat(Tuple{Float64,Float64})[(r*cos(u),r*sin(u)) for u in linspace(start_θ, end_θ, n)]
end
"interleave 2 vectors into each other (like a zipper's teeth)"
function weave(x,y; ordering = Vector[x,y])
ret = eltype(x)[]
done = false
while !done
for o in ordering
try
push!(ret, shift!(o))
end
# try
# push!(ret, shift!(y))
# end
end
done = isempty(x) && isempty(y)
end
ret
end
"create a star by weaving together points from an outer and inner circle. `n` is the number of arms"
function makestar(n; offset = -0.5, radius = 1.0)
z1 = offset * π
z2 = z1 + π / (n)
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
Shape(weave(outercircle, innercircle)[1:end-2])
end
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
function makeshape(n; offset = -0.5, radius = 1.0)
z = offset * π
Shape(partialcircle(z, z + 2π, n+1, radius)[1:end-1])
end
function makecross(; offset = -0.5, radius = 1.0)
z2 = offset * π
z1 = z2 - π/8
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
Shape(weave(outercircle, innercircle,
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
end
const _shapes = @compat Dict(
:ellipse => makeshape(20),
:rect => makeshape(4, offset=-0.25),
:diamond => makeshape(4),
:utriangle => makeshape(3),
:dtriangle => makeshape(3, offset=0.5),
:pentagon => makeshape(5),
:hexagon => makeshape(6),
:heptagon => makeshape(7),
:octagon => makeshape(8),
:cross => makecross(offset=-0.25),
:xcross => makecross(),
)
for n in [4,5,6,7,8]
_shapes[symbol("star$n")] = makestar(n)
end
# -----------------------------------------------------------------------
immutable Font
family::AbstractString
pointsize::Int
halign::Symbol
valign::Symbol
rotation::Float64
color::Colorant
end
"Create a Font from a list of unordered features"
function font(args...)
# defaults
family = "Helvetica"
pointsize = 14
halign = :hcenter
valign = :vcenter
rotation = 0.0
color = colorant"black"
for arg in args
T = typeof(arg)
if arg == :center
halign = :hcenter
valign = :vcenter
elseif arg in (:hcenter, :left, :right)
halign = arg
elseif arg in (:vcenter, :top, :bottom)
valign = arg
elseif T <: Colorant
color = arg
elseif T <: @compat Union{Symbol,AbstractString}
try
color = parse(Colorant, string(arg))
catch
family = string(arg)
end
elseif typeof(arg) <: Integer
pointsize = arg
elseif typeof(arg) <: Real
rotation = convert(Float64, arg)
else
warn("Unused font arg: $arg ($(typeof(arg)))")
end
end
Font(family, pointsize, halign, valign, rotation, color)
end
"Wrap a string with font info"
immutable PlotText
str::@compat(AbstractString)
font::Font
end
function text(str, args...)
PlotText(string(str), font(args...))
end
# -----------------------------------------------------------------------
type OHLC{T<:Real}
open::T
high::T
low::T
close::T
end
+303 -9
View File
@@ -1,7 +1,7 @@
calcMidpoints(edges::AbstractVector) = Float64[0.5 * (edges[i] + edges[i+1]) for i in 1:length(edges)-1]
doc"Make histogram-like bins of data"
"Make histogram-like bins of data"
function binData(data, nbins)
lo, hi = extrema(data)
edges = collect(linspace(lo, hi, nbins+1))
@@ -14,7 +14,7 @@ function binData(data, nbins)
edges, midpoints, buckets, counts
end
doc"""
"""
A hacky replacement for a histogram when the backend doesn't support histograms directly.
Convert it into a bar chart with the appropriate x/y values.
"""
@@ -26,19 +26,19 @@ function histogramHack(; kw...)
d[:x] = midpoints
d[:y] = float(counts)
d[:linetype] = :bar
d[:fillto] = d[:fillto] == nothing ? 0.0 : d[:fillto]
d[:fillrange] = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
d
end
doc"""
"""
A hacky replacement for a bar graph when the backend doesn't support bars directly.
Convert it into a line chart with fillto set.
Convert it into a line chart with fillrange set.
"""
function barHack(; kw...)
d = Dict(kw)
midpoints = d[:x]
heights = d[:y]
fillto = d[:fillto] == nothing ? 0.0 : d[:fillto]
fillrange = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
# estimate the edges
dists = diff(midpoints) * 0.5
@@ -59,13 +59,307 @@ function barHack(; kw...)
for i in 1:length(heights)
e1, e2 = edges[i:i+1]
append!(x, [e1, e1, e2, e2])
append!(y, [fillto, heights[i], heights[i], fillto])
append!(y, [fillrange, heights[i], heights[i], fillrange])
end
d[:x] = x
d[:y] = y
d[:linetype] = :line
d[:fillto] = fillto
d[:linetype] = :path
d[:fillrange] = fillrange
d
end
"""
A hacky replacement for a sticks graph when the backend doesn't support sticks directly.
Convert it into a line chart that traces the sticks, and a scatter that sets markers at the points.
"""
function sticksHack(; kw...)
dLine = Dict(kw)
dScatter = copy(dLine)
# these are the line vertices
x = Float64[]
y = Float64[]
fillrange = dLine[:fillrange] == nothing ? 0.0 : dLine[:fillrange]
# calculate the vertices
yScatter = dScatter[:y]
for (i,xi) in enumerate(dScatter[: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[:linetype] = :path
dLine[:markershape] = :none
dLine[:fillrange] = nothing
# change the scatter args
dScatter[:linetype] = :none
dLine, dScatter
end
function regressionXY(x, y)
# regress
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
# make a line segment
regx = [minimum(x), maximum(x)]
regy = β * regx + α
regx, regy
end
# ------------------------------------------------------------------------------------
get_mod(v::AVec, idx::Int) = v[mod1(idx, length(v))]
get_mod(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
get_mod(v, idx::Int) = v
makevec(v::AVec) = v
makevec{T}(v::T) = T[v]
"duplicate a single value, or pass the 2-tuple through"
maketuple(x::Real) = (x,x)
maketuple{T,S}(x::@compat(Tuple{T,S})) = x
unzip{T,S}(v::AVec{@compat(Tuple{T,S})}) = [vi[1] for vi in v], [vi[2] for vi in v]
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function expandLimits!(lims, x)
try
e1, e2 = extrema(x)
lims[1] = min(lims[1], e1)
lims[2] = max(lims[2], e2)
# catch err
# warn(err)
end
nothing
end
# if the type exists in a list, replace the first occurence. otherwise add it to the end
function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
for (i,vi) in enumerate(v)
if isa(vi, t)
v[i] = t(args...; kw...)
return
end
end
push!(v, t(args...; kw...))
return
end
function replaceType(vec, val)
filter!(x -> !isa(x, typeof(val)), vec)
push!(vec, val)
end
function replaceAliases!(d::Dict, aliases::Dict)
ks = collect(keys(d))
# for (k,v) in d
for k in ks
if haskey(aliases, k)
d[aliases[k]] = d[k]
delete!(d, k)
end
end
end
createSegments(z) = collect(repmat(z',2,1))[2:end]
Base.first(c::Colorant) = c
sortedkeys(d::Dict) = sort(collect(keys(d)))
function fakedata(sz...)
y = zeros(sz...)
for r in 2:size(y,1)
y[r,:] = 0.95 * y[r-1,:] + randn(size(y,2))'
end
y
end
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
ticksType{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
ticksType(ticks) = :invalid
limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
limsType(lims) = :invalid
# ---------------------------------------------------------------
type DebugMode
on::Bool
end
const _debugMode = DebugMode(false)
function debugplots(on = true)
_debugMode.on = on
end
debugshow(x) = show(x)
debugshow(x::AbstractArray) = print(summary(x))
function dumpdict(d::Dict, prefix = "")
_debugMode.on || return
println()
if prefix != ""
println(prefix, ":")
end
for k in sort(collect(keys(d)))
@printf("%14s: ", k)
debugshow(d[k])
println()
end
println()
end
function dumpcallstack()
error() # well... you wanted the stacktrace, didn't you?!?
end
# ---------------------------------------------------------------
# push/append/clear/set the underlying plot data
# NOTE: backends should implement the getindex and setindex! methods to get/set the x/y data objects
# index versions
function Base.push!(plt::Plot, i::Integer, x::Real, y::Real)
xdata, ydata = plt[i]
plt[i] = (extendSeriesData(xdata, x), extendSeriesData(ydata, y))
plt
end
function Base.push!(plt::Plot, i::Integer, y::Real)
xdata, ydata = plt[i]
# if !isa(xdata, UnitRange)
# error("Expected x is a UnitRange since you're trying to push a y value only. typeof(x) = $(typeof(xdata))")
# end
plt[i] = (extendSeriesByOne(xdata), extendSeriesData(ydata, y))
plt
end
Base.push!(plt::Plot, y::Real) = push!(plt, 1, y)
# update all at once
function Base.push!(plt::Plot, x::AVec, y::AVec)
nx = length(x)
ny = length(y)
for i in 1:plt.n
push!(plt, i, x[mod1(i,nx)], y[mod1(i,ny)])
end
plt
end
function Base.push!(plt::Plot, x::Real, y::AVec)
push!(plt, [x], y)
end
function Base.push!(plt::Plot, y::AVec)
ny = length(y)
for i in 1:plt.n
push!(plt, i, y[mod1(i,ny)])
end
plt
end
# append to index
function Base.append!(plt::Plot, i::Integer, x::AVec, y::AVec)
@assert length(x) == length(y)
xdata, ydata = plt[i]
plt[i] = (extendSeriesData(xdata, x), extendSeriesData(ydata, y))
plt
end
function Base.append!(plt::Plot, i::Integer, y::AVec)
xdata, ydata = plt[i]
if !isa(xdata, UnitRange{Int})
error("Expected x is a UnitRange since you're trying to push a y value only")
end
plt[i] = (extendSeriesByOne(xdata, length(y)), extendSeriesData(ydata, y))
plt
end
# used in updating an existing series
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) + 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)
# ---------------------------------------------------------------
function supportGraph(allvals, func)
vals = reverse(sort(allvals))
bs = sort(backends())
x = ASCIIString[]
y = ASCIIString[]
for val in vals
for b in bs
supported = func(Plots.backendInstance(b))
if val in supported
push!(x, string(b))
push!(y, string(val))
end
end
end
n = length(vals)
scatter(x,y,
m=:rect,
ms=10,
size=(300,100+18*n),
# xticks=(collect(1:length(bs)), bs),
leg=false
)
end
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
supportGraphMarkers() = supportGraph(_allMarkers, supportedMarkers)
supportGraphScales() = supportGraph(_allScales, supportedScales)
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
function dumpSupportGraphs()
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
supportGraphMarkers, supportGraphScales, supportGraphAxes)
plt = func()
png(IMG_DIR * "/supported/$(string(func))")
end
end
# ---------------------------------------------------------------
# Some conversion functions
# note: I borrowed these conversion constants from Compose.jl's Measure
const INCH_SCALAR = 25.4
const PX_SCALAR = 1 / 3.78
inch2px(inches::Real) = float(inches * INCH_SCALAR / PX_SCALAR)
px2inch(px::Real) = float(px * PX_SCALAR / INCH_SCALAR)
inch2mm(inches::Real) = float(inches * INCH_SCALAR)
mm2inch(mm::Real) = float(mm / INCH_SCALAR)
px2mm(px::Real) = float(px * PX_SCALAR)
mm2px(mm::Real) = float(px / PX_SCALAR)
+9
View File
@@ -0,0 +1,9 @@
julia 0.3
Colors
Reexport
FactCheck
Gadfly
Images
ImageMagick
PyPlot
+182
View File
@@ -0,0 +1,182 @@
# include this first to help with crashing??
try
@eval using Gtk
end
# 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
end
# macro test_approx_eq_sigma_eps(A, B, sigma, eps)
include("../docs/example_generation.jl")
# # make and display one plot
# function test_examples(pkg::Symbol, idx::Int; debug = true)
# Plots._debugMode.on = debug
# println("Testing plot: $pkg:$idx:$(examples[idx].header)")
# backend(pkg)
# backend()
# map(eval, examples[idx].exprs)
# plt = current()
# gui(plt)
# plt
# end
using Plots, FactCheck
import Images, ImageMagick
# if !isdefined(ImageMagick, :init_deps)
# function ImageMagick.init_deps()
# ccall((:MagickWandGenesis,libwand), Void, ())
# end
# end
function makeImageWidget(fn)
img = Gtk.GtkImageLeaf(fn)
vbox = Gtk.GtkBoxLeaf(:v)
push!(vbox, Gtk.GtkLabelLeaf(fn))
push!(vbox, img)
show(img)
vbox
end
function replaceReferenceImage(tmpfn, reffn)
cmd = `cp $tmpfn $reffn`
run(cmd)
info("Replaced reference image with: $cmd")
end
"Show a Gtk popup with both images and a confirmation whether we should replace the new image with the old one"
function compareToReferenceImage(tmpfn, reffn)
@eval import Gtk
# add the images
imgbox = Gtk.GtkBoxLeaf(:h)
push!(imgbox, makeImageWidget(tmpfn))
push!(imgbox, makeImageWidget(reffn))
# add the buttons
# doNothingButton = Gtk.GtkButtonLeaf("Skip")
# replaceReferenceButton = Gtk.GtkButtonLeaf("Replace reference image")
# btnbox = Gtk.GtkButtonBoxLeaf(:h)
# push!(btnbox, doNothingButton)
# push!(btnbox, replaceReferenceButton)
# # create the window
# box = Gtk.GtkBoxLeaf(:v)
# push!(box, imgbox)
# push!(box, btnbox)
win = Gtk.GtkWindowLeaf("Should we make this the new reference image?")
push!(win, Gtk.GtkFrameLeaf(imgbox))
showall(win)
# now ask the question
if Gtk.ask_dialog("Should we make this the new reference image?", "No", "Yes")
replaceReferenceImage(tmpfn, reffn)
end
destroy(win)
# # we'll wait on this condition
# c = Condition()
# Gtk.on_signal_destroy((x...) -> notify(c), win)
# Gtk.signal_connect(replaceReferenceButton, "clicked") do widget
# replaceReferenceImage(tmpfn, reffn)
# notify(c)
# end
# Gtk.signal_connect(doNothingButton, "clicked") do widget
# notify(c)
# end
# # wait until a button is clicked, then close the window
# Gtk.showall(win)
# wait(c)
# Gtk.destroy(win)
end
# 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)
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, sigma = [1,1], eps = 1e-2)
# first
Plots._debugMode.on = debug
info("Testing plot: $pkg:$idx:$(PlotExamples.examples[idx].header)")
backend(pkg)
backend()
# ensure consistent results
srand(1234)
# run the example
map(eval, PlotExamples.examples[idx].exprs)
# save the png
tmpfn = tempname() * ".png"
png(tmpfn)
# load the saved png
tmpimg = Images.load(tmpfn)
# reference image location
# refdir = joinpath(Pkg.dir("Plots"), "test", "refimg", "v$(VERSION.major).$(VERSION.minor)", string(pkg))
refdir = joinpath(Pkg.dir("Plots"), "test", "refimg", string(pkg))
try
run(`mkdir -p $refdir`)
catch err
display(err)
end
reffn = joinpath(refdir, "ref$idx.png")
try
# info("Comparing $tmpfn to reference $reffn")
# load the reference image
refimg = Images.load(reffn)
# run the comparison test... a difference will throw an error
# NOTE: sigma is a 2-length vector with x/y values for the number of pixels
# to blur together when comparing images
diffpct = Images.test_approx_eq_sigma_eps(tmpimg, refimg, sigma, eps)
# we passed!
info("Reference image $reffn matches. Difference: $diffpct")
return true
catch err
warn("Image did not match reference image $reffn. err: $err")
showerror(Base.STDERR, err)
if isinteractive()
# if we're in interactive mode, open a popup and give us a chance to examine the images
warn("Should we make this the new reference image?")
compareToReferenceImage(tmpfn, reffn)
return
else
# if we rejected the image, or if we're in automated tests, throw the error
rethrow(err)
end
end
end
function image_comparison_tests(pkg::Symbol; skip = [], debug = false, sigma = [1,1], eps = 1e-2)
for i in 1:length(PlotExamples.examples)
i in skip && continue
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) --> true
end
end
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Some files were not shown because too many files have changed in this diff Show More