1 /// 2 module plot2d.trtor; 3 4 import plot2d.types; 5 import plot2d.util; 6 7 /// 8 class Trtor 9 { 10 protected: 11 Point _size, _scale, _offset, _gridStep, _gridOffset; 12 Viewport _viewport; 13 Border _margin, _padding; 14 15 public: 16 /// 17 this(){} 18 19 @property 20 { 21 const 22 { 23 /// drawing area size 24 ref const(Point) size() { return _size; } 25 /// chart margin (axis offset) 26 ref const(Border) margin() { return _margin; } 27 /// 28 Viewport inMargin() 29 { 30 return Viewport( 31 DimSeg(_margin.left, _size.x - _margin.right), 32 DimSeg(_margin.top, _size.y - _margin.bottom) 33 ); 34 } 35 /// 36 Viewport inPadding() 37 { 38 return Viewport( 39 DimSeg(_margin.left + _padding.left, 40 _size.x - _margin.right - _padding.right), 41 DimSeg(_margin.top + _padding.top, 42 _size.y - _margin.bottom - _padding.bottom) 43 ); 44 } 45 46 /// chart padding inside axis 47 ref const(Border) padding() { return _padding; } 48 /// limits of displayed values of data 49 ref const(Viewport) viewport() { return _viewport; } 50 /// transform coeficient 51 ref const(Point) scale() { return _scale; } 52 /// ditto 53 ref const(Point) offset() { return _offset; } 54 /// 55 ref const(Point) gridStep() { return _gridStep; } 56 /// 57 ref const(Point) gridOffset() { return _gridOffset; } 58 } 59 60 /// 61 void size(Point s) { _size = s; recalc(); } 62 63 /// 64 void margin(Border m) { _margin = m; recalc(); } 65 66 /// 67 void padding(Border p) { _padding = p; recalc(); } 68 69 /// 70 void viewport(Viewport v) { _viewport = v; recalc(); } 71 72 /// 73 void gridStep(Point s) { _gridStep = s; } 74 75 /// 76 void gridOffset(Point s) { _gridOffset = s; } 77 } 78 79 /// 80 void setSMPV(Point size, Border margin, 81 Border padding, Viewport viewport) 82 { 83 _size = size; 84 _margin = margin; 85 _padding = padding; 86 _viewport = viewport; 87 recalc(); 88 } 89 90 /// 91 void recalc() 92 { 93 _scale = calcScale(_viewport); 94 recalcOffset(); // scale used 95 } 96 97 /// 98 Point calcScale()(auto ref const Viewport v) 99 { 100 alias m = _margin; 101 alias p = _padding; 102 return Point( 103 (_size.x - m.sx - p.sx) / v.w.diff, 104 (-_size.y + m.sy + p.sy) / v.h.diff); 105 // ^-- drawing area has downside Y direction 106 } 107 108 /// 109 void recalcOffset() 110 { 111 alias m = _margin; 112 alias p = _padding; 113 alias s = _size; 114 alias v = _viewport; 115 _offset = Point(m.left + p.left, 116 s.y - m.bottom - p.bottom) 117 - Point(v.w.min, v.h.min) * _scale; 118 } 119 120 /// 121 void correctGridOffset() 122 { 123 auto im = inMargin; 124 auto orig = Point(im.w.min, im.h.max); 125 auto p0 = toCh(orig); 126 auto chgs = _gridStep / _scale; 127 p0 = Point(p0.x.quantize!ceil(chgs.x), 128 p0.y.quantize!ceil(-chgs.y)); 129 auto r = toDA(p0) - orig; 130 _gridOffset = Point(r.x, -r.y); 131 } 132 133 /// 134 void optimizeGridStep(Point minGridCellSize) 135 { 136 double cs(double m, double delegate(double) stepper) 137 { 138 auto mv = max(abs(m), double.epsilon * 1e2); 139 double rs; 140 if (stepper is null) 141 rs = roundStepFunc!10(mv); 142 else 143 rs = stepper(mv); 144 return mv.quantize!ceil(rs); 145 } 146 147 auto m = minGridCellSize / _scale; 148 auto r = Point(cs(m.x, calcXGridStep), 149 cs(m.y, calcYGridStep)) * _scale; 150 _gridStep = Point(abs(r.x), abs(r.y)); 151 } 152 153 /// 154 double delegate(double) calcXGridStep, calcYGridStep; 155 156 import std.traits : isNumeric; 157 158 /// 159 static double roundStepFunc(alias bais)(double val) nothrow @nogc 160 if (isNumeric!(typeof(bais))) 161 { 162 alias mlog = std.math.log; 163 enum z = mlog(bais); 164 return bais ^^ floor(mlog(val) / z); 165 } 166 167 const pure nothrow @nogc @safe 168 { 169 /// transfrom coord from chart to drawing area 170 Point toDA(double x, double y) { return toDA(Point(x,y)); } 171 /// ditto 172 Point toDA(Point p) { return p * _scale + _offset; } 173 /// ditto 174 double toDAX(double x) { return x * _scale.x + _offset.x; } 175 /// ditto 176 double toDAY(double y) { return y * _scale.y + _offset.y; } 177 178 /// transform coord from drawing area to chart 179 Point toCh(double x, double y) { return toCh(Point(x,y)); } 180 /// ditto 181 Point toCh(Point p) { return (p - _offset) / _scale; } 182 /// ditto 183 double toChX(double x) { return (x - _offset.x) / _scale.x; } 184 /// ditto 185 double toChY(double y) { return (y - _offset.y) / _scale.y; } 186 } 187 }