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 }