Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

(Racing cars) Rewrite Programming Exercise 15.29 using a thread to control car r

ID: 3777743 • Letter: #

Question

(Racing cars) Rewrite Programming Exercise 15.29 using a thread to control
car racing. Compare the program with Programming Exercise 15.29 by setting
the delay time to 10 in both programs. Which one runs the animation faster?

Here's the code from 15.29

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polyline;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Exercise extends Application {

   @Override
   public void start(Stage primaryStage) throws Exception {

       CarPane carPane = new CarPane(600, 200);

       Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10), e -> carPane.moveCar(1)));
       timeline.setCycleCount(carPane.getOneCycleDuration());
       timeline.play();
       timeline.setOnFinished(e -> {
           carPane.reset();
           timeline.play();
       });

       // Buttons pause, resume, increase, decrease
       Button pause = new Button("Pause");
       pause.setOnAction(e -> timeline.pause());

       Button resume = new Button("Resume");
       resume.setOnAction(e -> timeline.play());

       Button increase = new Button("Increase");
       increase.setOnAction(e -> {
           timeline.setRate(timeline.getCurrentRate() + 1);
       });

       Button decrease = new Button("Decrease");
       decrease.setOnAction(e -> {
           if (timeline.getCurrentRate() <= 0)
               return;

           timeline.setRate(timeline.getCurrentRate() - 1);
       });

       HBox hButtons = new HBox(pause, resume, increase, decrease);
       hButtons.setSpacing(10);
       hButtons.setAlignment(Pos.CENTER);
       hButtons.setPadding(new Insets(5, 5, 5, 5));

       BorderPane borderPane = new BorderPane(carPane, null, null, hButtons, null);
       borderPane.setOnKeyPressed(e -> {
           switch (e.getCode()) {
           case UP:
               increase.fire();
               break;
           case DOWN:
               decrease.fire();
               break;
           }
       });
       primaryStage.setScene(new Scene(borderPane));
       primaryStage.setTitle("Moving Car");
       primaryStage.show();

   }

   public static void main(String[] args) {
       Application.launch(args);

   }

   private class CarPane extends Pane {

       private double w; // pane width
       private double h; // pane height

       // left Tire x,y and radius
       private double leftTireX; // left tire is the car's drawing start point
       private double leftTireY;
       private double tireRadius;

       Circle[] tires = new Circle[2]; // Index 0, 1 = left and right tire
       Polyline cover = new Polyline();
       ObservableList<Double> points;
       Rectangle base;

       private CarPane(double width, double height) {

           // Get width height and measurements for the left tire (starting
           // point)
           w = width;
           h = height;
           leftTireX = w * 0.2;
           leftTireY = h * 0.9;
           tireRadius = h * 0.1;

           // set MIN and MAX width
           setMinWidth(w);
           setMinHeight(h);

           setMaxWidth(w);
           setMaxHeight(h);

           reset(); // draws and adjusts car to starting position
       }

       public void reset() {

           if (points != null)
               points.clear();
           getChildren().clear();
           drawCar();
           moveCar(tireRadius * 13 * -1);
       }

       public void drawCar() {
           for (int i = 0; i < tires.length; i++) {
               tires[i] = new Circle(leftTireX + (i * 4 * tireRadius), leftTireY, tireRadius);
               tires[i].setStroke(Color.BLACK);
               tires[i].setFill(Color.TRANSPARENT);

           }

           double baseX = tires[0].getCenterX() - tires[0].getRadius() * 3;
           double baseY = tires[0].getCenterY() - tires[0].getRadius() * 3;
           base = new Rectangle(baseX, baseY, tireRadius * 10, tireRadius * 2);
           base.setFill(Color.TRANSPARENT);
           base.setStroke(Color.BLACK);

           // draw the car's top cover
           double startX = base.getX() + tireRadius * 2;
           double startY = base.getY();
           double currentX = startX;
           double currentY = startY;

           points = cover.getPoints();

           double distance = tireRadius * 2;
           points.addAll(currentX, currentY); // start point

           currentX += distance;
           currentY -= distance;
           points.addAll(currentX, currentY); // up right

           currentX += distance;
           points.addAll(currentX, currentY); // right

           currentX += distance;
           currentY += distance;
           points.addAll(currentX, currentY); // down right

           points.addAll(startX, startY); // connect to starting point
           cover.setFill(Color.BLUE);

           getChildren().addAll(tires);
           getChildren().add(base);
           getChildren().add(cover);
       }

       private void moveCar(double distance) {

           // tires
           for (Circle c : tires) {
               c.setCenterX(c.getCenterX() + distance);
           }

           base.setX(base.getX() + distance);

           for (int i = 0; i < points.size(); i += 2) {
               points.set(i, points.get(i) + distance);
           }

       }

       public int getOneCycleDuration() {
           return (int) w;
       }
   }
}

