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