dlangui/examples/dragon/src/dragon.d

438 lines
14 KiB
D

module app;
import dlangui;
mixin APP_ENTRY_POINT;
import dlangui.widgets.scroll;
class DragonView : ScrollWidget {
private {
int _scaleX;
int _scaleY;
int _middleX;
int _middleY;
int _dx;
int _dy;
int _x0;
int _y0;
int _length = 256;
int _dir0 = 0; // either 0 or 1
int _straightLen = 10;
int _roundLen = 4;
uint _bgcolor = 0x101010;
uint _grid1color = 0x303030;
uint _grid2color = 0x202020;
uint _grid3color = 0x181818;
uint _curve1color = 0x4050FF;
uint _curve2color = 0xFF4040;
uint _curve3color = 0x30C020;
uint _curve4color = 0xC000D0;
bool[4] _partVisible = [true, true, true, true];
bool _needRepaint = true;
Point[8] _directionVectors;
}
ColorDrawBuf _drawBuf;
this(string ID) {
super(ID);
fillParent();
//_fullScrollableArea.right = 2048;
//_fullScrollableArea.bottom = 2048;
setVectors();
resize(2048, 2048);
}
void resize(int dx, int dy) {
_dx = dx;
_dy = dy;
_fullScrollableArea.right = dx;
_fullScrollableArea.bottom = dy;
_visibleScrollableArea.left = dx / 2 - 400;
_visibleScrollableArea.top = dy / 2 - 300;
_visibleScrollableArea.right = _visibleScrollableArea.left + 400;
_visibleScrollableArea.bottom = _visibleScrollableArea.top + 400;
if (!_drawBuf) {
_drawBuf = new ColorDrawBuf(_fullScrollableArea.width, _fullScrollableArea.height);
} else {
_drawBuf.resize(dx, dy);
}
_middleX = _fullScrollableArea.width / 2;
_middleY = _fullScrollableArea.height / 2;
_needRepaint = true;
drawCurve();
}
void setVectors() {
setVectors(_straightLen, _roundLen);
}
void setVectors(int straightLen, int roundLen) {
if (!straightLen && !roundLen)
straightLen = 1;
setVectors([
Point(straightLen, 0),
Point(roundLen, -roundLen),
Point(0, -straightLen),
Point(-roundLen, -roundLen),
Point(-straightLen, 0),
Point(-roundLen, roundLen),
Point(0, straightLen),
Point(roundLen, roundLen),
]);
}
void setVectors(Point[8] vectors) {
import std.math : abs;
_directionVectors = vectors;
int maxx1, maxx2, maxy1, maxy2;
for(int i = 0; i < 8; i += 2) {
if (maxx1 == 0 || maxx1 < abs(_directionVectors[i].x))
maxx1 = abs(_directionVectors[i].x);
if (maxy1 == 0 || maxy1 < abs(_directionVectors[i].y))
maxy1 = abs(_directionVectors[i].y);
if (maxx2 == 0 || maxx1 < abs(_directionVectors[i + 1].x))
maxx2 = abs(_directionVectors[i + 1].x);
if (maxy2 == 0 || maxy1 < abs(_directionVectors[i + 1].y))
maxy2 = abs(_directionVectors[i + 1].y);
}
if (_dir0 == 0) {
_scaleX = maxx1 + maxx2 * 2;
_scaleY = maxy1 + maxy2 * 2;
} else {
_scaleX = maxx1 + maxx2;
_scaleY = maxy1 + maxy2;
}
_x0 = vectors[1].x;
_y0 = vectors[1].y;
repaint();
}
bool getPartVisible(int n) {
return _partVisible[n & 3];
}
void setPartVisible(int n, bool flgVisible) {
n = n & 3;
if (_partVisible[n] != flgVisible) {
_partVisible[n] = flgVisible;
repaint();
}
}
@property int straightLen() {
return _straightLen;
}
@property DragonView straightLen(int n) {
if (_straightLen != n) {
_straightLen = n;
setVectors();
}
return this;
}
@property int roundLen() {
return _roundLen;
}
@property DragonView roundLen(int n) {
if (_roundLen != n) {
_roundLen = n;
setVectors();
}
return this;
}
@property int length() {
return _length;
}
@property DragonView length(int n) {
if (_length != n) {
_length = n;
repaint();
}
return this;
}
void repaint() {
_needRepaint = true;
invalidate();
}
@property int rotation() {
return _dir0;
}
@property DragonView rotation(int angle) {
if (_dir0 != (angle & 7)) {
_dir0 = angle & 7;
_needRepaint = true;
repaint();
}
return this;
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (_needRepaint)
drawCurve();
super.onDraw(buf);
}
void drawLine(Point pt1, Point pt2, uint color) {
pt1.x += _middleX;
pt2.x += _middleX;
pt1.y = _middleY - pt1.y;
pt2.y = _middleY - pt2.y;
_drawBuf.drawLine(pt1, pt2, color);
}
void drawBackground() {
_drawBuf.fill(_bgcolor);
int i = 0;
for (int x = 0; x < _middleX; x += _scaleX) {
uint color = _scaleX > 2 ? _grid3color : COLOR_TRANSPARENT;
if (i == 0)
color = _grid1color;
else if ((i & 15) == 0)
color = _grid2color;
if (color != COLOR_TRANSPARENT) {
drawLine(Point(x, -_middleY), Point(x, _middleY), color);
drawLine(Point(-x, -_middleY), Point(-x, _middleY), color);
if (x == 0) {
drawLine(Point(x - 1, -_middleY), Point(x - 1, _middleY), color);
drawLine(Point(x + 1, -_middleY), Point(x + 1, _middleY), color);
}
}
i++;
}
i = 0;
for (int y = 0; y < _middleY; y += _scaleY) {
uint color = _scaleY > 2 ? _grid3color : COLOR_TRANSPARENT;
if (i == 0)
color = _grid1color;
else if ((i & 15) == 0)
color = _grid2color;
if (color != COLOR_TRANSPARENT) {
drawLine(Point(-_middleX, y), Point(_middleX, y), color);
drawLine(Point(-_middleX, -y), Point(_middleX, -y), color);
if (y == 0) {
drawLine(Point(-_middleX, y - 1), Point(_middleX, y - 1), color);
drawLine(Point(-_middleX, y + 1), Point(_middleX, y + 1), color);
}
}
i++;
}
}
int getDirectionDelta(int n) {
if (n == 0)
return -1;
for (int i = 0; i < 30; i++) {
if (n & (1 << i)) {
return (n & (2 << i)) ? 1 : -1;
}
}
return 0;
}
void drawSegment(ref Point currentPoint, ref int currentDir, int n, uint color, int mirror) {
int mx = _middleX + _scaleX * 2;
int my = _middleY + _scaleY * 2;
bool insideView = currentPoint.x >= -mx && currentPoint.x <= mx && currentPoint.y >= -my && currentPoint.y <= my;
int delta = getDirectionDelta(n) * mirror;
Point nextPoint = currentPoint + _directionVectors[currentDir];
if (insideView)
drawLine(currentPoint, nextPoint, color);
currentPoint = nextPoint;
currentDir = (currentDir + delta) & 7;
nextPoint = currentPoint + _directionVectors[currentDir];
if (insideView)
drawLine(currentPoint, nextPoint, color);
currentPoint = nextPoint;
currentDir = (currentDir + delta) & 7;
}
void drawLines() {
int dir;
Point p0;
Point pt;
if (_dir0 == 0)
p0 = Point(0, _directionVectors[_dir0 + 1].y);
else
p0 = Point(-_directionVectors[0].x / 2, _directionVectors[_dir0 + 1].y / 2);
// segment 1
if (_partVisible[0]) {
dir = 0 + _dir0;
pt = p0 - (_directionVectors[dir] + _directionVectors[dir + 1]);
for(int i = 0; i < _length; i++)
drawSegment(pt, dir, i, _curve1color, 1);
}
// segment 2
if (_partVisible[1]) {
dir = 4 + _dir0;
pt = p0 + _directionVectors[dir + 1];
for(int i = -1; i > -_length; i--)
drawSegment(pt, dir, i, _curve2color, -1);
}
// segment 3
if (_partVisible[2]) {
dir = 4 + _dir0;
pt = p0 - (_directionVectors[dir - 1] + _directionVectors[dir]);
for(int i = 0; i < _length; i++)
drawSegment(pt, dir, i, _curve3color, 1);
}
// segment 4
if (_partVisible[3]) {
dir = 0 + _dir0;
pt = p0 + _directionVectors[(dir - 1) & 7];
for(int i = -1; i > -_length; i--)
drawSegment(pt, dir, i, _curve4color, -1);
}
}
void drawCurve() {
drawBackground();
drawLines();
_needRepaint = false;
}
/// calculate full content size in pixels
override Point fullContentSize() {
Point sz = Point(_fullScrollableArea.width, _fullScrollableArea.height);
return sz;
}
override protected void drawClient(DrawBuf buf) {
Point sz = fullContentSize();
Point p = scrollPos;
//_contentWidget.layout(Rect(_clientRect.left - p.x, _clientRect.top - p.y, _clientRect.left + sz.x - p.x, _clientRect.top + sz.y - p.y));
//_contentWidget.onDraw(buf);
/// draw source buffer rectangle contents to destination buffer
buf.drawFragment(_clientRect.left, _clientRect.top, _drawBuf,
Rect(_visibleScrollableArea.left, _visibleScrollableArea.top,
_visibleScrollableArea.left + _clientRect.width, _visibleScrollableArea.top + _clientRect.height));
//Rect rc = _clientRect;
//rc.shrink(5, 5);
//buf.fillRect(rc, 0xFF8080);
}
}
immutable SLIDER_ACCELERATION_STEP = 64;
int sliderToSize(int n) {
int limit = 0;
int total = 0;
int factor = 1;
for(;;) {
int newlimit = limit + SLIDER_ACCELERATION_STEP;
if (n < newlimit) {
return total + (n - limit) * factor;
}
total += SLIDER_ACCELERATION_STEP * factor;
limit = newlimit;
factor *= 2;
}
}
int sizeToSlider(int n) {
int limit = 0;
int total = 0;
int factor = 1;
for(;;) {
int newlimit = limit + SLIDER_ACCELERATION_STEP;
int newtotal = total + SLIDER_ACCELERATION_STEP * factor;
if (n < newtotal) {
return limit + (n - total) / factor;
}
total = newtotal;
limit = newlimit;
factor *= 2;
}
}
/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
// create window
Window window = Platform.instance.createWindow("DlangUI example : Dragon Curve"d, null, WindowFlag.Resizable, 800, 600);
int n = sliderToSize(1000);
int n2 = sizeToSlider(n);
DragonView dragon = new DragonView("DRAGON_VIEW");
auto onScrollEvent = delegate(AbstractSlider source, ScrollEvent event) {
if (event.action == ScrollAction.SliderMoved) {
switch(source.id) {
case "straight":
dragon.straightLen = event.position;
break;
case "round":
dragon.roundLen = event.position;
break;
case "size":
dragon.length = sliderToSize(event.position);
break;
default:
break;
}
}
return true;
};
auto content = new VerticalLayout().fillParent;
auto controls1 = new HorizontalLayout().fillHorizontal.padding(3.pointsToPixels).backgroundColor(0xD8D8D8);
controls1.addChild(new TextWidget(null," Straight"d));
auto sliderStraight = new SliderWidget("straight");
sliderStraight.setRange(0, 20).position(dragon.straightLen).layoutWeight(1).fillHorizontal;
sliderStraight.scrollEvent = onScrollEvent;
controls1.addChild(sliderStraight);
controls1.addChild(new TextWidget(null," Rounding"d));
auto sliderRound = new SliderWidget("round");
sliderRound.setRange(0, 20).position(dragon.roundLen).layoutWeight(1).fillHorizontal;
sliderRound.scrollEvent = onScrollEvent;
controls1.addChild(sliderRound);
auto cbRotate = new CheckBox(null, " Rotate 45`"d);
controls1.addChild(cbRotate).checked(dragon.rotation ? true : false);
cbRotate.checkChange = delegate(Widget w, bool check) {
dragon.rotation(check ? 1 : 0); return true;
};
auto cbPartVisible0 = new CheckBox(null, " A1"d);
controls1.addChild(cbPartVisible0).checked(dragon.getPartVisible(0));
cbPartVisible0.checkChange = delegate(Widget w, bool check) {
dragon.setPartVisible(0, check); return true;
};
auto cbPartVisible1 = new CheckBox(null, " A2"d);
controls1.addChild(cbPartVisible1).checked(dragon.getPartVisible(1));
cbPartVisible1.checkChange = delegate(Widget w, bool check) {
dragon.setPartVisible(1, check); return true;
};
auto cbPartVisible2 = new CheckBox(null, " B1"d);
controls1.addChild(cbPartVisible2).checked(dragon.getPartVisible(2));
cbPartVisible2.checkChange = delegate(Widget w, bool check) {
dragon.setPartVisible(2, check); return true;
};
auto cbPartVisible3 = new CheckBox(null, " B2"d);
controls1.addChild(cbPartVisible3).checked(dragon.getPartVisible(3));
cbPartVisible3.checkChange = delegate(Widget w, bool check) {
dragon.setPartVisible(3, check); return true;
};
controls1.addChild(new TextWidget(null," Size"d));
auto sliderSize = new SliderWidget("size");
sliderSize.setRange(2, 1000).position(sizeToSlider(dragon.length)).layoutWeight(10).fillHorizontal;
sliderSize.scrollEvent = onScrollEvent;
controls1.addChild(sliderSize);
content.addChildren([controls1, dragon]);
window.mainWidget = content;
// show window
window.show();
// run message loop
return Platform.instance.enterMessageLoop();
}