1 ///
2 module plot2d.backend.dlangui;
3 
4 import std.exception : enforce;
5 import plot2d.backend.base;
6 
7 version (dlangui):
8 
9 import dlangui;
10 
11 alias PPoint = plot2d.Point;
12 alias PColor = plot2d.Color;
13 
14 alias UIPoint = dlangui.PointF;
15 alias UIPointI = dlangui.Point;
16 alias UIColor = dlangui.Color;
17 
18 import std.math : floor, ceil;
19 
20 int floori(double v) { return cast(int)floor(v); }
21 
22 UIPoint toUI()(auto ref const PPoint p)
23 { return UIPoint(p.x, p.y); }
24 
25 UIPointI toUII()(auto ref const PPoint p)
26 { return UIPointI(floori(p.x), floori(p.y)); }
27 
28 uint toUI()(auto ref const PColor c)
29 {
30     return (cast(uint)(c.r*255))<<16 |
31            (cast(uint)(c.g*255))<<8 |
32            (cast(uint)(c.b*255));
33 }
34 
35 import std.array;
36 import std.typecons : Tuple;
37 
38 ///
39 class DlangUICtx : Ctx
40 {
41 protected:
42     alias Line = Tuple!(PPoint, PPoint);
43 
44     static struct State
45     {
46         PPoint position;
47         Appender!(Line[]) lines;
48         float lineWidth = 1.0;
49         PColor color = PColor.red;
50         Rect clip;
51         string fontface;
52         double fontsize;
53     }
54 
55     State[] stateStack;
56 
57     ref State state() @property { return stateStack.back; }
58 
59     void setCurrentState()
60     {
61         buf.resetClipping();
62         buf.clipRect = state.clip;
63         buf.alpha = cast(uint)(state.color.a * 255);
64     }
65 
66     void reset()
67     {
68         stateStack.length = 1;
69         setCurrentState();
70     }
71 
72     struct StackReseter
73     {
74         DlangUICtx ctx;
75         this(DlangUICtx c) { ctx = c; }
76         ~this() { ctx.reset(); }
77     }
78 
79     DrawBuf buf;
80 
81     void drawLineSoft(PPoint a, PPoint b, int c)
82     { buf.drawLine(a.toUII, b.toUII, c); }
83 
84     void fillTriangleSoft(PPoint p0, PPoint p1, PPoint p2, int clr)
85     {
86         import std.algorithm : sort;
87         auto p = [p0, p1, p2].sort!"a.x < b.x";
88         
89         auto d1 = floori(p[1].x) - floori(p[0].x);
90         auto d2 = floori(p[2].x) - floori(p[1].x);
91         auto d3 = p[2].x - p[0].x;
92 
93         foreach (i; 0 .. d1+1)
94         {
95             auto fa = i/cast(float)d1;
96             auto a = (p[0] * (1-fa) + p[1] * fa);
97             auto fb = (a.x - p[0].x) / d3;
98             auto b = (p[0] * (1-fb) + p[2] * fb);
99             auto x = p[0].x + i;
100             drawLineSoft(PPoint(x, a.y), PPoint(x, b.y), clr);
101         }
102 
103         foreach (i; 0 .. d2+1)
104         {
105             auto fa = i/cast(float)d2;
106             auto a = (p[1] * (1-fa) + p[2] * fa);
107             auto fb = (a.x - p[0].x) / d3;
108             auto b = (p[0] * (1-fb) + p[2] * fb);
109             auto x = p[1].x + i;
110             drawLineSoft(PPoint(x, a.y), PPoint(x, b.y), clr);
111         }
112     }
113 
114 public:
115 
116     StackReseter set(DrawBuf buf)
117     {
118         this.buf = buf;
119         state.clip = buf.clipRect;
120         return StackReseter(this);
121     }
122 
123     this()
124     {
125         stateStack.length = 1;
126     }
127 
128 override:
129     void save() { stateStack ~= stateStack[$-1]; }
130 
131     void restore()
132     {
133         enforce(stateStack.length > 1, "no saved state");
134         stateStack.popBack();
135         setCurrentState();
136     }
137 
138     void stroke()
139     {
140         setCurrentState();
141         auto clr = state.color.toUI;
142         foreach (ln; state.lines.data)
143             //drawLineSoft(ln[0], ln[1], clr);
144             buf.drawLineF(ln[0].toUI, ln[1].toUI,
145                           state.lineWidth,
146                           state.color.toUI);
147         state.lines.clear();
148     }
149 
150     void fill()
151     {
152         if (state.lines.data.length == 0) return;
153         setCurrentState();
154         auto p0 = state.lines.data[0][0];
155         auto clr = state.color.toUI;
156         foreach (ln; state.lines.data[1..$])
157             //fillTriangleSoft(p0, ln[0], ln[1], clr);
158             buf.fillTriangleF(p0.toUI, ln[0].toUI,
159                              ln[1].toUI, clr);
160         state.lines.clear();
161     }
162 
163     void moveTo(double x, double y)
164     { state.position = PPoint(x,y); }
165 
166     void lineTo(double x, double y)
167     {
168         state.lines.put(Line(state.position, PPoint(x,y)));
169         moveTo(x, y);
170     }
171 
172     void setLineWidth(double lw) { state.lineWidth = lw; }
173 
174     void showText(string str)
175     {
176         // TODO
177         auto p = state.position;
178         Glyph g;
179         g.blackBoxX = 10;
180         g.blackBoxY = 15;
181         g.originX = 0;
182         g.originY = 0;
183         g.width = 10;
184         auto hh = 15;
185         g.glyph.length = g.width * hh;
186         foreach (i; 0..hh) foreach (j; 0..g.width)
187         {
188             if (i == j || g.width - i == j)
189                 g.glyph[i*g.width + j] = 1;
190         }
191         foreach (c; str)
192             buf.drawGlyph(cast(int)p.x, cast(int)p.y, &g,
193                             UIColor.red);
194     }
195 
196     void setDash(double[] dash, double offset)
197     {
198         // TODO
199     }
200 
201     void setColor(double r, double g, double b, double a=1)
202     { state.color = PColor(r,g,b,a); }
203 
204     void getTextSize(string str, out double w, out double h)
205     {
206         // TODO
207         w = str.length * 0.7 * 15;
208         h = 15;
209     }
210 
211     void setFont(string name, double size)
212     {
213         state.fontface = name;
214         state.fontsize = size;
215     }
216 
217     void clipViewport(Viewport vp)
218     {
219         state.clip = Rect(cast(int)vp.w.min,
220                           cast(int)vp.h.min,
221                           cast(int)vp.w.max,
222                           cast(int)vp.h.max);
223         buf.clipRect = state.clip;
224         state.clip = buf.clipRect;
225     }
226 }