1 /// 2 module plot2d.plot; 3 4 import std.datetime.stopwatch; 5 6 import std.stdio; 7 8 import plot2d.drawable; 9 import plot2d.axis; 10 import plot2d.label; 11 import plot2d.style; 12 import plot2d.trtor; 13 import plot2d.chart; 14 import plot2d.util; 15 16 /// 17 class Plot 18 { 19 public: 20 /// 21 struct Settings 22 { 23 /// 24 Viewport viewport = {w: DimSeg(0, 1), h: DimSeg(0, 1)}; 25 26 /// use summury charts viewports 27 bool autoFit = true; 28 29 /// 30 Point minGridStep = Point(20,20); 31 32 /// from border to axis 33 Border margin = Border(60, 10, 40, 40); 34 35 /// from axis to charts 36 Border padding = Border(0, 10); 37 } 38 39 /// 40 Settings settings; 41 42 /// 43 Trtor tr; 44 /// namespaced style 45 NSStyle style; 46 47 /// 48 Axis axis; 49 /// 50 Grid grid; 51 /// 52 LBGridLabel label; 53 54 /// 55 Chart[] charts; 56 57 /// 58 this() 59 { 60 tr = new Trtor; 61 style = new NSStyle; 62 63 axis = new LBAxis(style); 64 grid = new Grid(style); 65 grid.dash = [5, 8]; 66 label = new LBGridLabel(style); 67 } 68 69 /// 70 T add(T)(T ch) if (is(T : Chart)) 71 { 72 charts ~= ch; 73 if (auto sch = cast(Stylized)ch) 74 sch.setRootStyle(style); 75 return ch; 76 } 77 78 /// 79 void updateCharts() 80 { 81 foreach (c; charts) 82 c.update(); 83 } 84 85 /// 86 void draw(Ctx cr, Point size) 87 { 88 if (recalculateTrtor(cr, size)) return; 89 drawElements(cr); 90 } 91 92 protected: 93 94 int recalculateTrtor(Ctx cr, Point sz) 95 { 96 auto vp = getViewport(); 97 98 tr.setSMPV(sz, settings.margin, settings.padding, vp); 99 100 Point lmgs; 101 { 102 mixin(scopeSave!cr); 103 lmgs = label.minGridStep(cr); 104 } 105 106 tr.optimizeGridStep( 107 Point(max(settings.minGridStep.x, lmgs.x * 1.25), 108 max(settings.minGridStep.y, lmgs.y * 1.25))); 109 110 // invert '<=' to '>' for nan-check 111 if (tr.gridStep.x > 0 && tr.gridStep.y > 0) { } 112 else 113 { 114 .warning("bad grid step: ", tr.gridStep); 115 return 1; 116 } 117 118 tr.correctGridOffset(); 119 return 0; 120 } 121 122 Viewport getViewport() 123 { 124 Viewport ret = settings.viewport; 125 126 if (charts.length == 0 || 127 !settings.autoFit) return ret; 128 129 bool s = false; // for setup first viewport 130 foreach (c; charts.filter!"a.visible") 131 { 132 if (s) ret.expand(c.viewport); 133 else { ret = c.viewport; s = true; } 134 } 135 136 double minViewDiff = double.epsilon * 1e3; 137 138 if (abs(ret.w.diff) < minViewDiff) 139 ret.w.stepExpand(minViewDiff); 140 141 if (abs(ret.h.diff) < minViewDiff) 142 ret.h.stepExpand(minViewDiff); 143 144 return ret; 145 } 146 147 void drawElements(Ctx cr) 148 { 149 foreach (p; chain(only(cast(Drawable)axis, 150 cast(Drawable)grid, 151 cast(Drawable)label), 152 charts.map!(a=>cast(Drawable)a))) 153 { 154 if (p is null) continue; 155 mixin(scopeSave!cr); 156 if (auto c = cast(Chart)p) 157 if (!c.visible) continue; 158 p.draw(cr, tr); 159 } 160 } 161 }