gann-cdf /
turtle-logo
| 1 | package org.gannacademy.cdf.turtlelogo; |
||
| 2 | |||
| 3 | import javax.imageio.ImageIO; |
||
| 4 | import java.awt.*; |
||
| 5 | import java.awt.geom.AffineTransform; |
||
| 6 | import java.awt.image.BufferedImage; |
||
| 7 | import java.io.IOException; |
||
| 8 | |||
| 9 | /** |
||
| 10 | * <p>A turtles lives in a {@link Terrarium}. We imagine that turtles all hold a pen in their mouth. As they walk around |
||
| 11 | * the terrarium, they leave a trail (a series of {@link Track} segments) behind them. The turtles can avoid leaving |
||
| 12 | * a track if they pick up their pen.</p> |
||
| 13 | * |
||
| 14 | * <p><img src="doc-files/trail.png" alt="Turtle leaving a trail"></p> |
||
| 15 | * |
||
| 16 | * <p>Turtles understand a limited number of instructions:</p> |
||
| 17 | * |
||
| 18 | * <table style="margin-left: 4em;"> |
||
| 19 | * <tr> |
||
| 20 | * <td><img src="doc-files/move.png" alt="move() example"></td> |
||
| 21 | * <td><b>move(</b> |
||
| 22 | * <i>steps</i><b>)</b> (a.k.a <i>forward</i> or <i>fd</i> and <i>back</i> or <i>bk</i>) — the turtle will step |
||
| 23 | * forward, in the direction that it is currently facing, some number of steps (i.e. pixels).</td> |
||
| 24 | * </tr> |
||
| 25 | * <tr> |
||
| 26 | * <td><img src="doc-files/turn.png" alt="turn() example"></td> |
||
| 27 | * <td><b>turn(</b><i>degrees</i><b>)</b> |
||
| 28 | * (a.k.a. <i>left</i> or <i>lt</i> and <i>right</i> or <i>rt</i>) — the turtle will turn from its current heading |
||
| 29 | * some number of degrees.</td> |
||
| 30 | * </tr> |
||
| 31 | * <tr> |
||
| 32 | * <td><img src="doc-files/moveTo.png" alt="moveTo() example"></td> |
||
| 33 | * <td><b>moveTo(</b><i>x</i>, <i>y</i><b>)</b> (a.k.a. <i>to</i>) — the turtle will move from its current |
||
| 34 | * location to the coordinates given, without changing its heading.</td> |
||
| 35 | * </tr> |
||
| 36 | * <tr> |
||
| 37 | * <td><img src="doc-files/teleport.png" alt="teleport() example"></td> |
||
| 38 | * <td><b>teleport(</b><i>x</i>, <i>y</i><b>)</b> (a.k.a. <i>tp</i>) — the turtle will teleport (like in Star |
||
| 39 | * Trek) from its current location to the new coordinates, without changing its heading <i>and</i> without allowing the |
||
| 40 | * pen to drag between locations.</td> |
||
| 41 | * </tr> |
||
| 42 | * <tr> |
||
| 43 | * <td><img src="doc-files/penColor.png" alt="penColor() example"></td> |
||
| 44 | * <td><b>penColor(</b><i>color</i><b>)</b> (a.k.a. <i>pc</i>) — the turtle will change the color of its pen |
||
| 45 | * (initially the pen is black)</td> |
||
| 46 | * </tr> |
||
| 47 | * <tr> |
||
| 48 | * <td><img src="doc-files/penWidth.png" alt="penWidth() example"></td> |
||
| 49 | * <td><b>penWidth(</b><i>width</i><b>)</b> (a.k.a. <i>pw</i>) — the turtle will change the width of its pen |
||
| 50 | * stroke (measured in pixels)</td> |
||
| 51 | * </tr> |
||
| 52 | * <tr> |
||
| 53 | * <td><img src="doc-files/hide.png" alt="hide() example"></td> |
||
| 54 | * <td><b>hide()</b> (a.k.a. <i>ht</i>) — the turtle will hide itself (but remain at its current location and |
||
| 55 | * heading)</td> |
||
| 56 | * </tr> |
||
| 57 | * <tr> |
||
| 58 | * <td><img src="doc-files/show.png" alt="show() example"></td> |
||
| 59 | * <td><b>show()</b> (a.k.a. <i>st</i>) — the turtle, if hidden, will show itself again</td> |
||
| 60 | * </tr> |
||
| 61 | * <caption> </caption> |
||
| 62 | * </table> |
||
| 63 | */ |
||
| 64 | public class Turtle { |
||
| 65 | /** |
||
| 66 | * The parts of the turtle that are "under the shell" are not meant to be used by students. This mechanism |
||
| 67 | * (inspired by <a href="https://stackoverflow.com/a/18634125">this awesome Stack Overflow answer</a>) recreates a |
||
| 68 | * version of the C++ <code>friend</code> concept: a public method that is only available to <i>some</i> other |
||
| 69 | * objects, rather than <i>all</i> other objects. |
||
| 70 | * |
||
| 71 | * @author <a href="https://github.com/gann-cdf/turtlelogo/issues">Seth Battis</a> |
||
| 72 | */ |
||
| 73 | public static final class UnderTheShell { |
||
| 74 | private UnderTheShell() { |
||
| 75 | } |
||
| 76 | } |
||
| 77 | |||
| 78 | protected static final UnderTheShell UNDER_THE_SHELL = new UnderTheShell(); |
||
| 79 | |||
| 80 | /** |
||
| 81 | * 270° |
||
| 82 | */ |
||
| 83 | public static final double NORTH = 270; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * 90° |
||
| 87 | */ |
||
| 88 | public static final double SOUTH = 90; |
||
| 89 | |||
| 90 | /** |
||
| 91 | * 0° |
||
| 92 | */ |
||
| 93 | public static final double EAST = 0; |
||
| 94 | |||
| 95 | /** |
||
| 96 | * 180° |
||
| 97 | */ |
||
| 98 | public static final double WEST = 180; |
||
| 99 | |||
| 100 | /** |
||
| 101 | * {@link #EAST} |
||
| 102 | */ |
||
| 103 | public static final double DEFAULT_HEADING_IN_DEGREES = EAST; // degrees |
||
| 104 | |||
| 105 | /** |
||
| 106 | * {@link java.awt.Color#BLACK} |
||
| 107 | */ |
||
| 108 | public static final Color DEFAULT_PEN_COLOR = Color.BLACK; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * 1.0 pixels |
||
| 112 | */ |
||
| 113 | public static final float DEFAULT_PEN_WIDTH = 1; |
||
| 114 | |||
| 115 | /** |
||
| 116 | * <code>true</code> |
||
| 117 | */ |
||
| 118 | public static final boolean DEFAULT_PEN_DOWN = true; |
||
| 119 | |||
| 120 | /** |
||
| 121 | * <code>false</code> |
||
| 122 | */ |
||
| 123 | public static final boolean DEFAULT_HIDDEN = false; |
||
| 124 | |||
| 125 | |||
| 126 | private Terrarium terrarium; |
||
| 127 | private static BufferedImage icon; |
||
| 128 | |||
| 129 | private double x, y; |
||
| 130 | private double headingInDegrees; |
||
| 131 | private Color penColor; |
||
| 132 | private BasicStroke penStroke; |
||
| 133 | private boolean penDown; |
||
| 134 | private boolean hidden; |
||
| 135 | |||
| 136 | /** |
||
| 137 | * Construct a turtle in the default terrarium |
||
| 138 | */ |
||
| 139 | public Turtle() { |
||
| 140 | this(Terrarium.getInstance()); |
||
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Construct a turtle in a custom terrarium |
||
| 145 | * |
||
| 146 | * @param terrarium to house the turtle |
||
| 147 | */ |
||
| 148 | public Turtle(Terrarium terrarium) { |
||
| 149 | this.x = terrarium.getWidth() / 2.0; |
||
| 150 | this.y = terrarium.getHeight() / 2.0; |
||
| 151 | this.headingInDegrees = DEFAULT_HEADING_IN_DEGREES; |
||
| 152 | this.penColor = DEFAULT_PEN_COLOR; |
||
| 153 | this.penStroke = new BasicStroke(DEFAULT_PEN_WIDTH); |
||
| 154 | this.penDown = DEFAULT_PEN_DOWN; |
||
| 155 | this.hidden = DEFAULT_HIDDEN; |
||
| 156 | this.terrarium = terrarium; |
||
| 157 | this.terrarium.add(this, UNDER_THE_SHELL); |
||
| 158 | } |
||
| 159 | |||
| 160 | /** |
||
| 161 | * @return X-coordinate of turtle |
||
| 162 | */ |
||
| 163 | public double getX() { |
||
| 164 | return x; |
||
| 165 | } |
||
| 166 | |||
| 167 | /** |
||
| 168 | * @return Y-coordinate of turtle |
||
| 169 | */ |
||
| 170 | public double getY() { |
||
| 171 | return y; |
||
| 172 | } |
||
| 173 | |||
| 174 | /** |
||
| 175 | * @return Current turtle heading in degrees |
||
| 176 | */ |
||
| 177 | public double getHeadingInDegrees() { |
||
| 178 | return headingInDegrees; |
||
| 179 | } |
||
| 180 | |||
| 181 | /** |
||
| 182 | * @return Current turtle heading in radians |
||
| 183 | */ |
||
| 184 | public double getHeadingInRadians() { |
||
| 185 | return Math.toRadians(headingInDegrees); |
||
| 186 | } |
||
| 187 | |||
| 188 | /** |
||
| 189 | * @return Current pen color |
||
| 190 | */ |
||
| 191 | public Color getPenColor() { |
||
| 192 | return penColor; |
||
| 193 | } |
||
| 194 | |||
| 195 | /** |
||
| 196 | * @return Current pen width |
||
| 197 | */ |
||
| 198 | public double getPenWidth() { |
||
| 199 | return penStroke.getLineWidth(); |
||
| 200 | } |
||
| 201 | |||
| 202 | protected BasicStroke getPenStroke() { |
||
| 203 | return penStroke; |
||
| 204 | } |
||
| 205 | |||
| 206 | /** |
||
| 207 | * @return <code>true</code> if the pen is down, <code>false</code> otherwise |
||
| 208 | */ |
||
| 209 | public boolean isPenDown() { |
||
| 210 | return penDown; |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * @return <code>true</code> if the turtle is hidden, <code>false</code> otherwise |
||
| 215 | */ |
||
| 216 | public boolean isHidden() { |
||
| 217 | return hidden; |
||
| 218 | } |
||
| 219 | |||
| 220 | private BufferedImage getIcon() { |
||
| 221 | if (icon == null) { |
||
| 222 | try { |
||
| 223 | icon = ImageIO.read(getClass().getResource("/turtle.png")); |
||
|
0 ignored issues
–
show
|
|||
| 224 | } catch (IOException e) { |
||
| 225 | System.err.println("The image file containing the turtle icon could not be found and/or opened."); |
||
| 226 | e.printStackTrace(); |
||
|
0 ignored issues
–
show
|
|||
| 227 | } |
||
| 228 | } |
||
| 229 | return icon; |
||
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * @return Terrarium currently housing the turtle |
||
| 234 | */ |
||
| 235 | public Terrarium getTerrarium() { |
||
| 236 | return terrarium; |
||
| 237 | } |
||
| 238 | |||
| 239 | /** |
||
| 240 | * Move the turtle to another terrarium |
||
| 241 | * |
||
| 242 | * @param terrarium to house the turtle |
||
| 243 | */ |
||
| 244 | public void setTerrarium(Terrarium terrarium) { |
||
| 245 | if (this.terrarium != null) { |
||
| 246 | this.terrarium.remove(this, UNDER_THE_SHELL); |
||
| 247 | } |
||
| 248 | this.terrarium = terrarium; |
||
| 249 | terrarium.add(this, UNDER_THE_SHELL); |
||
| 250 | } |
||
| 251 | |||
| 252 | /** |
||
| 253 | * Alias for {@link #back(double)} |
||
| 254 | * |
||
| 255 | * @param steps in pixels |
||
| 256 | */ |
||
| 257 | public void bk(double steps) { |
||
| 258 | back(steps); |
||
| 259 | } |
||
| 260 | |||
| 261 | /** |
||
| 262 | * Alias for {@link #move(double)} |
||
| 263 | * |
||
| 264 | * @param steps in pixels |
||
| 265 | */ |
||
| 266 | public void back(double steps) { |
||
| 267 | move(-1 * steps); |
||
| 268 | } |
||
| 269 | |||
| 270 | /** |
||
| 271 | * Alias for {@link #forward(double)} |
||
| 272 | * |
||
| 273 | * @param steps in pixels |
||
| 274 | */ |
||
| 275 | public void fd(double steps) { |
||
| 276 | forward(steps); |
||
| 277 | } |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Alias for {@link #move(double)} |
||
| 281 | * |
||
| 282 | * @param steps in pixels |
||
| 283 | */ |
||
| 284 | public void forward(double steps) { |
||
| 285 | move(steps); |
||
| 286 | } |
||
| 287 | |||
| 288 | /** |
||
| 289 | * <p>Move the turtle in the direction of its current heading</p> |
||
| 290 | * <p>A positive value for <code>steps</code> is interpreted as forward movement and a negative value as backward |
||
| 291 | * movement</p> |
||
| 292 | * |
||
| 293 | * @param steps in pixels |
||
| 294 | */ |
||
| 295 | public void move(double steps) { |
||
| 296 | double newX = x + Math.cos(getHeadingInRadians()) * steps, |
||
| 297 | newY = y + Math.sin(getHeadingInRadians()) * steps; |
||
| 298 | if (penDown) { |
||
| 299 | getTerrarium().add(new Track(x, y, newX, newY, penColor, penStroke, UNDER_THE_SHELL), UNDER_THE_SHELL); |
||
| 300 | } |
||
| 301 | x = newX; |
||
| 302 | y = newY; |
||
| 303 | } |
||
| 304 | |||
| 305 | /** |
||
| 306 | * Alias for {@link #moveTo(double, double)} |
||
| 307 | * |
||
| 308 | * @param x coordinate |
||
| 309 | * @param y coordinate |
||
| 310 | */ |
||
| 311 | public void to(double x, double y) { |
||
| 312 | moveTo(x, y); |
||
| 313 | } |
||
| 314 | |||
| 315 | /** |
||
| 316 | * <p>Move the turtle to a particular location</p> |
||
| 317 | * <p>The turtle moves directly to the window coordinates (<code>x</code>, <code>y</code>). Note that the origin of |
||
| 318 | * the wind ow is in the top, left corner and that, while the X-axis increases from left to right, the Y-axis |
||
| 319 | * increases <i>from to to bottom</i>.</p> |
||
| 320 | * <p>If the turtle's pen is currently down, the move will create a track from the old location to the new location.</p> |
||
| 321 | * |
||
| 322 | * @param x coordinate |
||
| 323 | * @param y coordinate |
||
| 324 | */ |
||
| 325 | public void moveTo(double x, double y) { |
||
| 326 | if (penDown) { |
||
| 327 | getTerrarium().add(new Track(this.x, this.y, x, y, penColor, penStroke, UNDER_THE_SHELL), UNDER_THE_SHELL); |
||
| 328 | } |
||
| 329 | this.x = x; |
||
| 330 | this.y = y; |
||
| 331 | } |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Alias for {@link #teleport(double, double)} |
||
| 335 | * |
||
| 336 | * @param x coordinate |
||
| 337 | * @param y coordinate |
||
| 338 | */ |
||
| 339 | public void tp(double x, double y) { |
||
| 340 | teleport(x, y); |
||
| 341 | } |
||
| 342 | |||
| 343 | /** |
||
| 344 | * <p>Move the turtle instantaneously to a particular location</p> |
||
| 345 | * <p>The turtle moves directly to the window coordinates (<code>x</code>, <code>y</code>). Note that the origin of |
||
| 346 | * the wind ow is in the top, left corner and that, while the X-axis increases from left to right, the Y-axis |
||
| 347 | * increases <i>from to to bottom</i>.</p> |
||
| 348 | * <p>No track is left by a teleportation.</p> |
||
| 349 | * |
||
| 350 | * @param x coordinate |
||
| 351 | * @param y coordinate |
||
| 352 | */ |
||
| 353 | public void teleport(double x, double y) { |
||
| 354 | this.x = x; |
||
| 355 | this.y = y; |
||
| 356 | } |
||
| 357 | |||
| 358 | /** |
||
| 359 | * <p>Reset the turtle to its home position</p> |
||
| 360 | * <p>The turtle's home position is at the center of the window, heading {@link #EAST}</p> |
||
| 361 | */ |
||
| 362 | public void home() { |
||
| 363 | teleport(getTerrarium().getWidth() / 2.0, getTerrarium().getHeight() / 2.0); |
||
| 364 | head(EAST); |
||
| 365 | } |
||
| 366 | |||
| 367 | /** |
||
| 368 | * Alias for {@link #right(double)} |
||
| 369 | * |
||
| 370 | * @param angle in degrees |
||
| 371 | */ |
||
| 372 | public void rt(double angle) { |
||
| 373 | right(angle); |
||
| 374 | } |
||
| 375 | |||
| 376 | /** |
||
| 377 | * Alias for {@link #turn(double)} |
||
| 378 | * |
||
| 379 | * @param angle in degrees |
||
| 380 | */ |
||
| 381 | public void right(double angle) { |
||
| 382 | turn(angle); |
||
| 383 | } |
||
| 384 | |||
| 385 | /** |
||
| 386 | * Alias for {@link #left(double)} |
||
| 387 | * |
||
| 388 | * @param angle in degrees |
||
| 389 | */ |
||
| 390 | public void lt(double angle) { |
||
| 391 | left(angle); |
||
| 392 | } |
||
| 393 | |||
| 394 | /** |
||
| 395 | * Alias for {@link #turn(double)} |
||
| 396 | * |
||
| 397 | * @param angle in degrees |
||
| 398 | */ |
||
| 399 | public void left(double angle) { |
||
| 400 | turn(-1 * angle); |
||
| 401 | } |
||
| 402 | |||
| 403 | /** |
||
| 404 | * <p>Turn the turtle from its current heading</p> |
||
| 405 | * <p>A positive angle is interpreted as a <i>right</i> turn and a negative angle is a left turn. This is mildly |
||
| 406 | * surprising if you know about the unit circle, but is because the Y-axis of the window increases from top to bottom, |
||
| 407 | * which results in a mirrored unit circle around the X-axis, with 90° at the {@link #SOUTH} and270° at the |
||
| 408 | * {@link #NORTH}</p> |
||
| 409 | * |
||
| 410 | * @param angle in degrees |
||
| 411 | */ |
||
| 412 | public void turn(double angle) { |
||
| 413 | headingInDegrees = (headingInDegrees + angle) % 360; |
||
| 414 | getTerrarium().repaint(); |
||
| 415 | } |
||
| 416 | |||
| 417 | /** |
||
| 418 | * Alias for {@link #head(double)} |
||
| 419 | * |
||
| 420 | * @param heading in degrees |
||
| 421 | */ |
||
| 422 | public void hd(double heading) { |
||
| 423 | head(heading); |
||
| 424 | } |
||
| 425 | |||
| 426 | /** |
||
| 427 | * <p>Turn the turtle to a particular heading</p> |
||
| 428 | * <p>Note that, because the Y-axis of the window increases from top to bottom, the usual angles of the unit circle |
||
| 429 | * have been mirrored around the X-axis, with 90° at the {@link #SOUTH} and 270%deg; at the {@link #NORTH}. |
||
| 430 | * Convenience constants have been provided for the cardinal directions.</p> |
||
| 431 | * |
||
| 432 | * @param heading [0..360) in degrees |
||
| 433 | */ |
||
| 434 | public void head(double heading) { |
||
| 435 | this.headingInDegrees = heading % 360; |
||
| 436 | } |
||
| 437 | |||
| 438 | /** |
||
| 439 | * Alis for {@link #penUp()} |
||
| 440 | */ |
||
| 441 | public void pu() { |
||
| 442 | penUp(); |
||
| 443 | } |
||
| 444 | |||
| 445 | /** |
||
| 446 | * Lift the turtle's pen up, causing it not to leave a track |
||
| 447 | */ |
||
| 448 | public void penUp() { |
||
| 449 | penDown = false; |
||
| 450 | } |
||
| 451 | |||
| 452 | /** |
||
| 453 | * Alias for {@link #penDown()} |
||
| 454 | */ |
||
| 455 | public void pd() { |
||
| 456 | penDown(); |
||
| 457 | } |
||
| 458 | |||
| 459 | /** |
||
| 460 | * Lower the turtle's pen, causing it to leave a trail |
||
| 461 | */ |
||
| 462 | public void penDown() { |
||
| 463 | penDown = true; |
||
| 464 | } |
||
| 465 | |||
| 466 | /** |
||
| 467 | * Alias for {@link #penColor(Color)} |
||
| 468 | * |
||
| 469 | * @param color to use |
||
| 470 | */ |
||
| 471 | public void pc(Color color) { |
||
| 472 | penColor(color); |
||
| 473 | } |
||
| 474 | |||
| 475 | /** |
||
| 476 | * <p>Set the color of the turtle's pen</p> |
||
| 477 | * <p>Color values are given as {@link Color} values. {@link Color} has a number of helpful constants like |
||
| 478 | * {@link Color#GREEN} or {@link Color#GREEN}. Custom colors can also be constructed. Refer to the |
||
| 479 | * <a href="https://docs.oracle.com/javase/10/docs/api/java/awt/Color.html" target="_blank">j<code>ava.awt.Color</code> |
||
| 480 | * API documentation</a> more details (including how to create transparent colors!)</p> |
||
| 481 | * |
||
| 482 | * @param color to use |
||
| 483 | */ |
||
| 484 | public void penColor(Color color) { |
||
| 485 | penColor = color; |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * Alias for {@link #penWidth(double)} |
||
| 490 | * |
||
| 491 | * @param width in pixels |
||
| 492 | */ |
||
| 493 | public void pw(double width) { |
||
| 494 | penWidth(width); |
||
| 495 | } |
||
| 496 | |||
| 497 | /** |
||
| 498 | * Set the width of the turtle's pen |
||
| 499 | * |
||
| 500 | * @param width in pixels |
||
| 501 | */ |
||
| 502 | public void penWidth(double width) { |
||
| 503 | penStroke = new BasicStroke((float) width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); |
||
| 504 | } |
||
| 505 | |||
| 506 | /** |
||
| 507 | * Alias for {@link #hide()} |
||
| 508 | */ |
||
| 509 | public void ht() { |
||
| 510 | hide(); |
||
| 511 | } |
||
| 512 | |||
| 513 | /** |
||
| 514 | * <p>Hide the turtle</p> |
||
| 515 | * <p>Hiding the turtle causes it to become invisible — it can still be moved and create tracks, but the turtle |
||
| 516 | * itself is not visible</p> |
||
| 517 | */ |
||
| 518 | public void hide() { |
||
| 519 | hidden = true; |
||
| 520 | getTerrarium().repaint(); |
||
| 521 | } |
||
| 522 | |||
| 523 | /** |
||
| 524 | * Alias for {@link #show()} |
||
| 525 | */ |
||
| 526 | public void st() { |
||
| 527 | show(); |
||
| 528 | } |
||
| 529 | |||
| 530 | /** |
||
| 531 | * Show the turtle (if it was hidden) |
||
| 532 | */ |
||
| 533 | public void show() { |
||
| 534 | hidden = false; |
||
| 535 | getTerrarium().repaint(); |
||
| 536 | } |
||
| 537 | |||
| 538 | /** |
||
| 539 | * <p>Draw the turtle</p> |
||
| 540 | * <p>May only be called by {@link Terrarium} and its subclasses, enforced by {@link Terrarium.UnderTheSurface}</p> |
||
| 541 | * |
||
| 542 | * @param context for drawing commands |
||
| 543 | * @param key to authenticate "Terrarium-iality" |
||
| 544 | */ |
||
| 545 | public void draw(Graphics2D context, Terrarium.UnderTheSurface key) { |
||
| 546 | key.hashCode(); |
||
| 547 | drawIcon(x, y, getHeadingInRadians(), context); |
||
| 548 | } |
||
| 549 | |||
| 550 | protected void drawIcon(double x, double y, double headingInRadians, Graphics2D context) { |
||
| 551 | if (!hidden) { |
||
| 552 | AffineTransform transform = new AffineTransform(); // transformations are applied in reverse order |
||
| 553 | transform.translate(x, y); // move turtle to location |
||
| 554 | transform.rotate(headingInRadians); // orient turtle to heading |
||
| 555 | transform.translate(-1 * getIcon().getWidth(), getIcon().getHeight() / -2.0); // move icon origin to turtle nose |
||
|
0 ignored issues
–
show
|
|||
| 556 | context.drawImage(getIcon(), transform, null); |
||
| 557 | } |
||
| 558 | } |
||
| 559 | } |
||
| 560 |
If you really need to set this static field, consider writing a thread-safe setter and atomic getter.