1 module rip.draw.turtle;
2 
3 private
4 {
5 	import std.math;
6 	import std.random;
7 
8 	import rip.concepts;
9 	import rip.draw.primitives : drawDDALine;
10 }
11 
12 // состояние черепахи
13 class TurtleState
14 {
15 	private
16 	{
17 		float x;
18 		float y;
19 		float angle;
20 	}
21 
22 	this(T, U, V)(T x, U y, V angle)
23 		if (allArithmetic!(T, U, V))
24 	{
25 		this.x = cast(float) x;
26 		this.y = cast(float) y;
27 		this.angle = cast(float) angle;
28 	}
29 
30 	mixin(addTypedGetter!("x", "getX"));
31 	
32 	mixin(addTypedGetter!("y", "getY"));
33 
34 	mixin(addTypedGetter!("angle", "getAngle"));
35 
36 	void setX(T)(T x)
37 		if (allArithmetic!T)
38 	{
39 		this.x = cast(float) x;
40 	}
41 
42 	void setY(T)(T y)
43 		if (allArithmetic!T)
44 	{
45 		this.y = cast(float) y;
46 	}
47 	
48 	void setAngle(T)(T angle)
49 		if (allArithmetic!T)
50 	{
51 		this.angle = cast(float) angle;
52 	}
53 }
54 
55 
56 // И теперь в наших руках появляется мощное оружие - исполнитель "черепаха"... 
57 class Turtle
58 {
59 	private
60 	{
61 		Surface surface;
62 		RGBColor color;
63 
64 		TurtleState[] stateStack;
65 		TurtleState state;
66 
67 		float stepIncrement;
68 		float angleIncrement;
69 	}
70 
71 	this(T, U)(Surface surface, RGBColor color, TurtleState state, T stepIncrement, U angleIncrement)
72 		if (allArithmetic!(T, U))
73 	{
74 		this.surface = surface;
75 		this.color = color;
76 		this.state = state;
77 		this.stepIncrement = cast(float) stepIncrement;
78 		this.angleIncrement = cast(float) angleIncrement;
79 	}
80 
81 	// шаг вперед с отрисовкой следа
82 	TurtleState drawStep()
83 	{
84 		float newX, newY;
85 
86 		newX = state.getX!float + cos(state.getAngle!float) * stepIncrement;
87 		newY = state.getY!float - sin(state.getAngle!float) * stepIncrement;
88 
89 		surface.drawDDALine(color, 
90 			state.getX!float, 
91 			state.getY!float,
92 			newX,
93 			newY
94 			);
95 
96 		state.setX(newX);
97 		state.setY(newY);
98 
99 		return state;
100 	}
101 
102 	// шаг вперед без отрисовки следа
103 	TurtleState moveStep()
104 	{
105 		float newX, newY;
106 		
107 		newX = state.getX!float + cos(state.getAngle!float) * stepIncrement;
108 		newY = state.getY!float - sin(state.getAngle!float) * stepIncrement;
109 
110 		state.setX(newX);
111 		state.setY(newY);
112 		
113 		return state;
114 	}
115 
116 	// поворот влево
117 	TurtleState rotateLeft()
118 	{
119 		float newAngle;
120 
121 		newAngle = state.getAngle!float + angleIncrement;
122 
123 		state.setAngle(newAngle);
124 
125 		return state;
126 	}
127 
128 	// поворот вправо
129 	TurtleState rotateRight()
130 	{
131 		float newAngle;
132 		
133 		newAngle = state.getAngle!float - angleIncrement;
134 		
135 		state.setAngle(newAngle);
136 		
137 		return state;
138 	}
139 
140 	// поворот на случайный угол
141 	TurtleState rotateRandom()
142 	{
143 		float newAngle;
144 
145 		auto rndGenerator = new Random(unpredictableSeed);
146 		newAngle = uniform(-2 * PI, 2 * PI, rndGenerator);
147 		
148 		state.setAngle(newAngle);
149 		
150 		return state;
151 	}
152 
153 	// сохранить состояние черепахи
154 	TurtleState saveState()
155 	{
156 		stateStack ~= state;
157 
158 		return state;
159 	}
160 	
161 	// восстановить состояние черепахи
162 	TurtleState restoreState()
163 	{
164 		state = stateStack[$-1];
165 		stateStack = stateStack[0 .. $-1];
166 
167 		return state;
168 	}
169 
170 	// выполнить команду с помощью черепахи
171 	TurtleState execute(string s)
172 	{
173 		TurtleState currentState;
174 		for (int i = 0; i < s.length; i++)
175 		{
176 			switch(s[i])
177 			{
178 				case 'F':
179 					currentState = drawStep();
180 					break;
181 				case 'f':
182 					currentState = moveStep();
183 					break;
184 				case '+':
185 					currentState = rotateRight();
186 					break;
187 				case '-':
188 					currentState = rotateLeft();
189 					break;
190 				case '?':
191 					currentState = rotateRandom();
192 					break;
193 				case '[':
194 					currentState = saveState();
195 					break;
196 				case ']':
197 					currentState = restoreState();
198 					break;
199 				default:
200 					break;
201 			}
202 		}
203 
204 		return currentState;
205 	}
206 }