Explanation / Answer

RaceCars.java

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Line;
import javafx.scene.layout.Pane;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.util.Duration;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;

public class RaceCars extends Application{
   /*public static void main(String[] args){

   }*/
   private int screenWidth = 800;
   private int screenHeight = 400;
   /*private double moveRate = 20;
   private double position = 0;*/

   @Override
   public void start(Stage primaryStage){
       Pane pane = new Pane();

       Car raceCar = new Car();

       //raceCar.reposition(moveRate);

       pane.getChildren().add(raceCar.getBody());
       pane.getChildren().add(raceCar.getWheel1());
       pane.getChildren().add(raceCar.getWheel2());
       pane.getChildren().add(raceCar.getCabin());

       EventHandler<ActionEvent> eventHandler = e->{
           pane.getChildren().clear();
           //position += moveRate;
           raceCar.reposition();
           pane.getChildren().add(raceCar.getBody());
           pane.getChildren().add(raceCar.getWheel1());
           pane.getChildren().add(raceCar.getWheel2());
           pane.getChildren().add(raceCar.getCabin());
       };

       Timeline animation = new Timeline(
           new KeyFrame(Duration.millis(10), eventHandler));
       animation.setCycleCount(Timeline.INDEFINITE);
       animation.play();

       Scene scene = new Scene(pane, screenWidth, screenHeight);
       primaryStage.setTitle("RaceCar");
       primaryStage.setScene(scene);
       primaryStage.show();
   }

   class Car{
       private Polygon cabin = null;
      
       private Rectangle body = null;
       private double bodyHeight = 10;
       private double bodyWidth = 50;

       private Circle wheel1 = null;
       private Circle wheel2 = null;
       private double wheelRadius = 5;

       private ObservableList<Double> list = null;

       private double moveRate = 20;
       private double position = 0;

       //Create the car at the starting point
       public Car(){
           this.reset();
       }

       public Rectangle getBody(){
           return body;
       }

       public Polygon getCabin(){
           return cabin;
       }

       public Circle getWheel1(){return wheel1;}

       public Circle getWheel2(){return wheel2;}

       public void reposition(){
           position += moveRate;

           if(position + bodyWidth >= screenWidth){
               this.reset();
           }
           else{
               //Create the cabin
               cabin = new Polygon();
               list = cabin.getPoints();
               double centerX = (bodyWidth / 2) + position, centerY = (screenHeight - 20);
               double radius = Math.min(bodyWidth, bodyHeight) * 0.90;
               for(int i = 0; i < 4; i++){
                   list.add(centerX + radius * Math.cos(2* i * Math.PI / 6));
                   list.add(centerY - radius * Math.sin(2 * i * Math.PI/6));
               }

               //Create the body
               body = new Rectangle(position, screenHeight - 20, bodyWidth, bodyHeight);

               //Create the wheels
               wheel1 = new Circle((bodyWidth/4) + position, screenHeight - 5, 5);
               wheel2 = new Circle((3*bodyWidth/4) + position, screenHeight - 5, 5);
           }
       }

       //Reset the position of the car to the start
       public void reset(){
           position = 0;
           this.reposition();
       }
   }
}