001/* 002 * Renderer Models. The MIT License. 003 * Copyright (c) 2022 rlkraft@pnw.edu 004 * See LICENSE for details. 005*/ 006 007package renderer.models_L.turtlegraphics; 008 009import renderer.scene.*; 010import renderer.scene.primitives.*; 011 012/** 013 https://www.clear.rice.edu/comp360/lectures/K10188_C001.pdf 014*/ 015public class Turtle 016{ 017 public final Model model; 018 public final String name; 019 public final double z; 020 public final double xHome; 021 public final double yHome; 022 private double xPos; 023 private double yPos; 024 private double heading; 025 private boolean penDown; 026 private double stepSize; // see the resize() method 027 028 /** 029 @param model a reference to the {link Model} that this {@code Turtle} is builing 030 @throws NullPointerException if {@code model} is {@code null} 031 */ 032 public Turtle(final Model model) 033 { 034 this(model, model.name, 0, 0, 0); 035 } 036 037 038 /** 039 @param model a reference to the {@link Model} that this {@code Turtle} is builing 040 @param name a {@link String} that is a name for this {@code Turtle} 041 @throws NullPointerException if {@code model} is {@code null} 042 @throws NullPointerException if {@code name} is {@code null} 043 */ 044 public Turtle(final Model model, final String name) 045 { 046 this(model, name, 0, 0, 0); 047 } 048 049 050 /** 051 @param model a reference to the {@link Model} that this {@code Turtle} is builing 052 @param z the z-plane for this {@code Turtle} 053 @throws NullPointerException if {@code model} is {@code null} 054 */ 055 public Turtle(final Model model, final double z) 056 { 057 this(model, model.name, 0, 0, z); 058 } 059 060 061 /** 062 @param model a reference to the {@link Model} that this {@code Turtle} is builing 063 @param name a {@link String} that is a name for this {@code Turtle} 064 @param z the z-plane for this {@code Turtle} 065 @throws NullPointerException if {@code model} is {@code null} 066 @throws NullPointerException if {@code name} is {@code null} 067 */ 068 public Turtle(final Model model, final String name, final double z) 069 { 070 this(model, name, 0, 0, z); 071 } 072 073 074 /** 075 @param model a reference to the {@link Model} that this {@code Turtle} is builing 076 @param xHome the intial x-coordinate for this {@code Turtle} 077 @param yHome the intial y-coordinate for this {@code Turtle} 078 @param z the z-plane for this {@code Turtle} 079 @throws NullPointerException if {@code model} is {@code null} 080 */ 081 public Turtle(final Model model, final double xHome, final double yHome, final double z) 082 { 083 this(model, model.name, xHome, yHome, z); 084 } 085 086 087 /** 088 @param model a reference to the {@link Model} that this {@code Turtle} is builing 089 @param name a {@link String} that is a name for this {@code Turtle} 090 @param xHome the intial x-coordinate for this {@code Turtle} 091 @param yHome the intial y-coordinate for this {@code Turtle} 092 @param z the z-plane for this {@code Turtle} 093 @throws NullPointerException if {@code model} is {@code null} 094 @throws NullPointerException if {@code name} is {@code null} 095 */ 096 public Turtle(final Model model, final String name, final double xHome, final double yHome, final double z) 097 { 098 if (null == model) 099 throw new NullPointerException("Turtle's Model must not be null"); 100 if (null == name) 101 throw new NullPointerException("Turtle's name must not be null"); 102 103 this.model = model; 104 this.name = name; 105 this.xHome = xHome; 106 this.yHome = yHome; 107 this.z = z; 108 109 this.xPos = xHome; 110 this.yPos = yHome; 111 this.heading = 0; 112 this.penDown = true; 113 this.stepSize = 1; 114 } 115 116 117 /** 118 Check if this {@code Turtle}'s pen is down. 119 120 @return true if down else false 121 */ 122 public boolean isPenDown() 123 { 124 return penDown; 125 } 126 127 128 /** 129 Set this {@code Turtle}'s penDown variable. 130 131 @param value value for this {@code Turtle}'s penDown variable 132 */ 133 public void setPenDown(final boolean value) 134 { 135 penDown = value; 136 } 137 138 139 /** 140 Set this {@code Turtle}'s pen down. 141 */ 142 public void penDown() 143 { 144 penDown = true; 145 } 146 147 148 /** 149 Lift this {@code Turtle}'s pen up. 150 */ 151 public void penUp() 152 { 153 penDown = false; 154 } 155 156 157 /** 158 Get the current x position of this {@code Turtle}. 159 160 @return the x position of this {@code Turtle} 161 */ 162 public double getXPos() 163 { 164 return xPos; 165 } 166 167 168 /** 169 Get the current y position of this {@code Turtle}. 170 171 @return the y position of this {@code Turtle} 172 */ 173 public double getYPos() 174 { 175 return yPos; 176 } 177 178 179 /** 180 Get the current heading of this {@code Turtle}. 181 182 @return the heading in degrees of this {@code Turtle} 183 */ 184 public double getHeading() 185 { 186 return this.heading; 187 } 188 189 190 /** 191 Set the heading of this {@code Turtle}. 192 193 @param heading new heading in degrees for this {@code Turtle} 194 */ 195 public void setHeading(final double heading) 196 { 197 this.heading = heading; 198 } 199 200 201 /** 202 Turn this {@code Turtle} 90 degrees clockwise. 203 */ 204 public void right() 205 { 206 turn(90); 207 } 208 209 210 /** 211 Turn this {@code Turtle} 90 degrees counterclockwise. 212 */ 213 public void left() 214 { 215 turn(-90); 216 } 217 218 219 /** 220 Turn this {@code Turtle} by the given angle in degrees. 221 Use positive angles to turn clockwise and negative angles to turn counterclockwise. 222 223 @param degrees the amount to turn this {@code Turtle} in degrees 224 */ 225 public void turn(final double degrees) 226 { 227 heading = (heading + degrees) % 360; 228 } 229 230 231 /** 232 Turn this {@code Turtle} to face another {@code Turtle}. 233 234 @param turtle the {@code Turtle} to turn towards 235 */ 236 public void turnToFace(final Turtle turtle) 237 { 238 turnToFace(turtle.xPos, turtle.yPos); 239 } 240 241 242 /** 243 Turn this {@code Turtle} towards the given (x, y). 244 245 @param x the x to turn this {@code Turtle} towards 246 @param y the y to turn this {@code Turtle} towards 247 */ 248 public void turnToFace(final double x, final double y) 249 { 250 final double dx = x - xPos; 251 final double dy = y - yPos; 252 253 if (0 == dx) // avoid a division by 0 254 { 255 if (dy > 0) // if below the turtle 256 { 257 heading = 180; 258 } 259 else if (dy < 0) // if above the turtle 260 { 261 heading = 0; 262 } 263 } 264 else // dx isn't 0 so can divide by it 265 { 266 final double arcTan = Math.toDegrees(Math.atan(dy / dx)); 267 if (dx < 0) 268 { 269 heading = arcTan - 90; 270 } 271 else 272 { 273 heading = arcTan + 90; 274 } 275 } 276 } 277 278 279 /** 280 Move this {@code Turtle} to the coordinates (0, 0) and give it the heading of 0 degrees. 281 */ 282 public void home() 283 { 284 xPos = xHome; 285 yPos = yHome; 286 heading = 0; 287 } 288 289 290 /** 291 Move this {@code Turtle} to the given (x, y) location. 292 293 @param x the x-coordinate to move this {@code Turtle} to 294 @param y the y-coordinate to move this {@code Turtle} to 295 */ 296 public void moveTo(final double x, final double y) 297 { 298 xPos = x; 299 yPos = y; 300 } 301 302 303 /** 304 Move this {@code Turtle} foward one unit in the heading direction. 305 */ 306 public void forward() 307 { 308 forward(1); 309 } 310 311 312 /** 313 Move this {@code Turtle} backward one unit. 314 */ 315 public void backward() 316 { 317 backward(1); 318 } 319 320 321 /** 322 Move this {@code Turtle} backward the given number of units. 323 324 @param distance the distance to walk this {@code Turtle} backward 325 */ 326 public void backward(final double distance) 327 { 328 forward(-distance); 329 } 330 331 332 /** 333 Move this {@code Turtle} forward the given number of units 334 in the heading direction. If the pen is down, then add two 335 {@link Vertex} objects and a {@link LineSegment} object to 336 the underlying {@code Turtle}. 337 338 @param distance the distance to walk this {@code Turtle} forward in the heading direction 339 */ 340 public void forward(final double distance) 341 { 342 final double xOld = xPos; 343 final double yOld = yPos; 344 345 // change the current position 346 xPos = xOld + (stepSize * distance * Math.sin(Math.toRadians(heading))); 347 yPos = yOld + (stepSize * distance * Math.cos(Math.toRadians(heading))); 348 349 if (penDown) 350 { 351 final int index = this.model.vertexList.size(); 352 353 final Vertex oldVertex = new Vertex(xOld, yOld, z); 354 final Vertex newVertex = new Vertex(xPos, yPos, z); 355 356 this.model.addVertex(oldVertex, newVertex); 357 this.model.addPrimitive(new LineSegment(index, index+1)); 358 } 359 } 360 361 362 /** 363 Same as the forward() method but without building a {@link LineSegment}. 364 <p> 365 This is part of "Turtle Geometry" as defined by Ronald Goldman. 366 <p> 367 https://www.clear.rice.edu/comp360/lectures/old/TurtlesGraphicL1New.pdf 368 https://people.engr.tamu.edu/schaefer/research/TurtlesforCADRevised.pdf 369 https://www.routledge.com/An-Integrated-Introduction-to-Computer-Graphics-and-Geometric-Modeling/Goldman/p/book/9781138381476 370 371 @param distance the distance to walk this {@code Turtle} forward in the heading direction 372 */ 373 public void move(final double distance) 374 { 375 // change the current position 376 xPos = xPos + (stepSize * distance * Math.sin(Math.toRadians(heading))); 377 yPos = yPos + (stepSize * distance * Math.cos(Math.toRadians(heading))); 378 } 379 380 381 /** 382 Change the length of the step size by the factor {@code s}. 383 <p> 384 This is part of "Turtle Geometry" as defined by Ronald Goldman. 385 386 @param s scaling factor for the new {@code stepSize} 387 */ 388 public void resize(final double s) 389 { 390 stepSize = s * stepSize; 391 } 392 393 394 /** 395 For debugging. 396 397 @return {@link String} representation of this {@code Turtle} object 398 */ 399 @Override 400 public String toString() 401 { 402 String result = ""; 403 result += "Turtle: " + name + "\n"; 404 result += "z-plane: " + z + "\n"; 405 result += "origin: (" + xPos + ", " + yPos + ")\n"; 406 result += model.toString() + "\n"; 407 return result; 408 } 409}//Turtle