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 }