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 }