1 module app;
2
3 import dlangui;
4
5 mixin APP_ENTRY_POINT;
6
7 import dlangui.widgets.scroll;
8
9 class DragonView : ScrollWidget {
10
11 private {
12 int _scaleX;
13 int _scaleY;
14 int _middleX;
15 int _middleY;
16 int _dx;
17 int _dy;
18 int _x0;
19 int _y0;
20 int _length = 256;
21 int _dir0 = 0; // either 0 or 1
22 int _straightLen = 10;
23 int _roundLen = 4;
24 uint _bgcolor = 0x101010;
25 uint _grid1color = 0x303030;
26 uint _grid2color = 0x202020;
27 uint _grid3color = 0x181818;
28 uint _curve1color = 0x4050FF;
29 uint _curve2color = 0xFF4040;
30 uint _curve3color = 0x30C020;
31 uint _curve4color = 0xC000D0;
32 bool[4] _partVisible = [true, true, true, true];
33
34 bool _needRepaint = true;
35 Point[8] _directionVectors;
36 }
37
38
39 ColorDrawBuf _drawBuf;
40
41 this(string ID) {
42 super(ID);
43 fillParent();
44 //_fullScrollableArea.right = 2048;
45 //_fullScrollableArea.bottom = 2048;
46 setVectors();
47 resize(2048, 2048);
48 }
49
50 void resize(int dx, int dy) {
51 _dx = dx;
52 _dy = dy;
53 _fullScrollableArea.right = dx;
54 _fullScrollableArea.bottom = dy;
55 _visibleScrollableArea.left = dx / 2 - 400;
56 _visibleScrollableArea.top = dy / 2 - 300;
57 _visibleScrollableArea.right = _visibleScrollableArea.left + 400;
58 _visibleScrollableArea.bottom = _visibleScrollableArea.top + 400;
59
60 if (!_drawBuf) {
61 _drawBuf = new ColorDrawBuf(_fullScrollableArea.width, _fullScrollableArea.height);
62 } else {
63 _drawBuf.resize(dx, dy);
64 }
65 _middleX = _fullScrollableArea.width / 2;
66 _middleY = _fullScrollableArea.height / 2;
67 _needRepaint = true;
68 drawCurve();
69 }
70
71 void setVectors() {
72 setVectors(_straightLen, _roundLen);
73 }
74 void setVectors(int straightLen, int roundLen) {
75 if (!straightLen && !roundLen)
76 straightLen = 1;
77 setVectors([
78 Point(straightLen, 0),
79 Point(roundLen, -roundLen),
80 Point(0, -straightLen),
81 Point(-roundLen, -roundLen),
82 Point(-straightLen, 0),
83 Point(-roundLen, roundLen),
84 Point(0, straightLen),
85 Point(roundLen, roundLen),
86 ]);
87 }
88
89 void setVectors(Point[8] vectors) {
90 import std.math : abs;
91 _directionVectors = vectors;
92 int maxx1, maxx2, maxy1, maxy2;
93 for(int i = 0; i < 8; i += 2) {
94 if (maxx1 == 0 || maxx1 < abs(_directionVectors[i].x))
95 maxx1 = abs(_directionVectors[i].x);
96 if (maxy1 == 0 || maxy1 < abs(_directionVectors[i].y))
97 maxy1 = abs(_directionVectors[i].y);
98 if (maxx2 == 0 || maxx1 < abs(_directionVectors[i + 1].x))
99 maxx2 = abs(_directionVectors[i + 1].x);
100 if (maxy2 == 0 || maxy1 < abs(_directionVectors[i + 1].y))
101 maxy2 = abs(_directionVectors[i + 1].y);
102 }
103 if (_dir0 == 0) {
104 _scaleX = maxx1 + maxx2 * 2;
105 _scaleY = maxy1 + maxy2 * 2;
106 } else {
107 _scaleX = maxx1 + maxx2;
108 _scaleY = maxy1 + maxy2;
109 }
110 _x0 = vectors[1].x;
111 _y0 = vectors[1].y;
112 repaint();
113 }
114
115 bool getPartVisible(int n) {
116 return _partVisible[n & 3];
117 }
118 void setPartVisible(int n, bool flgVisible) {
119 n = n & 3;
120 if (_partVisible[n] != flgVisible) {
121 _partVisible[n] = flgVisible;
122 repaint();
123 }
124 }
125
126 @property int straightLen() {
127 return _straightLen;
128 }
129 @property DragonView straightLen(int n) {
130 if (_straightLen != n) {
131 _straightLen = n;
132 setVectors();
133 }
134 return this;
135 }
136 @property int roundLen() {
137 return _roundLen;
138 }
139 @property DragonView roundLen(int n) {
140 if (_roundLen != n) {
141 _roundLen = n;
142 setVectors();
143 }
144 return this;
145 }
146 @property int length() {
147 return _length;
148 }
149 @property DragonView length(int n) {
150 if (_length != n) {
151 _length = n;
152 repaint();
153 }
154 return this;
155 }
156 void repaint() {
157 _needRepaint = true;
158 invalidate();
159 }
160 @property int rotation() {
161 return _dir0;
162 }
163 @property DragonView rotation(int angle) {
164 if (_dir0 != (angle & 7)) {
165 _dir0 = angle & 7;
166 _needRepaint = true;
167 repaint();
168 }
169 return this;
170 }
171
172 /// Draw widget at its position to buffer
173 override void onDraw(DrawBuf buf) {
174 if (_needRepaint)
175 drawCurve();
176 super.onDraw(buf);
177 }
178
179 void drawLine(Point pt1, Point pt2, uint color) {
180 pt1.x += _middleX;
181 pt2.x += _middleX;
182 pt1.y = _middleY - pt1.y;
183 pt2.y = _middleY - pt2.y;
184 _drawBuf.drawLine(pt1, pt2, color);
185 }
186
187 void drawBackground() {
188 _drawBuf.fill(_bgcolor);
189 int i = 0;
190 for (int x = 0; x < _middleX; x += _scaleX) {
191 uint color = _scaleX > 2 ? _grid3color : COLOR_TRANSPARENT;
192 if (i == 0)
193 color = _grid1color;
194 else if ((i & 15) == 0)
195 color = _grid2color;
196 if (color != COLOR_TRANSPARENT) {
197 drawLine(Point(x, -_middleY), Point(x, _middleY), color);
198 drawLine(Point(-x, -_middleY), Point(-x, _middleY), color);
199 if (x == 0) {
200 drawLine(Point(x - 1, -_middleY), Point(x - 1, _middleY), color);
201 drawLine(Point(x + 1, -_middleY), Point(x + 1, _middleY), color);
202 }
203 }
204 i++;
205 }
206 i = 0;
207 for (int y = 0; y < _middleY; y += _scaleY) {
208 uint color = _scaleY > 2 ? _grid3color : COLOR_TRANSPARENT;
209 if (i == 0)
210 color = _grid1color;
211 else if ((i & 15) == 0)
212 color = _grid2color;
213 if (color != COLOR_TRANSPARENT) {
214 drawLine(Point(-_middleX, y), Point(_middleX, y), color);
215 drawLine(Point(-_middleX, -y), Point(_middleX, -y), color);
216 if (y == 0) {
217 drawLine(Point(-_middleX, y - 1), Point(_middleX, y - 1), color);
218 drawLine(Point(-_middleX, y + 1), Point(_middleX, y + 1), color);
219 }
220 }
221 i++;
222 }
223 }
224
225 int getDirectionDelta(int n) {
226 if (n == 0)
227 return -1;
228 for (int i = 0; i < 30; i++) {
229 if (n & (1 << i)) {
230 return (n & (2 << i)) ? 1 : -1;
231 }
232 }
233 return 0;
234 }
235
236 void drawSegment(ref Point currentPoint, ref int currentDir, int n, uint color, int mirror) {
237 int mx = _middleX + _scaleX * 2;
238 int my = _middleY + _scaleY * 2;
239 bool insideView = currentPoint.x >= -mx && currentPoint.x <= mx && currentPoint.y >= -my && currentPoint.y <= my;
240 int delta = getDirectionDelta(n) * mirror;
241 Point nextPoint = currentPoint + _directionVectors[currentDir];
242 if (insideView)
243 drawLine(currentPoint, nextPoint, color);
244 currentPoint = nextPoint;
245 currentDir = (currentDir + delta) & 7;
246 nextPoint = currentPoint + _directionVectors[currentDir];
247 if (insideView)
248 drawLine(currentPoint, nextPoint, color);
249 currentPoint = nextPoint;
250 currentDir = (currentDir + delta) & 7;
251 }
252
253 void drawLines() {
254 int dir;
255 Point p0;
256 Point pt;
257 if (_dir0 == 0)
258 p0 = Point(0, _directionVectors[_dir0 + 1].y);
259 else
260 p0 = Point(-_directionVectors[0].x / 2, _directionVectors[_dir0 + 1].y / 2);
261 // segment 1
262 if (_partVisible[0]) {
263 dir = 0 + _dir0;
264 pt = p0 - (_directionVectors[dir] + _directionVectors[dir + 1]);
265 for(int i = 0; i < _length; i++)
266 drawSegment(pt, dir, i, _curve1color, 1);
267 }
268 // segment 2
269 if (_partVisible[1]) {
270 dir = 4 + _dir0;
271 pt = p0 + _directionVectors[dir + 1];
272 for(int i = -1; i > -_length; i--)
273 drawSegment(pt, dir, i, _curve2color, -1);
274 }
275 // segment 3
276 if (_partVisible[2]) {
277 dir = 4 + _dir0;
278 pt = p0 - (_directionVectors[dir - 1] + _directionVectors[dir]);
279 for(int i = 0; i < _length; i++)
280 drawSegment(pt, dir, i, _curve3color, 1);
281 }
282 // segment 4
283 if (_partVisible[3]) {
284 dir = 0 + _dir0;
285 pt = p0 + _directionVectors[(dir - 1) & 7];
286 for(int i = -1; i > -_length; i--)
287 drawSegment(pt, dir, i, _curve4color, -1);
288 }
289 }
290 void drawCurve() {
291 drawBackground();
292 drawLines();
293 _needRepaint = false;
294 }
295
296 /// calculate full content size in pixels
297 override Point fullContentSize() {
298 Point sz = Point(_fullScrollableArea.width, _fullScrollableArea.height);
299 return sz;
300 }
301
302 override protected void drawClient(DrawBuf buf) {
303 Point sz = fullContentSize();
304 Point p = scrollPos;
305 //_contentWidget.layout(Rect(_clientRect.left - p.x, _clientRect.top - p.y, _clientRect.left + sz.x - p.x, _clientRect.top + sz.y - p.y));
306 //_contentWidget.onDraw(buf);
307 /// draw source buffer rectangle contents to destination buffer
308 buf.drawFragment(_clientRect.left, _clientRect.top, _drawBuf,
309 Rect(_visibleScrollableArea.left, _visibleScrollableArea.top,
310 _visibleScrollableArea.left + _clientRect.width, _visibleScrollableArea.top + _clientRect.height));
311 //Rect rc = _clientRect;
312 //rc.shrink(5, 5);
313 //buf.fillRect(rc, 0xFF8080);
314 }
315
316
317 }
318
319 immutable SLIDER_ACCELERATION_STEP = 64;
320
321 int sliderToSize(int n) {
322 int limit = 0;
323 int total = 0;
324 int factor = 1;
325 for(;;) {
326 int newlimit = limit + SLIDER_ACCELERATION_STEP;
327 if (n < newlimit) {
328 return total + (n - limit) * factor;
329 }
330 total += SLIDER_ACCELERATION_STEP * factor;
331 limit = newlimit;
332 factor *= 2;
333 }
334 }
335
336 int sizeToSlider(int n) {
337 int limit = 0;
338 int total = 0;
339 int factor = 1;
340 for(;;) {
341 int newlimit = limit + SLIDER_ACCELERATION_STEP;
342 int newtotal = total + SLIDER_ACCELERATION_STEP * factor;
343 if (n < newtotal) {
344 return limit + (n - total) / factor;
345 }
346 total = newtotal;
347 limit = newlimit;
348 factor *= 2;
349 }
350 }
351
352 /// entry point for dlangui based application
353 extern (C) int UIAppMain(string[] args) {
354
355 // create window
356 Window window = Platform.instance.createWindow("DlangUI example : Dragon Curve"d, null, WindowFlag.Resizable, 800, 600);
357 int n = sliderToSize(1000);
358 int n2 = sizeToSlider(n);
359
360 DragonView dragon = new DragonView("DRAGON_VIEW");
361
362 auto onScrollEvent = delegate(AbstractSlider source, ScrollEvent event) {
363 if (event.action == ScrollAction.SliderMoved) {
364 switch(source.id) {
365 case "straight":
366 dragon.straightLen = event.position;
367 break;
368 case "round":
369 dragon.roundLen = event.position;
370 break;
371 case "size":
372 dragon.length = sliderToSize(event.position);
373 break;
374 default:
375 break;
376 }
377 }
378 return true;
379 };
380
381 auto content = new VerticalLayout().fillParent;
382 auto controls1 = new HorizontalLayout().fillHorizontal.padding(3.pointsToPixels).backgroundColor(0xD8D8D8);
383
384 controls1.addChild(new TextWidget(null," Straight"d));
385 auto sliderStraight = new SliderWidget("straight");
386 sliderStraight.setRange(0, 20).position(dragon.straightLen).layoutWeight(1).fillHorizontal;
387 sliderStraight.scrollEvent = onScrollEvent;
388 controls1.addChild(sliderStraight);
389
390 controls1.addChild(new TextWidget(null," Rounding"d));
391 auto sliderRound = new SliderWidget("round");
392 sliderRound.setRange(0, 20).position(dragon.roundLen).layoutWeight(1).fillHorizontal;
393 sliderRound.scrollEvent = onScrollEvent;
394 controls1.addChild(sliderRound);
395
396 auto cbRotate = new CheckBox(null, " Rotate 45`"d);
397 controls1.addChild(cbRotate).checked(dragon.rotation ? true : false);
398 cbRotate.checkChange = delegate(Widget w, bool check) {
399 dragon.rotation(check ? 1 : 0); return true;
400 };
401 auto cbPartVisible0 = new CheckBox(null, " A1"d);
402 controls1.addChild(cbPartVisible0).checked(dragon.getPartVisible(0));
403 cbPartVisible0.checkChange = delegate(Widget w, bool check) {
404 dragon.setPartVisible(0, check); return true;
405 };
406 auto cbPartVisible1 = new CheckBox(null, " A2"d);
407 controls1.addChild(cbPartVisible1).checked(dragon.getPartVisible(1));
408 cbPartVisible1.checkChange = delegate(Widget w, bool check) {
409 dragon.setPartVisible(1, check); return true;
410 };
411 auto cbPartVisible2 = new CheckBox(null, " B1"d);
412 controls1.addChild(cbPartVisible2).checked(dragon.getPartVisible(2));
413 cbPartVisible2.checkChange = delegate(Widget w, bool check) {
414 dragon.setPartVisible(2, check); return true;
415 };
416 auto cbPartVisible3 = new CheckBox(null, " B2"d);
417 controls1.addChild(cbPartVisible3).checked(dragon.getPartVisible(3));
418 cbPartVisible3.checkChange = delegate(Widget w, bool check) {
419 dragon.setPartVisible(3, check); return true;
420 };
421
422 controls1.addChild(new TextWidget(null," Size"d));
423 auto sliderSize = new SliderWidget("size");
424 sliderSize.setRange(2, 1000).position(sizeToSlider(dragon.length)).layoutWeight(10).fillHorizontal;
425 sliderSize.scrollEvent = onScrollEvent;
426 controls1.addChild(sliderSize);
427
428 content.addChildren([controls1, dragon]);
429
430 window.mainWidget = content;
431
432 // show window
433 window.show();
434
435 // run message loop
436 return Platform.instance.enterMessageLoop();
437 }