diff --git a/src/edu/nd/sarec/railwaycrossing/Simulation.java b/src/edu/nd/sarec/railwaycrossing/Simulation.java new file mode 100644 index 00000000..0bf1deff --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/Simulation.java @@ -0,0 +1,100 @@ +package edu.nd.sarec.railwaycrossing; + +import java.util.ArrayList; +import java.util.Collection; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.MapBuilder; +import edu.nd.sarec.railwaycrossing.model.infrastructure.RailwayTracks; +import edu.nd.sarec.railwaycrossing.model.infrastructure.Road; +import edu.nd.sarec.railwaycrossing.model.infrastructure.gate.CrossingGate; +import edu.nd.sarec.railwaycrossing.model.vehicles.Car; +import edu.nd.sarec.railwaycrossing.model.vehicles.Train; +import edu.nd.sarec.railwaycrossing.view.MapDisplay; +import javafx.animation.AnimationTimer; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.layout.Pane; +import javafx.stage.Stage; + +public class Simulation extends Application{ + + private Pane root; + private Scene scene; + private MapBuilder mapBuilder; + private MapDisplay mapDisplay; + + @Override + public void start(Stage stage) throws Exception { + + root = new Pane(); + + // Build infrastructure + mapBuilder = new MapBuilder(); + mapDisplay = new MapDisplay(root, mapBuilder.getRoads(), mapBuilder.getTracks(),mapBuilder.getAllGates()); + mapDisplay.drawTracks(); + mapDisplay.drawRoad(); + mapDisplay.drawGate(); + + scene = new Scene(root,1200,1000); + stage.setTitle("Railways"); + stage.setScene(scene); + stage.show(); + + // Train + RailwayTracks track = mapBuilder.getTrack("Royal"); + Train train = new Train(track.getEndX()+100,track.getEndY()-25); + root.getChildren().add(train.getImageView()); + + for(CrossingGate gate: mapBuilder.getAllGates()) + train.addObserver(gate); + + // Sets up a repetitive loop i.e., in handle that runs the actual simulation + new AnimationTimer(){ + + @Override + public void handle(long now) { + + createCar(); + train.move(); + + for(CrossingGate gate: mapBuilder.getAllGates()) + gate.operateGate(); + + if (train.offScreen()) + train.reset(); + + clearCars(); + } + }.start(); + } + + // Clears cars as they leave the simulation + private void clearCars(){ + Collection roads = mapBuilder.getRoads(); + for(Road road: roads){ + if (road.getCarFactory()!= null){ + ArrayList junkCars = road.getCarFactory().removeOffScreenCars(); + mapDisplay.removeCarImages(junkCars); + } + } + } + + private void createCar(){ + Collection roads = mapBuilder.getRoads(); + for(Road road: roads){ + if (road.getCarFactory() != null){ + if ((int)(Math.random() * 100) == 15){ + Car car = road.getCarFactory().buildCar(); + if (car != null){ + root.getChildren().add(car.getImageView()); + } + } + } + } + } + + public static void main(String[] args){ + launch(args); + } +} + diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/Direction.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/Direction.java new file mode 100644 index 00000000..f1b0d627 --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/Direction.java @@ -0,0 +1,9 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure; + +public enum Direction { + NORTH, + SOUTH, + EAST, + WEST, + CUSTOM; +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/MapBuilder.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/MapBuilder.java new file mode 100644 index 00000000..267ae8e1 --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/MapBuilder.java @@ -0,0 +1,70 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure; + +import java.awt.Point; +import java.util.Collection; +import java.util.HashMap; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.gate.CrossingGate; + +/** + * Creates all infrastructure for the simulation + * @author jane + * + */ +public class MapBuilder { + HashMap roads; + HashMap gates; + HashMap tracks; + + public MapBuilder(){ + roads = new HashMap(); + gates = new HashMap(); + tracks = new HashMap(); + buildRoads(); + buildCrossingGates(); + buildTracks(); + assignGatesToRoads(); + buildCarFactories(); + } + + private void buildRoads(){ + roads.put("Western Highway",new Road(new Point(800,0),new Point (800,1000),Direction.SOUTH,true,false)); + roads.put("Skyway",new Road(new Point(400,0),new Point (400,1000),Direction.SOUTH,true,false)); + roads.put("EastWest",new Road(new Point(415,800),new Point (785,800),Direction.EAST,true,true)); + } + + private void buildCrossingGates(){ + gates.put("Gate1", new CrossingGate(780,480, "Gate1")); + gates.put("Gate2", new CrossingGate(380,480, "Gate2")); + } + + private void buildTracks(){ + tracks.put("Royal", new RailwayTracks(new Point(0,500),new Point(1200,500))); + } + + private void assignGatesToRoads(){ + roads.get("Western Highway").assignGate(gates.get("Gate1")); + roads.get("Skyway").assignGate(gates.get("Gate2")); + } + + private void buildCarFactories(){ + roads.get("Western Highway").addCarFactory(); + roads.get("Skyway").addCarFactory(); + } + + public Collection getAllGates(){ + return gates.values(); + } + + public Collection getTracks(){ + return tracks.values(); + } + + public Collection getRoads(){ + return roads.values(); + } + + public RailwayTracks getTrack(String name){ + return tracks.get("Royal"); + } +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/RailwayTracks.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/RailwayTracks.java new file mode 100644 index 00000000..50822926 --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/RailwayTracks.java @@ -0,0 +1,46 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure; + +import java.awt.Point; + +/** + * Railway Tracks (Entity Object) + * @author Jane Cleland-Huang + * + */ +public class RailwayTracks { + + private int startX; + private int endX; + private int startY; + private int endY; + + public RailwayTracks(){} + + public RailwayTracks(Point startPoint, Point endPoint){ + startX = startPoint.x; + startY = startPoint.y; + endX = endPoint.x; + endY = endPoint.y; + } + + public int getStartX(){ + return startX; + } + + public int getEndX(){ + return endX; + } + + public int getStartY(){ + return startY; + } + + public int getEndY(){ + return endY; + } + + @Override + public String toString(){ + return "Tracks from (" + startX + "," + startY + ") to (" + endX + "," + endY + ")"; + } +} \ No newline at end of file diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/Road.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/Road.java new file mode 100644 index 00000000..2d22448c --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/Road.java @@ -0,0 +1,86 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure; + +import java.awt.Point; +import java.util.Collection; +import java.util.Vector; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.gate.CrossingGate; +import edu.nd.sarec.railwaycrossing.model.vehicles.CarFactory; + +/** + * Represents a single road + * @author jane + * + */ +public class Road { + private int startX; + private int endX; + private int startY; + private int endY; + private CarFactory carFactory; + Direction direction; + Collection gates; + boolean clearEnds = false; + int roadSize; + + public Road(){} + + public Road(Point start, Point end, Direction direction, boolean buildCarFactory, boolean clearEnds){ + startX = start.x; + startY = start.y; + endX = end.x; + endY = end.y; + roadSize = 18; + + this.direction = direction; + gates = new Vector(); + this.clearEnds = clearEnds; + + } + + // Adds a gate to a road + // In case a new gate is added after the factory is assigned, we reassign factory + // The factory needs to know all gates on the road in order to register each car as an observer. + public void assignGate(CrossingGate gate){ + gates.add(gate); + if (carFactory != null) + carFactory = new CarFactory(direction, new Point(startX-roadSize/2,startY), gates); // allows additional gates. Needs fixing + } + + public void addCarFactory(){ + if (carFactory == null) // We only allow one + carFactory = new CarFactory(direction, new Point(startX-roadSize/2,startY), gates); + } + + public CarFactory getCarFactory(){ + return carFactory; + } + + public int getStartX(){ + return startX; + } + + public int getEndX(){ + return endX; + } + + public int getStartY(){ + return startY; + } + + public int getEndY(){ + return endY; + } + + public Direction getDirection(){ + return direction; + } + + public boolean getClearEnds(){ + return clearEnds; + } + + public int getRoadWidth(){ + return roadSize; + } +} \ No newline at end of file diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/CrossingGate.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/CrossingGate.java new file mode 100644 index 00000000..1314ffcf --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/CrossingGate.java @@ -0,0 +1,129 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure.gate; + +import java.util.Observable; +import java.util.Observer; + +import edu.nd.sarec.railwaycrossing.model.vehicles.Train; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Line; + +/** + * Context class for Crossing Gate + * @author jane + * + */ +public class CrossingGate extends Observable implements Observer{ + + // Crossing Gate location and its trigger & exit points + private int anchorX; + private int anchorY; + private int movingX; + private int movingY; + private int triggerPoint; + private int exitPoint; + + private IGateState gateClosed; + private IGateState gateOpen; + private IGateState gateClosing; + private IGateState gateOpening; + private IGateState currentGateState; + private Line line; + private Pane root; + + String gateName; + + public CrossingGate(){} + + public CrossingGate(int xPosition, int yPosition, String crossingGate){ + anchorX = xPosition; + anchorY = yPosition; + movingX = anchorX; + movingY = anchorY-60; + triggerPoint = anchorX+250; + exitPoint = anchorX-250; + + // Gate elements + line = new Line(anchorX, anchorY,movingX,movingY); + line.setStroke(Color.RED); + line.setStrokeWidth(10); + + // Gate States + gateClosed = new GateClosed(this); + gateOpen = new GateOpen(this); + gateOpening = new GateOpening(this); + gateClosing = new GateClosing(this); + currentGateState = gateOpen; + gateName = crossingGate; + } + + public Line getGateLine(){ + return line; + } + + public void operateGate(){ + currentGateState.operate(); + } + + public void close(){ + if (movingYanchorX){ + movingX-=1; + movingY-=1; + line.setStartX(anchorX); + line.setStartY(anchorY); + line.setEndX(movingX); + line.setEndY(movingY); + } else { + currentGateState.gateFinishedOpening(); + } + } + + // State getters and setters + public IGateState getGateClosedState(){ + return gateClosed; + } + public IGateState getGateOpenState(){ + return gateOpen; + } + public IGateState getGateClosingState(){ + return gateClosing; + } + public IGateState getGateOpeningState(){ + return gateOpening; + } + + public void setGateState(IGateState newState){ + currentGateState = newState; + setChanged(); + notifyObservers(); + } + + public String getTrafficCommand(){ + return currentGateState.getTrafficAction(); + } + + @Override + public void update(Observable o, Object arg) { + if (o instanceof Train){ + Train train = (Train)o; + if (train.getVehicleX() < exitPoint) + currentGateState.leaveStation(); + else if(train.getVehicleX() < triggerPoint){ + currentGateState.approachStation(); + } + } + } +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateClosed.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateClosed.java new file mode 100644 index 00000000..0407e47b --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateClosed.java @@ -0,0 +1,49 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure.gate; + +/** + * Gate in CLOSED state + * @author jane + * + */ +public class GateClosed implements IGateState { + + private CrossingGate gate; + + protected GateClosed(CrossingGate gate){ + this.gate = gate; + } + + @Override + public void approachStation() { + // Do nothing. Gate is already closed. + // IF there were two tracks we would have to keep track of how many trains were in the station! + } + + @Override + public void leaveStation() { + gate.setGateState(gate.getGateOpeningState()); + } + + @Override + public void gateFinishedOpening() { + // not applicable + } + + @Override + public void gateFinishedClosing() { + // not applicable. Gate is already closed. + } + + @Override + public void operate() { + // Flash lights + + } + + @Override + public String getTrafficAction() { + return "STOP"; + } + + +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateClosing.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateClosing.java new file mode 100644 index 00000000..dd97ed3a --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateClosing.java @@ -0,0 +1,50 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure.gate; + +/** + * Gate in closing state + * @author jane + * + */ +public class GateClosing implements IGateState{ + + private CrossingGate gate; + + protected GateClosing(CrossingGate gate){ + this.gate = gate; + } + + @Override + public void approachStation() { + // Gate is already closing + } + + @Override + public void leaveStation() { + // This was an unwanted event. The gate wasn't fully closed when the train was in the station. + // Nevertheless we will open the gate. + gate.setGateState(gate.getGateOpeningState()); + } + + @Override + public void gateFinishedOpening() { + // n/a + } + + @Override + public void gateFinishedClosing() { + gate.setGateState(gate.getGateClosedState()); + } + + @Override + public void operate() { + gate.close(); + // flash lights + } + + @Override + public String getTrafficAction() { + return "STOP"; + } + + +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateOpen.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateOpen.java new file mode 100644 index 00000000..14c6666b --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateOpen.java @@ -0,0 +1,46 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure.gate; + +/** + * Gate in open state + * @author jane + * + */ +public class GateOpen implements IGateState { + + private CrossingGate gate; + + protected GateOpen(CrossingGate gate){ + this.gate = gate; + } + + @Override + public void approachStation() { + gate.setGateState(gate.getGateClosingState()); + } + + @Override + public void leaveStation() { + // n/a gate already open + } + + @Override + public void gateFinishedOpening() { + // n/a gate already open + } + + @Override + public void gateFinishedClosing() { + // n/a can't be closing and opened. + } + + @Override + public void operate() { + // Normal operation. Do nothing. + } + + @Override + public String getTrafficAction() { + return "GO"; + } + +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateOpening.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateOpening.java new file mode 100644 index 00000000..b396e63a --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/GateOpening.java @@ -0,0 +1,48 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure.gate; + +/** + * Gate in opening state + * @author jane + * + */ +public class GateOpening implements IGateState{ + + CrossingGate gate; + + protected GateOpening(CrossingGate gate){ + this.gate = gate; + } + + @Override + public void approachStation() { + gate.setGateState(gate.getGateClosingState()); + } + + @Override + public void leaveStation() { + // Already opening. + } + + @Override + public void gateFinishedOpening() { + gate.setGateState(gate.getGateOpenState()); + } + + @Override + public void gateFinishedClosing() { + // not reachable except through error. + // Raise an alarm!! + } + + @Override + public void operate() { + gate.open(); + // Flash lights.. + } + + @Override + public String getTrafficAction() { + return "STOP"; + } + +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/IGateState.java b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/IGateState.java new file mode 100644 index 00000000..c221c20a --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/infrastructure/gate/IGateState.java @@ -0,0 +1,15 @@ +package edu.nd.sarec.railwaycrossing.model.infrastructure.gate; + +/** + * Declares all operations that GateState classes must implement + * @author jane + * + */ +public interface IGateState { + public void approachStation(); + public void leaveStation(); + public void gateFinishedOpening(); + public void gateFinishedClosing(); + public void operate(); + public String getTrafficAction(); +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/vehicles/Car.java b/src/edu/nd/sarec/railwaycrossing/model/vehicles/Car.java new file mode 100644 index 00000000..3f3073cb --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/vehicles/Car.java @@ -0,0 +1,118 @@ +package edu.nd.sarec.railwaycrossing.model.vehicles; + +import java.util.Observable; +import java.util.Observer; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.gate.CrossingGate; +import edu.nd.sarec.railwaycrossing.view.CarImageSelector; +import javafx.scene.Node; +import javafx.scene.image.ImageView; + +/** + * Represents Car object + * @author jane + * + */ +public class Car extends Observable implements IVehicle, Observer{ + private ImageView ivCar; + private double currentX = 0; + private double currentY = 0; + private double originalY = 0; + private boolean gateDown = false; + private double leadCarY = -1; // Current Y position of car directly infront of this one + private double speed = 0.5; + + /** + * Constructor + * @param x initial x coordinate of car + * @param y initial y coordinate of car + */ + public Car(int x, int y){ + this.currentX = x; + this.currentY = y; + originalY = y; + ivCar = new ImageView(CarImageSelector.getImage()); + ivCar.setX(getVehicleX()); + ivCar.setY(getVehicleY()); + } + + @Override + public Node getImageView() { + return ivCar; + } + + public boolean gateIsClosed(){ + return gateDown; + } + + public double getVehicleX(){ + return currentX; + } + public double getVehicleY(){ + return currentY; + } + + public void move(){ + boolean canMove = true; + + // First case. Car is at the front of the stopping line. + if (gateDown && getVehicleY() < 430 && getVehicleY()> 390) + canMove = false; + + // Second case. Car is too close too other car. + if (leadCarY != -1 && getDistanceToLeadCar() < 50) + canMove = false; + + if (canMove){ + currentY+=speed; + ivCar.setY(currentY); + setChanged(); + notifyObservers(); + } + } + + public void setSpeed(double speed){ + this.speed = speed; + } + + public void setGateDownFlag(boolean gateDown){ + this.gateDown = gateDown; + } + + public boolean offScreen(){ + if (currentY > 1020) + return true; + else + return false; + } + + public void reset(){ + currentY = originalY; + } + + public double getDistanceToLeadCar(){ + return Math.abs(leadCarY-getVehicleY()); + } + + public void removeLeadCar(){ + leadCarY = -1; + } + + @Override + public void update(Observable o, Object arg1) { + if (o instanceof Car){ + leadCarY = (((Car)o).getVehicleY()); + if (leadCarY > 1020) + leadCarY = -1; + } + + if (o instanceof CrossingGate){ + CrossingGate gate = (CrossingGate)o; + if(gate.getTrafficCommand()=="STOP") + gateDown = true; + else + gateDown = false; + + } + } +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/vehicles/CarFactory.java b/src/edu/nd/sarec/railwaycrossing/model/vehicles/CarFactory.java new file mode 100644 index 00000000..1eae29ed --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/vehicles/CarFactory.java @@ -0,0 +1,75 @@ +package edu.nd.sarec.railwaycrossing.model.vehicles; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.Collection; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.Direction; +import edu.nd.sarec.railwaycrossing.model.infrastructure.gate.CrossingGate; + + +/** + * Very basic car factory. Creates the car and registers it with the crossing gate and the car infront of it. + * @author jane + * + */ +public class CarFactory { + + private Collection gates = null; + private Car previousCar = null; + private ArrayList cars = new ArrayList(); + Direction direction; + Point location; + + public CarFactory(){} + + public CarFactory(Direction direction, Point location, Collection gates){ + this.direction = direction; + this.location = location; + this.gates = gates; + } + + + // Most code here is to create random speeds + public Car buildCar(){ + if (previousCar == null || location.y < previousCar.getVehicleY()-100){ + Car car = new Car(location.x,location.y); + double speedVariable = (Math.random() * 10)/10; + car.setSpeed((2-speedVariable)*1.5); + + // All cars created by this factory must be aware of crossing gates in the road + for(CrossingGate gate: gates){ + gate.addObserver(car); + if(gate != null && gate.getTrafficCommand()=="STOP") + car.setGateDownFlag(false); + } + + // Each car must observe the car infront of it so it doesn't collide with it. + if (previousCar != null) + previousCar.addObserver(car); + previousCar = car; + + cars.add(car); + return car; + } else + return null; + } + + // We will get a concurrency error if we try to delete cars whilst iterating through the array list + // so we perform this in two stages. + // 1. Loop through the list and identify which cars are off the screen. Add them to 'toDelete' array. + // 2. Iterate through toDelete and remove the cars from the original arrayList. + public ArrayList removeOffScreenCars() { + // Removing cars from the array list. + ArrayList toDelete = new ArrayList(); + for(Car car: cars){ + car.move(); + if (car.offScreen()) + toDelete.add(car); + + } + for (Car car: toDelete) + cars.remove(car); + return toDelete; + } +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/vehicles/IVehicle.java b/src/edu/nd/sarec/railwaycrossing/model/vehicles/IVehicle.java new file mode 100644 index 00000000..ad739705 --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/vehicles/IVehicle.java @@ -0,0 +1,13 @@ +package edu.nd.sarec.railwaycrossing.model.vehicles; + +import javafx.scene.Node; + + +public interface IVehicle { + public Node getImageView(); + public double getVehicleX(); + public double getVehicleY(); + public void move(); + public boolean offScreen(); + public void reset(); +} diff --git a/src/edu/nd/sarec/railwaycrossing/model/vehicles/Train.java b/src/edu/nd/sarec/railwaycrossing/model/vehicles/Train.java new file mode 100644 index 00000000..9265466d --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/model/vehicles/Train.java @@ -0,0 +1,62 @@ +package edu.nd.sarec.railwaycrossing.model.vehicles; + +import java.util.Observable; + +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +/** + * Represents the train entity object + * @author jane + * + */ +public class Train extends Observable implements IVehicle{ + private double currentX = 0; + private double currentY = 0; + private double originalX = 0; + private Image img; + private ImageView imgView; + private int trainLength = 35; + + public Train(int x, int y){ + this.currentX = x; + this.currentY = y; + originalX = x; + img = new Image("images\\Train.PNG",120,trainLength,false,false); + imgView = new ImageView(img); + imgView.setX(currentX); + imgView.setY(currentY); + } + + public double getVehicleX(){ + return currentX; + } + + public double getVehicleY(){ + return currentY; + } + + public void move(){ + currentX-=2; + imgView.setX(currentX); + setChanged(); + notifyObservers(); + } + + public boolean offScreen(){ + if (currentX < -200) + return true; + else + return false; + } + + public void reset(){ + currentX = originalX; + } + + //@Override + public Node getImageView() { + return imgView; + } +} \ No newline at end of file diff --git a/src/edu/nd/sarec/railwaycrossing/view/CarImageSelector.java b/src/edu/nd/sarec/railwaycrossing/view/CarImageSelector.java new file mode 100644 index 00000000..f841371c --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/view/CarImageSelector.java @@ -0,0 +1,23 @@ +package edu.nd.sarec.railwaycrossing.view; + +import javafx.scene.image.Image; + +public class CarImageSelector { + + + public static Image getImage(){ + int imageSize = 20; + int pickNum = (int)(Math.random() * 4); + Image img; + switch(pickNum){ + case 0: img = new Image("images\\bluecar.PNG",imageSize,imageSize,false,true); + break; + case 2: img = new Image("images\\graycar.PNG",imageSize,imageSize,false,true); + break; + case 3: img = new Image("images\\greencar.PNG",imageSize,imageSize,false,true); + break; + default: img = new Image("images\\redcar.PNG",imageSize,imageSize,false,true); + } + return img; + } +} \ No newline at end of file diff --git a/src/edu/nd/sarec/railwaycrossing/view/IDisplay.java b/src/edu/nd/sarec/railwaycrossing/view/IDisplay.java new file mode 100644 index 00000000..0e99f9dc --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/view/IDisplay.java @@ -0,0 +1,5 @@ +package edu.nd.sarec.railwaycrossing.view; + +public interface IDisplay { + public void draw(); +} diff --git a/src/edu/nd/sarec/railwaycrossing/view/MapDisplay.java b/src/edu/nd/sarec/railwaycrossing/view/MapDisplay.java new file mode 100644 index 00000000..0726aa3c --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/view/MapDisplay.java @@ -0,0 +1,53 @@ +package edu.nd.sarec.railwaycrossing.view; + +import java.util.ArrayList; +import java.util.Collection; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.RailwayTracks; +import edu.nd.sarec.railwaycrossing.model.infrastructure.Road; +import edu.nd.sarec.railwaycrossing.model.infrastructure.gate.CrossingGate; +import edu.nd.sarec.railwaycrossing.model.vehicles.Car; +import javafx.scene.layout.Pane; + +/** + * Called by JavaFX main UI thread to help display elements on the UI + * @author jane + * + */ +public class MapDisplay { + Pane root; + IDisplay roadDisplay, tracksDisplay; + Collection roads; + Collection track; + Collection gates; + + + public MapDisplay(Pane root, Collection roads, Collection tracks, Collection gates){ + this.root = root; + this.roads= roads; + this.track = tracks; + this.gates = gates; + roadDisplay = new RoadDisplay(roads,root); + tracksDisplay = new TracksDisplay(tracks,root); + } + + public void drawTracks(){ + tracksDisplay.draw(); + } + + public void drawRoad(){ + roadDisplay.draw(); + } + + public void drawGate(){ + for (CrossingGate gate: gates) + root.getChildren().add(gate.getGateLine()); + } + + + public void removeCarImages(ArrayList junkCars) { + for(Car car: junkCars) + root.getChildren().remove(car.getImageView()); + + } +} diff --git a/src/edu/nd/sarec/railwaycrossing/view/RoadDisplay.java b/src/edu/nd/sarec/railwaycrossing/view/RoadDisplay.java new file mode 100644 index 00000000..208de87a --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/view/RoadDisplay.java @@ -0,0 +1,51 @@ +package edu.nd.sarec.railwaycrossing.view; + +import java.util.Collection; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.Direction; +import edu.nd.sarec.railwaycrossing.model.infrastructure.Road; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Line; + + +/** + * Draws a road + * @author jane + * + */ +public class RoadDisplay implements IDisplay { + Pane root; + Collection roads; + int roadSize; + + + public RoadDisplay(Collection roads, Pane root){ + this.root = root; + this.roads = roads; + + } + + @Override + public void draw() { + for(Road road: roads){ + roadSize = road.getRoadWidth(); + if (road.getDirection() == Direction.NORTH || road.getDirection() == Direction.SOUTH) { + root.getChildren().add(new Line(road.getStartX()-roadSize,road.getStartY(),road.getEndX()-roadSize,road.getEndY())); + root.getChildren().add(new Line(road.getStartX()+roadSize,road.getStartY(),road.getEndX()+roadSize,road.getEndY())); + } else { + root.getChildren().add(new Line(road.getStartX(),road.getStartY()-roadSize,road.getEndX(),road.getEndY()-roadSize)); + root.getChildren().add(new Line(road.getStartX(),road.getStartY()+roadSize,road.getEndX(),road.getEndY()+roadSize)); + if (road.getClearEnds()){ + Line line = new Line(road.getStartX(),road.getStartY()-roadSize,road.getStartX(),road.getStartY()+roadSize); + line.setStroke(Color.WHITE); + root.getChildren().add(line); + Line line2 = new Line(road.getEndX(),road.getEndY()-roadSize,road.getEndX(),road.getEndY()+roadSize); + line2.setStroke(Color.WHITE); + root.getChildren().add(line2); + } + } + } + } +} + diff --git a/src/edu/nd/sarec/railwaycrossing/view/TracksDisplay.java b/src/edu/nd/sarec/railwaycrossing/view/TracksDisplay.java new file mode 100644 index 00000000..88ca2cf1 --- /dev/null +++ b/src/edu/nd/sarec/railwaycrossing/view/TracksDisplay.java @@ -0,0 +1,35 @@ +package edu.nd.sarec.railwaycrossing.view; + +import java.util.Collection; + +import edu.nd.sarec.railwaycrossing.model.infrastructure.RailwayTracks; +import javafx.scene.layout.Pane; +import javafx.scene.shape.Line; + + +/** + * Draws a track + * @author jane + * + */ +public class TracksDisplay implements IDisplay { + Pane root; + Collection tracks; + int trackSize = 16; + + public TracksDisplay(Collection tracks, Pane root){ + this.root = root; + this.tracks = tracks; + } + + @Override + public void draw() { + for(RailwayTracks track: tracks){ + root.getChildren().add(new Line(track.getStartX(),track.getStartY()-trackSize,track.getEndX(),track.getEndY()-trackSize)); + root.getChildren().add(new Line(track.getStartX(),track.getStartY()+trackSize,track.getEndX(),track.getEndY()+trackSize)); + for(int j = track.getStartX()+(trackSize/2); j < track.getEndX(); j+=trackSize){ + root.getChildren().add(new Line(j,track.getStartY()-trackSize - 2,j,track.getEndY()+trackSize+2)); + } + } + } +} diff --git a/src/edu/nd/se2018/examples/animationtimer/AnimationTimerTest.java b/src/edu/nd/se2018/examples/animationtimer/AnimationTimerTest.java new file mode 100644 index 00000000..9964b559 --- /dev/null +++ b/src/edu/nd/se2018/examples/animationtimer/AnimationTimerTest.java @@ -0,0 +1,390 @@ +package edu.nd.se2018.examples.animationtimer; +import static java.lang.Math.PI; +import static java.lang.Math.cos; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; +import static javafx.scene.paint.Color.BLACK; +import static javafx.scene.paint.Color.BLUE; +import static javafx.scene.paint.Color.BROWN; +import static javafx.scene.paint.Color.GREEN; +import static javafx.scene.paint.Color.PINK; +import static javafx.scene.paint.Color.RED; +import static javafx.scene.paint.Color.YELLOW; + +import java.util.ListIterator; +import java.util.Random; +import java.util.concurrent.Callable; + +import javafx.animation.AnimationTimer; +import javafx.application.Application; +import javafx.beans.binding.Bindings; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.ReadOnlyDoubleProperty; +import javafx.beans.property.ReadOnlyDoubleWrapper; +import javafx.beans.property.ReadOnlyStringProperty; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Shape; +import javafx.stage.Stage; + +public class AnimationTimerTest extends Application { + + private ObservableList balls = FXCollections.observableArrayList(); + private static final int NUM_BALLS = 100; + private static final double MIN_RADIUS = 5 ; + private static final double MAX_RADIUS = 15 ; + private static final double MIN_SPEED = 50 ; + private static final double MAX_SPEED = 250 ; + private static final Color[] COLORS = new Color[] { RED, YELLOW, GREEN, + BROWN, BLUE, PINK, BLACK }; + + private final FrameStats frameStats = new FrameStats() ; + + @Override + public void start(Stage primaryStage) { + final Pane ballContainer = new Pane(); + + constrainBallsOnResize(ballContainer); + + ballContainer.addEventHandler(MouseEvent.MOUSE_CLICKED, + new EventHandler() { + @Override + public void handle(MouseEvent event) { + if (event.getClickCount() == 2) { + balls.clear(); + createBalls(NUM_BALLS, MIN_RADIUS, MAX_RADIUS, MIN_SPEED, MAX_SPEED, ballContainer.getWidth()/2, ballContainer.getHeight()/2); + } + } + }); + + balls.addListener(new ListChangeListener() { + @Override + public void onChanged(Change change) { + while (change.next()) { + for (Ball b : change.getAddedSubList()) { + ballContainer.getChildren().add(b.getView()); + } + for (Ball b : change.getRemoved()) { + ballContainer.getChildren().remove(b.getView()); + } + } + } + }); + + createBalls(NUM_BALLS, MIN_RADIUS, MAX_RADIUS, MIN_SPEED, MAX_SPEED, 400, 300); + + final BorderPane root = new BorderPane(); + final Label stats = new Label() ; + stats.textProperty().bind(frameStats.textProperty()); + + root.setCenter(ballContainer); + root.setBottom(stats); + + final Scene scene = new Scene(root, 800, 600); + primaryStage.setScene(scene); + primaryStage.show(); + + startAnimation(ballContainer); + } + + private void startAnimation(final Pane ballContainer) { + final LongProperty lastUpdateTime = new SimpleLongProperty(0); + final AnimationTimer timer = new AnimationTimer() { + @Override + public void handle(long timestamp) { + if (lastUpdateTime.get() > 0) { + long elapsedTime = timestamp - lastUpdateTime.get(); + checkCollisions(ballContainer.getWidth(), ballContainer.getHeight()); + updateWorld(elapsedTime); + frameStats.addFrame(elapsedTime); + } + lastUpdateTime.set(timestamp); + } + + }; + timer.start(); + } + + private void updateWorld(long elapsedTime) { + double elapsedSeconds = elapsedTime / 1_000_000_000.0; + for (Ball b : balls) { + b.setCenterX(b.getCenterX() + elapsedSeconds * b.getXVelocity()); + b.setCenterY(b.getCenterY() + elapsedSeconds * b.getYVelocity()); + } + } + + private void checkCollisions(double maxX, double maxY) { + for (ListIterator slowIt = balls.listIterator(); slowIt.hasNext();) { + Ball b1 = slowIt.next(); + // check wall collisions: + double xVel = b1.getXVelocity(); + double yVel = b1.getYVelocity(); + if ((b1.getCenterX() - b1.getRadius() <= 0 && xVel < 0) + || (b1.getCenterX() + b1.getRadius() >= maxX && xVel > 0)) { + b1.setXVelocity(-xVel); + } + if ((b1.getCenterY() - b1.getRadius() <= 0 && yVel < 0) + || (b1.getCenterY() + b1.getRadius() >= maxY && yVel > 0)) { + b1.setYVelocity(-yVel); + } + for (ListIterator fastIt = balls.listIterator(slowIt.nextIndex()); fastIt.hasNext();) { + Ball b2 = fastIt.next(); + // performance hack: both colliding(...) and bounce(...) need deltaX and deltaY, so compute them once here: + final double deltaX = b2.getCenterX() - b1.getCenterX() ; + final double deltaY = b2.getCenterY() - b1.getCenterY() ; + if (colliding(b1, b2, deltaX, deltaY)) { + bounce(b1, b2, deltaX, deltaY); + } + } + } + } + + + public boolean colliding(final Ball b1, final Ball b2, final double deltaX, final double deltaY) { + // square of distance between balls is s^2 = (x2-x1)^2 + (y2-y1)^2 + // balls are "overlapping" if s^2 < (r1 + r2)^2 + // We also check that distance is decreasing, i.e. + // d/dt(s^2) < 0: + // 2(x2-x1)(x2'-x1') + 2(y2-y1)(y2'-y1') < 0 + + final double radiusSum = b1.getRadius() + b2.getRadius(); + if (deltaX * deltaX + deltaY * deltaY <= radiusSum * radiusSum) { + if ( deltaX * (b2.getXVelocity() - b1.getXVelocity()) + + deltaY * (b2.getYVelocity() - b1.getYVelocity()) < 0) { + return true; + } + } + return false; + } + + private void bounce(final Ball b1, final Ball b2, final double deltaX, final double deltaY) { + final double distance = sqrt(deltaX * deltaX + deltaY * deltaY) ; + final double unitContactX = deltaX / distance ; + final double unitContactY = deltaY / distance ; + + final double xVelocity1 = b1.getXVelocity(); + final double yVelocity1 = b1.getYVelocity(); + final double xVelocity2 = b2.getXVelocity(); + final double yVelocity2 = b2.getYVelocity(); + + final double u1 = xVelocity1 * unitContactX + yVelocity1 * unitContactY ; // velocity of ball 1 parallel to contact vector + final double u2 = xVelocity2 * unitContactX + yVelocity2 * unitContactY ; // same for ball 2 + + final double massSum = b1.getMass() + b2.getMass() ; + final double massDiff = b1.getMass() - b2.getMass() ; + + final double v1 = ( 2*b2.getMass()*u2 + u1 * massDiff ) / massSum ; // These equations are derived for one-dimensional collision by + final double v2 = ( 2*b1.getMass()*u1 - u2 * massDiff ) / massSum ; // solving equations for conservation of momentum and conservation of energy + + final double u1PerpX = xVelocity1 - u1 * unitContactX ; // Components of ball 1 velocity in direction perpendicular + final double u1PerpY = yVelocity1 - u1 * unitContactY ; // to contact vector. This doesn't change with collision + final double u2PerpX = xVelocity2 - u2 * unitContactX ; // Same for ball 2.... + final double u2PerpY = yVelocity2 - u2 * unitContactY ; + + b1.setXVelocity(v1 * unitContactX + u1PerpX); + b1.setYVelocity(v1 * unitContactY + u1PerpY); + b2.setXVelocity(v2 * unitContactX + u2PerpX); + b2.setYVelocity(v2 * unitContactY + u2PerpY); + + } + + private void createBalls(int numBalls, double minRadius, double maxRadius, double minSpeed, double maxSpeed, double initialX, double initialY) { + final Random rng = new Random(); + for (int i = 0; i < numBalls; i++) { + double radius = minRadius + (maxRadius-minRadius) * rng.nextDouble(); + double mass = Math.pow((radius / 40), 3); + + final double speed = minSpeed + (maxSpeed - minSpeed) * rng.nextDouble(); + final double angle = 2 * PI * rng.nextDouble(); + Ball ball = new Ball(initialX, initialY, radius, speed*cos(angle), + speed*sin(angle), mass); + ball.getView().setFill(COLORS[i % COLORS.length]); +// ball.getView().setFill(i==0 ? RED : TRANSPARENT); + balls.add(ball); + } + } + + private void constrainBallsOnResize(final Pane ballContainer) { + ballContainer.widthProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, + Number oldValue, Number newValue) { + if (newValue.doubleValue() < oldValue.doubleValue()) { + for (Ball b : balls) { + double max = newValue.doubleValue() - b.getRadius(); + if (b.getCenterX() > max) { + b.setCenterX(max); + } + } + } + } + }); + + ballContainer.heightProperty().addListener(new ChangeListener() { + + @Override + public void changed(ObservableValue observable, + Number oldValue, Number newValue) { + if (newValue.doubleValue() < oldValue.doubleValue()) { + for (Ball b : balls) { + double max = newValue.doubleValue() - b.getRadius(); + if (b.getCenterY() > max) { + b.setCenterY(max); + } + } + } + } + + }); + } + + private static class Ball { + private final DoubleProperty xVelocity ; // pixels per second + private final DoubleProperty yVelocity ; + private final ReadOnlyDoubleWrapper speed ; + private final double mass; // arbitrary units + private final double radius; // pixels + + private final Circle view; + + public Ball(double centerX, double centerY, double radius, + double xVelocity, double yVelocity, double mass) { + + this.view = new Circle(centerX, centerY, radius); + this.xVelocity = new SimpleDoubleProperty(this, "xVelocity", xVelocity); + this.yVelocity = new SimpleDoubleProperty(this, "yVelocity", yVelocity); + this.speed = new ReadOnlyDoubleWrapper(this, "speed"); + speed.bind(Bindings.createDoubleBinding(new Callable() { + + @Override + public Double call() throws Exception { + final double xVel = getXVelocity(); + final double yVel = getYVelocity(); + return sqrt(xVel * xVel + yVel * yVel); + } + }, this.xVelocity, this.yVelocity)); + this.mass = mass; + this.radius = radius; + view.setRadius(radius); + } + + public double getMass() { + return mass; + } + + public double getRadius() { + return radius; + } + + public final double getXVelocity() { + return xVelocity.get(); + } + + public final void setXVelocity(double xVelocity) { + this.xVelocity.set(xVelocity); + } + + public final DoubleProperty xVelocityProperty() { + return xVelocity; + } + + public final double getYVelocity() { + return yVelocity.get(); + } + + public final void setYVelocity(double yVelocity) { + this.yVelocity.set(yVelocity); + } + + public final DoubleProperty yVelocityProperty() { + return yVelocity; + } + + public final double getSpeed() { + return speed.get(); + } + + public final ReadOnlyDoubleProperty speedProperty() { + return speed.getReadOnlyProperty() ; + } + + public final double getCenterX() { + return view.getCenterX(); + } + + public final void setCenterX(double centerX) { + view.setCenterX(centerX); + } + + public final DoubleProperty centerXProperty() { + return view.centerXProperty(); + } + + public final double getCenterY() { + return view.getCenterY(); + } + + public final void setCenterY(double centerY) { + view.setCenterY(centerY); + } + + public final DoubleProperty centerYProperty() { + return view.centerYProperty(); + } + + public Shape getView() { + return view; + } + } + + private static class FrameStats { + private long frameCount ; + private double meanFrameInterval ; // millis + private final ReadOnlyStringWrapper text = new ReadOnlyStringWrapper(this, "text", "Frame count: 0 Average frame interval: N/A"); + + public long getFrameCount() { + return frameCount; + } + public double getMeanFrameInterval() { + return meanFrameInterval; + } + + public void addFrame(long frameDurationNanos) { + meanFrameInterval = (meanFrameInterval * frameCount + frameDurationNanos / 1_000_000.0) / (frameCount + 1) ; + frameCount++ ; + text.set(toString()); + } + + public String getText() { + return text.get(); + } + + public ReadOnlyStringProperty textProperty() { + return text.getReadOnlyProperty() ; + } + + @Override + public String toString() { + return String.format("Frame count: %,d Average frame interval: %.3f milliseconds", getFrameCount(), getMeanFrameInterval()); + } + } + + public static void main(String[] args) { + launch(args); + } +} \ No newline at end of file diff --git a/src/edu/nd/se2018/examples/mazeCode/Maze.java b/src/edu/nd/se2018/examples/mazeCode/Maze.java new file mode 100644 index 00000000..a055c682 --- /dev/null +++ b/src/edu/nd/se2018/examples/mazeCode/Maze.java @@ -0,0 +1,135 @@ +/* + * Code for generating the maze is taken and modified from: + * ?? Searching for the source to attribute correct...!! + ****************************************************************************** + */ + +package edu.nd.se2018.examples.mazeCode; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.Random; + +import javafx.scene.shape.Line; + +public class Maze { + private int N; + boolean[][] north; // true if a wall exists to north of cell i,j + boolean[][] east; + boolean[][] south; + boolean[][] west; + private boolean[][] visited; + private ArrayList lines = new ArrayList(); + Point winningCell; + + public Maze (int mazeSize){ + N = mazeSize; + initMaze(); + generate(1,1); + buildLines(); + } + + // Stores lines into ArrayList. No scaling. + private void setLine(int topLeftX, int topLeftY, int bottomRightX, int bottomRightY){ + Line line = new Line(); + line.setStartX(topLeftX); + line.setStartY(topLeftY); + line.setEndX(bottomRightX); + line.setEndY(bottomRightY); + lines.add(line); + } + + // Returns the array list of lines + // These will need to be scaled by the client + public ArrayList getLines(){ + return lines; + } + + private void initMaze() { + // initialize border cells as already visited + visited = new boolean[N+2][N+2]; + for (int x = 0; x < N+2; x++) { + visited[x][0] = true; + visited[x][N+1] = true; + } + for (int y = 0; y < N+2; y++) { + visited[0][y] = true; + visited[N+1][y] = true; + } + + // initialize all walls as present + north = new boolean[N+2][N+2]; + east = new boolean[N+2][N+2]; + south = new boolean[N+2][N+2]; + west = new boolean[N+2][N+2]; + for (int x = 0; x < N+2; x++) { + for (int y = 0; y < N+2; y++) { + north[x][y] = true; + east[x][y] = true; + south[x][y] = true; + west[x][y] = true; + } + } + } + + // generate the maze + private void generate(int x, int y) { + + visited[x][y] = true; + Random rand = new Random(); + + // while there is an unvisited neighbor + while (!visited[x][y+1] || !visited[x+1][y] || !visited[x][y-1] || !visited[x-1][y]) { + + // pick random neighbor + while (true) { + double r = rand.nextInt(4); + if (r == 0 && !visited[x][y+1]) { + north[x][y] = false; + south[x][y+1] = false; + generate(x, y + 1); + break; + } + else if (r == 1 && !visited[x+1][y]) { + east[x][y] = false; + west[x+1][y] = false; + generate(x+1, y); + break; + } + else if (r == 2 && !visited[x][y-1]) { + south[x][y] = false; + north[x][y-1] = false; + generate(x, y-1); + break; + } + else if (r == 3 && !visited[x-1][y]) { + west[x][y] = false; + east[x-1][y] = false; + generate(x-1, y); + break; + } + } + } + } + + // draw the maze + public void buildLines() { + + for (int x = 1; x <= N; x++) { + for (int y = 1; y <= N; y++) { + if (south[x][y]) setLine(x, y, x + 1, y); + if (north[x][y]) setLine(x, y + 1, x + 1, y + 1); + if (west[x][y]) setLine(x, y, x, y + 1); + if (east[x][y]) setLine(x + 1, y, x + 1, y + 1); + } + } + } + + public Point getTargetPoint(){ + return new Point(N/2,N/2); + } + + public Point getStartPoint(){ + return new Point(1,1); + } +} diff --git a/src/edu/nd/se2018/examples/mazeCode/MazeGame.java b/src/edu/nd/se2018/examples/mazeCode/MazeGame.java new file mode 100644 index 00000000..6de8d6df --- /dev/null +++ b/src/edu/nd/se2018/examples/mazeCode/MazeGame.java @@ -0,0 +1,142 @@ + +package edu.nd.se2018.examples.mazeCode; + +import java.awt.Point; +import java.util.ArrayList; + +import javafx.application.Application; +import javafx.event.EventHandler; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Line; +import javafx.stage.Stage; + +// Maze Game +public class MazeGame extends Application{ + + final private int cellSize = 20; + final private int mazeSize = 12; + boolean foundTarget = false; + Scene scene; + ImageView seekImageView, foundImageView; + Image seekImage, foundImage; + + Seeker seeker; + Circle target; + Maze maze; + Pane root; + Point targetPoint; + + @Override + public void start(Stage stage) throws Exception { + + // Generate the Maze + maze = new Maze(mazeSize); + targetPoint = maze.getTargetPoint(); + Point startPosition = maze.getStartPoint(); + + // Create the seeker + seeker = new Seeker(startPosition.x, startPosition.y,cellSize,maze); + seeker.setColor(Color.BLUE); + + // Add maze lines, seeker, and images to view + root = new Pane(); + root.getChildren().add(seeker.getCircle()); + drawLines(maze.getLines()); + loadImages(); + + // Setup + scene = new Scene(root,mazeSize*cellSize + cellSize*2,mazeSize*cellSize + cellSize*2); + stage.setTitle("aMAZing Fun"); + stage.setScene(scene); + stage.show(); + + // Start play + startGame(); + } + + private void startGame(){ + // Create a keypressed handler + scene.setOnKeyPressed(new EventHandler(){ + + @Override + public void handle(KeyEvent ke) { + if(!foundTarget) + // Delegate movement decisions and actions to the seeker + switch(ke.getCode()){ + case RIGHT: + seeker.moveRight(); + break; + case LEFT: + seeker.moveLeft(); + break; + case DOWN: + seeker.moveDown(); + break; + case UP: + seeker.moveUp(); + break; + default: + break; + } + + // Check for "end of game" -- Target found! + if(seeker.getCurrentLocation().equals(maze.getTargetPoint())){ + setFoundTargetImage(); + foundTarget = true; + } + } + }); + } + + protected void setFoundTargetImage(){ + if(root.getChildren().contains(seekImageView)){ + root.getChildren().remove(seekImageView); + root.getChildren().add(foundImageView); + } + } + + public Scene getScene(){ + return scene; + } + + private void loadImages(){ + + seekImage = new Image("images\\target.png",cellSize, cellSize, false, true); + seekImageView = new ImageView(seekImage); + seekImageView.setX(maze.getTargetPoint().x*cellSize); + seekImageView.setY(maze.getTargetPoint().y*cellSize); + + // Load "found" target image + foundImage = new Image("images\\win.gif",cellSize, cellSize, false, true); + foundImageView = new ImageView(foundImage); + foundImageView.setX(maze.getTargetPoint().x*cellSize); + foundImageView.setY(maze.getTargetPoint().y*cellSize); + + // set current target image to "not found yet" image + root.getChildren().add(seekImageView); + } + + // Get lines from the maze and draw them according to current scale + public void drawLines(ArrayList lines){ + for(Line line: lines){ + line.setStartX(line.getStartX()*cellSize); + line.setStartY(line.getStartY()*cellSize); + line.setEndX(line.getEndX()*cellSize); + line.setEndY(line.getEndY()*cellSize); + root.getChildren().add(line); + System.out.println("Stop here"); + } + } + + public static void main(String[] args) { + launch(args); + } + +} + diff --git a/src/edu/nd/se2018/examples/mazeCode/Seeker.java b/src/edu/nd/se2018/examples/mazeCode/Seeker.java new file mode 100644 index 00000000..1f77b6fb --- /dev/null +++ b/src/edu/nd/se2018/examples/mazeCode/Seeker.java @@ -0,0 +1,77 @@ +package edu.nd.se2018.examples.mazeCode; + +import java.awt.Point; + +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +public class Seeker { + Circle circle; + int xCell; + int yCell; + int unitSize; // Scaling factor + Maze maze; + + public Seeker(int x, int y, int unitSize, Maze maze){ + xCell = x; + yCell = y; + this.unitSize = unitSize; + circle = new Circle(unitSize/2); + circle.setCenterX(x*unitSize+unitSize/2); + circle.setCenterY(y*unitSize+unitSize/2); + //circle = new Circle(x*unitSize+unitSize/2, y*unitSize+unitSize/2, unitSize/2); + this.maze = maze; // We need a reference to it so we can access the grids! + } + + public void setColor(Color color){ + circle.setFill(color); + } + + private void setCircleX(int x){ + circle.setCenterX(computeDisplayPosition(x)); + } + + private void setCircleY(int y){ + circle.setCenterY(computeDisplayPosition(y)); + } + + private int computeDisplayPosition(int gridCoordinate){ + return (gridCoordinate*unitSize+unitSize/2); + } + + public void moveRight(){ + if (maze.east[xCell][yCell] == false){ + xCell++; + setCircleX(xCell); + } + } + + public void moveLeft(){ + if (maze.west[xCell][yCell] == false){ + xCell--; + setCircleX(xCell); + } + } + + public void moveUp(){ + if (maze.south[xCell][yCell] == false){ + yCell--; + setCircleY(yCell); + } + } + + public void moveDown(){ + if (maze.north[xCell][yCell] == false){ + yCell++; + setCircleY(yCell); + } + } + + public Circle getCircle(){ + return circle; + } + + public Point getCurrentLocation(){ + return new Point(xCell,yCell); + } +} diff --git a/src/edu/nd/se2018/examples/mvc/.settings/org.eclipse.jdt.core.prefs b/src/edu/nd/se2018/examples/mvc/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..bb35fa0a --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/controller/Drone.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/controller/Drone.java new file mode 100644 index 00000000..fc59e34f --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/controller/Drone.java @@ -0,0 +1,118 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.controller; + +import java.awt.Point; +import java.util.Observable; + +import edu.nd.se2018.examples.mvc.dronetakeoff.model.Ascending; +import edu.nd.se2018.examples.mvc.dronetakeoff.model.DroneState; +import edu.nd.se2018.examples.mvc.dronetakeoff.model.Landing; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.text.Text; + +/** + * Drone Manages the behavior of an individual drone + * @author Jane + * + */ +public class Drone extends Observable{ + ImageView droneImageView; + Image droneImage; + //Point droneCoordinates; + int heightFromCeiling; + int GroundLevel; + DroneState droneState; + + /** + * + * @param startX xCoordinates + * @param startY yCoordinates (starting from top of screen) + * @param heightFromCeiling maximum height + * @param infoMessage handle for posting status messages + * @param GroundLevel ground level in terms of y coordinates + */ + public Drone(int startX, int startY, int heightFromCeiling, Text infoMessage, int GroundLevel){ + this.heightFromCeiling = heightFromCeiling; + this.GroundLevel = GroundLevel; + droneState = new DroneState(infoMessage); + droneState.initializeStates(); + droneState.setCoordinates(new Point(startX, startY)); + } + + public boolean isAscending(){ + if (droneState.getFlyingState() instanceof Ascending) + return true; + else + return false; + } + + public boolean isLanding(){ + if (droneState.getFlyingState() instanceof Landing) + return true; + else + return false; + } + + /** + * Takeoff event initiated by button press + */ + public void takeOff(){ + droneState.getFlyingState().takeoffPressed(); + } + + /** + * Land event initiated by button press + */ + public void land(){ + droneState.getFlyingState().landPressed(); + } + + /** + * Hover event initiated by button press + */ + public void hover(){ + droneState.getFlyingState().hoverPressed(); + } + + + /** + * Drone ascends. Its position is modified + */ + public void ascend(){ + Point droneCoordinates = droneState.getCoordinates(); + if (droneCoordinates.getY() > heightFromCeiling){ // A bit unintuitive because y axis grows from the top down. + droneCoordinates.setLocation(droneCoordinates.getX(), droneCoordinates.getY()-1); + setChanged(); + notifyObservers(); + } + else + droneState.getFlyingState().reachedMaxHeight(); + } + + /** + * Drone descends. Its position is modified + */ + public void descend(){ + Point droneCoordinates = droneState.getCoordinates(); + if (droneCoordinates.getY() < GroundLevel-50){ + droneCoordinates.setLocation(droneCoordinates.getX(), droneCoordinates.getY()+1); + setChanged(); + notifyObservers(); + } + else + droneState.getFlyingState().reachedGround(); + } + + public Point getCoordinates(){ + return droneState.getCoordinates(); + } + + /** + * + * @return ImageView (needed by JavaFX) + */ + public ImageView getDroneImageView(){ + return droneImageView; + } + +} diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Ascending.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Ascending.java new file mode 100644 index 00000000..8b6b90e7 --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Ascending.java @@ -0,0 +1,41 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.model; + +public class Ascending implements IFlyingState{ + + DroneState droneState; + + public Ascending (DroneState droneState){ + this.droneState = droneState; + } + + @Override + public void takeoffPressed() { + // Do nothing. Already ascending + droneState.setInfoMessage("No change in state. Already taking off"); + } + + @Override + public void landPressed() { + droneState.setFlyingState(droneState.getLandingState()); + droneState.setInfoMessage("Landing"); + } + + @Override + public void reachedGround() { + // Cannot reach ground from ascending mode. + } + + @Override + public void reachedMaxHeight() { + droneState.setFlyingState(droneState.getHoveringState()); + droneState.setInfoMessage("Reached Max height. Hovering."); + } + + @Override + public void hoverPressed() { + droneState.setFlyingState(droneState.getHoveringState()); + droneState.setInfoMessage("Hovering in place"); + + } + +} diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/DroneState.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/DroneState.java new file mode 100644 index 00000000..e5dc430f --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/DroneState.java @@ -0,0 +1,68 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.model; + +import java.awt.Point; + +import javafx.scene.text.Text; + +/** + * Manages the state of a single drone + * @author Jane + * + */ +public class DroneState { + Point droneCoordinates; + IFlyingState ascendingState; + IFlyingState hoveringState; + IFlyingState landingState; + IFlyingState groundState; + IFlyingState currentState; + Text infoMessage; + + public void setCoordinates(Point point){ + droneCoordinates = point; + } + + public DroneState(Text infoMessage){ + this.infoMessage = infoMessage; + } + + public Point getCoordinates(){ + return droneCoordinates; + } + + /** + * Sets information message + * @param text + */ + public void setInfoMessage(String text){ + infoMessage.setText(text); + } + + public void initializeStates(){ + ascendingState = new Ascending(this); + hoveringState = new Hovering(this); + landingState = new Landing(this); + groundState = new OnGround(this); + currentState = groundState; + } + + public void setFlyingState(IFlyingState flyingState){ + currentState = flyingState; + } + public IFlyingState getFlyingState(){ + return currentState; + } + public IFlyingState getAscendingState(){ + return ascendingState; + } + public IFlyingState getHoveringState(){ + return hoveringState; + } + public IFlyingState getLandingState(){ + return landingState; + } + public IFlyingState getGroundState(){ + return groundState; + } + +} diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Hovering.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Hovering.java new file mode 100644 index 00000000..7142ba8d --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Hovering.java @@ -0,0 +1,39 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.model; + +public class Hovering implements IFlyingState{ + + DroneState droneState; + + public Hovering (DroneState droneState){ + this.droneState = droneState; + } + + @Override + public void takeoffPressed() { + droneState.setFlyingState(droneState.getAscendingState()); + droneState.setInfoMessage("Ascending again"); + } + + @Override + public void landPressed() { + droneState.setFlyingState(droneState.getLandingState()); + droneState.setInfoMessage("Landing again"); + } + + @Override + public void reachedGround() { + // Impossible to reach ground from hovering state + } + + @Override + public void reachedMaxHeight() { + // Impossible to reach max height from hovering state + } + + @Override + public void hoverPressed() { + // Already hovering. + + } + +} \ No newline at end of file diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/IFlyingState.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/IFlyingState.java new file mode 100644 index 00000000..a5fbcd74 --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/IFlyingState.java @@ -0,0 +1,9 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.model; + +public interface IFlyingState { + public void takeoffPressed(); + public void landPressed(); + public void reachedGround(); + public void reachedMaxHeight(); + public void hoverPressed(); +} diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Landing.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Landing.java new file mode 100644 index 00000000..37921649 --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/Landing.java @@ -0,0 +1,36 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.model; + +public class Landing implements IFlyingState{ + DroneState droneState; + + public Landing (DroneState droneState){ + this.droneState = droneState; + } + + @Override + public void takeoffPressed() { + droneState.setInfoMessage("Cannot abort landing"); + } + + @Override + public void landPressed() { + droneState.setInfoMessage("Already landing"); + } + + @Override + public void reachedGround() { + droneState.setFlyingState(droneState.getGroundState()); + droneState.setInfoMessage("Landed on ground"); + } + + @Override + public void reachedMaxHeight() { + // Impossible to reach max height from landing state + } + + @Override + public void hoverPressed() { + droneState.setFlyingState(droneState.getHoveringState()); + droneState.setInfoMessage("Hovering in place"); + } +} diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/OnGround.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/OnGround.java new file mode 100644 index 00000000..97be1659 --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/model/OnGround.java @@ -0,0 +1,39 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.model; + + +public class OnGround implements IFlyingState{ + + DroneState droneState; + + public OnGround (DroneState droneState){ + this.droneState = droneState; + } + + @Override + public void takeoffPressed() { + droneState.setInfoMessage("Taking off"); + droneState.setFlyingState(droneState.getAscendingState()); + } + + @Override + public void landPressed() { + // already on ground + } + + @Override + public void reachedGround() { + // already on ground + } + + @Override + public void reachedMaxHeight() { + // not moving yet + } + + @Override + public void hoverPressed() { + droneState.setInfoMessage("On ground. Cannot hover!!"); + } + +} + diff --git a/src/edu/nd/se2018/examples/mvc/dronetakeoff/view/DroneUI.java b/src/edu/nd/se2018/examples/mvc/dronetakeoff/view/DroneUI.java new file mode 100644 index 00000000..0b1b769a --- /dev/null +++ b/src/edu/nd/se2018/examples/mvc/dronetakeoff/view/DroneUI.java @@ -0,0 +1,137 @@ +package edu.nd.se2018.examples.mvc.dronetakeoff.view; + +import java.util.Observable; +import java.util.Observer; + +import edu.nd.se2018.examples.mvc.dronetakeoff.controller.Drone; +import javafx.animation.AnimationTimer; +import javafx.application.Application; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import javafx.stage.Stage; + +public class DroneUI extends Application implements Observer{ + + Stage mainStage; + Scene mainScene; + Drone xCopter; + Text infoMessage; + final int GROUNDLEVEL = 420; + ImageView droneImageView; + Image droneImage; + + private void setDroneImage(Drone drone){ + Image droneImage = new Image("images\\drone4.png",100,100,true,true); + droneImageView = new ImageView(droneImage); + droneImageView.setX(drone.getCoordinates().x); + droneImageView.setY(drone.getCoordinates().y); + } + + @Override + public void start(Stage primaryStage) throws Exception { + AnchorPane root = new AnchorPane(); + mainScene = new Scene(root,450,600); + mainStage = new Stage(); + mainStage.setTitle("Take off demo"); + mainStage.setScene(mainScene); + mainStage.show(); + + infoMessage = new Text(); + infoMessage.setX(25); + infoMessage.setY(GROUNDLEVEL+140); + infoMessage.setFont(new Font("Times New Roman", 20)); + root.getChildren().add(infoMessage); + + xCopter = new Drone(180,GROUNDLEVEL-50,50, infoMessage, GROUNDLEVEL); + xCopter.addObserver(this); + setDroneImage(xCopter); + root.getChildren().add(droneImageView); + + Button takeoffButton = createButton(root,"Take off", 50, GROUNDLEVEL+50); + takeoffButton.setOnAction(new EventHandler(){ + @Override + public void handle(ActionEvent event) { + xCopter.takeOff(); + }}); + + Button hoverButton = createButton(root,"Hover", 200, GROUNDLEVEL+50); + hoverButton.setOnAction(new EventHandler(){ + @Override + public void handle(ActionEvent event) { + xCopter.hover(); + }}); + + Button landButton = createButton(root,"Land", 350, GROUNDLEVEL+50); + landButton.setOnAction(new EventHandler(){ + @Override + public void handle(ActionEvent event) { + xCopter.land(); + }}); + + // Starts the animation + new AnimationTimer(){ + @Override + public void handle(long now) { + runSimulation(); + } + }.start(); + } + + public void setMessage(String msg){ + infoMessage.setText(msg); + } + + private void runSimulation(){ + if (xCopter.isAscending()){//currentState instanceof Ascending){ + xCopter.ascend(); + } + if (xCopter.isLanding()){ + xCopter.descend(); + } + try { + Thread.sleep(25); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Helper function for creating buttons + * @param pane Button gets added to the pane + * @param buttonName + * @param x + * @param y + * @return + */ + private Button createButton(AnchorPane pane, String buttonName,int x, int y){ + Button button = new Button(); + button.setLayoutX(x); + button.setLayoutY(y); + button.setText(buttonName); + button.setFont(new Font("Times New Roman",20)); + pane.getChildren().add(button); + return button; + } + + public static void main(String[] args) { + launch(args); + } + + @Override + public void update(Observable o, Object arg) { + if (o instanceof Drone){ + Drone drone = (Drone)o; + droneImageView.setY(drone.getCoordinates().y); + } + + } +} + diff --git a/src/edu/nd/se2018/examples/observer/catmouse/CatAndMouseGameFX.java b/src/edu/nd/se2018/examples/observer/catmouse/CatAndMouseGameFX.java index 8b264678..53245096 100644 --- a/src/edu/nd/se2018/examples/observer/catmouse/CatAndMouseGameFX.java +++ b/src/edu/nd/se2018/examples/observer/catmouse/CatAndMouseGameFX.java @@ -27,7 +27,7 @@ public CatAndMouseGameFX(){ mouse = new Mouse(); - int catCount = 1; + int catCount = 3; // Create cats for(int j = 0; j < catCount; j++) diff --git a/src/edu/nd/se2018/examples/observer/catmouse/Mouse.java b/src/edu/nd/se2018/examples/observer/catmouse/Mouse.java index 57b7d657..ebef703f 100644 --- a/src/edu/nd/se2018/examples/observer/catmouse/Mouse.java +++ b/src/edu/nd/se2018/examples/observer/catmouse/Mouse.java @@ -16,7 +16,6 @@ * */ public class Mouse extends Observable { - List observers = new LinkedList(); Point mousePosition = new Point(400,500); // Actual position of the mouse Point targetPosition; // Coordinates that the mouse is currently heading towards Random rand; // Introduces randomness into the "game" diff --git a/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStation2.java b/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStation2.java new file mode 100644 index 00000000..005f7936 --- /dev/null +++ b/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStation2.java @@ -0,0 +1,7 @@ +package edu.nd.se2018.examples.observer.weatherstation; + +import java.util.Observable; + +public class WeatherStation2 extends Observable{ + +} diff --git a/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStationSimulator.java b/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStationSimulator.java index cbf7e614..adbb11e9 100644 --- a/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStationSimulator.java +++ b/src/edu/nd/se2018/examples/observer/weatherstation/WeatherStationSimulator.java @@ -11,6 +11,6 @@ public static void main(String[] args){ weatherStation.setTemperature(70); weatherStation.setOutlook(Outlook.SUNNY); - weatherStation.displayWeather(); + //weatherStation.displayWeather(); } } diff --git a/src/edu/nd/se2018/examples/observer/weatherstation/WeatherView3.java b/src/edu/nd/se2018/examples/observer/weatherstation/WeatherView3.java new file mode 100644 index 00000000..0dda661f --- /dev/null +++ b/src/edu/nd/se2018/examples/observer/weatherstation/WeatherView3.java @@ -0,0 +1,14 @@ +package edu.nd.se2018.examples.observer.weatherstation; + +import java.util.Observable; +import java.util.Observer; + +public class WeatherView3 implements Observer{ + + @Override + public void update(Observable o, Object arg) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/edu/nd/se2018/examples/shapepopper/Canvas.java b/src/edu/nd/se2018/examples/shapepopper/Canvas.java new file mode 100644 index 00000000..bed15536 --- /dev/null +++ b/src/edu/nd/se2018/examples/shapepopper/Canvas.java @@ -0,0 +1,115 @@ +package edu.nd.se2018.examples.shapepopper; + +import java.util.ArrayList; +import java.util.List; + +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.application.Application; +import javafx.event.EventHandler; +import javafx.geometry.Point2D; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +public class Canvas extends Application { + + AnchorPane root; + Point2D clickPoint; + MyShape currentShape; + Scene scene; + int temp = 0; + List shapes = new ArrayList(); + Point2D oldPoint = null; + boolean inDragMode = false; + @Override + public void start(Stage primaryStage) throws Exception { + + root = new AnchorPane(); + + scene = new Scene(root,500,500); + scene.setOnMouseClicked(mouseHandler); + scene.setOnMouseDragged(mouseHandler); + scene.setOnMouseEntered(mouseHandler); + scene.setOnMouseExited(mouseHandler); + scene.setOnMouseMoved(mouseHandler); + scene.setOnMousePressed(mouseHandler); + scene.setOnMouseReleased(mouseHandler); + + primaryStage.setTitle("Shape Composer"); + primaryStage.setScene(scene); + primaryStage.show(); + } + + EventHandler mouseHandler = new EventHandler() { + @Override + public void handle(MouseEvent mouseEvent) { + + clickPoint = new Point2D(mouseEvent.getX(),mouseEvent.getY()); + String eventName = mouseEvent.getEventType().getName(); + + // Return shape if target of mouse click (otherwise return null) + if(!inDragMode){ + currentShape = getCurrentShape(); + } + + switch(eventName){ + case "MOUSE_RELEASED": + + // If current shape is a circle and mouse released inside rectangle + if(currentShape!=null && currentShape instanceof MyCircle){ + for(MyShape container: shapes){ + if (container instanceof MyRectangle && container.containsPoint(clickPoint)){ + ((MyRectangle)container).addChild(currentShape); + break; + } + } + } + + // If mouse is released and not currently dragging a shape + if(currentShape==null){ + // Left mouse button. Create a circle + if(mouseEvent.getButton() == MouseButton.PRIMARY) { + MyCircle circle = new MyCircle(clickPoint,20); + shapes.add(circle); + root.getChildren().add(circle.getCircle()); + } else { // Right mouse button. Create a rectangle. + MyRectangle rect = new MyRectangle(clickPoint,200,200); + shapes.add(rect); + root.getChildren().add(rect.getRectangle()); + } + } + else { + currentShape = null; // Housekeeping as per state diagram + } + inDragMode = false; + break; + + case "MOUSE_DRAGGED": + // Move shape according to delta from last drag event + inDragMode = true; + if(currentShape!=null && oldPoint != null){ + currentShape.moveRelative(clickPoint.getX()-oldPoint.getX(), clickPoint.getY()-oldPoint.getY()); + } + break; + } + // Needed by mouse dragged + oldPoint = clickPoint; + } + }; + + private MyShape getCurrentShape(){ + MyShape currentShape = null; + for(MyShape myShape: shapes){ + if (myShape.containsPoint(clickPoint)){ + currentShape = myShape; + break; + } + } + return currentShape; + } + + public static void main(String args[]){ + launch(args); + } +} diff --git a/src/edu/nd/se2018/examples/shapepopper/MyCircle.java b/src/edu/nd/se2018/examples/shapepopper/MyCircle.java new file mode 100644 index 00000000..9dc540ce --- /dev/null +++ b/src/edu/nd/se2018/examples/shapepopper/MyCircle.java @@ -0,0 +1,44 @@ +package edu.nd.se2018.examples.shapepopper; + +import javafx.geometry.Point2D; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +public class MyCircle implements MyShape{ + + Point2D center; + int radius; + Circle circle = new Circle(); + public MyCircle(Point2D point, int radius){ + center = point; + this.radius = radius; + setPosition(point); + } + + public void setLineColor(Color color){ + circle.setStroke(color); + circle.setFill(color); + } + + public void setPosition(Point2D point){ + circle.setCenterX(point.getX()); + circle.setCenterY(point.getY()); + circle.setRadius(radius); + } + + public Circle getCircle(){ + return circle; + } + + @Override + public boolean containsPoint(javafx.geometry.Point2D point) { + return (circle.contains(point)); + } + + @Override + public void moveRelative(double X, double Y) { + circle.setCenterX(circle.getCenterX()+X); + circle.setCenterY(circle.getCenterY()+Y); + + } +} diff --git a/src/edu/nd/se2018/examples/shapepopper/MyRectangle.java b/src/edu/nd/se2018/examples/shapepopper/MyRectangle.java new file mode 100644 index 00000000..f0a577e0 --- /dev/null +++ b/src/edu/nd/se2018/examples/shapepopper/MyRectangle.java @@ -0,0 +1,79 @@ +package edu.nd.se2018.examples.shapepopper; + +import java.util.ArrayList; +import java.util.List; + +import javafx.geometry.Point2D; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +public class MyRectangle implements MyShape{ + + Point2D topLeft; + int height; + int width; + Color rectangleColor; + Rectangle rectangle = new Rectangle(); + List innerShapes = new ArrayList(); + RandomColorPicker colorPicker = new RandomColorPicker(); + + public MyRectangle(Point2D topLeft, int height, int width){ + this.topLeft = topLeft; + this.height = height; + this.width = width; + rectangle.setHeight(height); + rectangle.setWidth(width); + rectangle.setX(topLeft.getX()); + rectangle.setY(topLeft.getY()); + rectangle.setFill(null); + setLineColor(colorPicker.pickRandomColor()); + rectangle.setStrokeWidth(5); + setPosition(topLeft); + } + + + public void setPosition(Point2D point){ + topLeft = point; + System.out.println("Setting position"); + } + + public Rectangle getRectangle(){ + return rectangle; + } + + @Override + public boolean containsPoint(Point2D point) { + boolean inRectangle = (point.getX() >= rectangle.getX() && point.getX() <= rectangle.getX()+rectangle.getWidth() + && point.getY()>= rectangle.getY() && point.getY() <= rectangle.getY()+rectangle.getHeight()); + return inRectangle; + + } + + public void addChild(MyShape shape){ + innerShapes.add(shape); + shape.setLineColor(rectangleColor); + } + + public void removeChild(MyShape shape){ + if(innerShapes.contains(shape)){ + innerShapes.remove(shape); + shape.setLineColor(rectangleColor); + } + } + + @Override + public void moveRelative(double X, double Y) { + rectangle.setX(rectangle.getX()+X); + rectangle.setY(rectangle.getY()+Y); + for(MyShape child: innerShapes){ + child.moveRelative(X,Y); + } + } + + + @Override + public void setLineColor(Color color) { + rectangleColor = color; + rectangle.setStroke(rectangleColor); + } +} diff --git a/src/edu/nd/se2018/examples/shapepopper/MyShape.java b/src/edu/nd/se2018/examples/shapepopper/MyShape.java new file mode 100644 index 00000000..7fd6445a --- /dev/null +++ b/src/edu/nd/se2018/examples/shapepopper/MyShape.java @@ -0,0 +1,12 @@ +package edu.nd.se2018.examples.shapepopper; + +import javafx.geometry.Point2D; +import javafx.scene.paint.Color; + + +public interface MyShape { + public boolean containsPoint(Point2D point); + public void setPosition(Point2D point); + public void moveRelative(double X, double Y); + public void setLineColor(Color color); +} diff --git a/src/edu/nd/se2018/examples/shapepopper/RandomColorPicker.java b/src/edu/nd/se2018/examples/shapepopper/RandomColorPicker.java new file mode 100644 index 00000000..1005c57c --- /dev/null +++ b/src/edu/nd/se2018/examples/shapepopper/RandomColorPicker.java @@ -0,0 +1,28 @@ +package edu.nd.se2018.examples.shapepopper; + +import java.util.Random; + +import javafx.scene.paint.Color; + +public class RandomColorPicker { + Color[] colorPick = new Color[10]; + Random rand = new Random(); + + public RandomColorPicker(){ + colorPick[0] = Color.RED; + colorPick[1] = Color.BLUE; + colorPick[2] = Color.GREEN; + colorPick[3] = Color.ORANGE; + colorPick[4] = Color.YELLOW; + colorPick[5] = Color.DARKGOLDENROD; + colorPick[6] = Color.GRAY; + colorPick[7] = Color.ALICEBLUE; + colorPick[8] = Color.HOTPINK; + colorPick[9] = Color.PLUM; + } + + public Color pickRandomColor(){ + int picker = rand.nextInt(10); + return colorPick[picker]; + } +} diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/GumballMachine.java b/src/edu/nd/se2018/examples/state/gumballmachine/GumballMachine.java new file mode 100644 index 00000000..cecf585e --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/GumballMachine.java @@ -0,0 +1,92 @@ +package edu.nd.se2018.examples.state.gumballmachine; + +public class GumballMachine { + + State soldOutState; + State noQuarterState; + State hasQuarterState; + State soldState; + + State state; + int count = 0; + + public GumballMachine(int numberGumballs) { + soldOutState = new SoldOutState(this); + noQuarterState = new NoQuarterState(this); + hasQuarterState = new HasQuarterState(this); + soldState = new SoldState(this); + + this.count = numberGumballs; + if (numberGumballs > 0) { + state = noQuarterState; + } else { + state = soldOutState; + } + } + + public void insertQuarter() { + state.insertQuarter(); + } + + public void ejectQuarter() { + state.ejectQuarter(); + } + + public void turnCrank() { + state.turnCrank(); + state.dispense(); + } + + void releaseBall() { + System.out.println("A gumball comes rolling out the slot..."); + if (count != 0) { + count = count - 1; + } + } + + int getCount() { + return count; + } + + void refill(int count) { + this.count += count; + System.out.println("The gumball machine was just refilled; it's new count is: " + this.count); + state.refill(); + } + + void setState(State state) { + this.state = state; + } + public State getState() { + return state; + } + + public State getSoldOutState() { + return soldOutState; + } + + public State getNoQuarterState() { + return noQuarterState; + } + + public State getHasQuarterState() { + return hasQuarterState; + } + + public State getSoldState() { + return soldState; + } + + public String toString() { + StringBuffer result = new StringBuffer(); + result.append("\nMighty Gumball, Inc."); + result.append("\nJava-enabled Standing Gumball Model #2004"); + result.append("\nInventory: " + count + " gumball"); + if (count != 1) { + result.append("s"); + } + result.append("\n"); + result.append("Machine is " + state + "\n"); + return result.toString(); + } +} diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/GumballMachineTestDrive.java b/src/edu/nd/se2018/examples/state/gumballmachine/GumballMachineTestDrive.java new file mode 100644 index 00000000..36887ed0 --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/GumballMachineTestDrive.java @@ -0,0 +1,26 @@ +package edu.nd.se2018.examples.state.gumballmachine; + +public class GumballMachineTestDrive { + + public static void main(String[] args) { + GumballMachine gumballMachine = new GumballMachine(2); + + System.out.println(gumballMachine); + + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + + System.out.println(gumballMachine); + + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + + gumballMachine.refill(5); + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + + System.out.println(gumballMachine); + } +} diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/HasQuarterState.java b/src/edu/nd/se2018/examples/state/gumballmachine/HasQuarterState.java new file mode 100644 index 00000000..2f40370e --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/HasQuarterState.java @@ -0,0 +1,35 @@ +package edu.nd.se2018.examples.state.gumballmachine; + +//import java.util.Random; + +public class HasQuarterState implements State { + GumballMachine gumballMachine; + + public HasQuarterState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + public void insertQuarter() { + System.out.println("You can't insert another quarter"); + } + + public void ejectQuarter() { + System.out.println("Quarter returned"); + gumballMachine.setState(gumballMachine.getNoQuarterState()); + } + + public void turnCrank() { + System.out.println("You turned..."); + gumballMachine.setState(gumballMachine.getSoldState()); + } + + public void dispense() { + System.out.println("No gumball dispensed"); + } + + public void refill() { } + + public String toString() { + return "waiting for turn of crank"; + } +} diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/NoQuarterState.java b/src/edu/nd/se2018/examples/state/gumballmachine/NoQuarterState.java new file mode 100644 index 00000000..aba7a488 --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/NoQuarterState.java @@ -0,0 +1,32 @@ +package edu.nd.se2018.examples.state.gumballmachine; + + +public class NoQuarterState implements State { + GumballMachine gumballMachine; + + public NoQuarterState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + public void insertQuarter() { + System.out.println("You inserted a quarter"); + gumballMachine.setState(gumballMachine.getHasQuarterState()); + } + + public void ejectQuarter() { + System.out.println("You haven't inserted a quarter"); + } + + public void turnCrank() { + System.out.println("You turned, but there's no quarter"); + } + + public void dispense() { + System.out.println("You need to pay first"); + } + + public void refill() { } + public String toString() { + return "waiting for quarter"; + } +} diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/SoldOutState.java b/src/edu/nd/se2018/examples/state/gumballmachine/SoldOutState.java new file mode 100644 index 00000000..5d71349c --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/SoldOutState.java @@ -0,0 +1,33 @@ +package edu.nd.se2018.examples.state.gumballmachine; + +public class SoldOutState implements State { + GumballMachine gumballMachine; + + public SoldOutState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + public void insertQuarter() { + System.out.println("You can't insert a quarter, the machine is sold out"); + } + + public void ejectQuarter() { + System.out.println("You can't eject, you haven't inserted a quarter yet"); + } + + public void turnCrank() { + System.out.println("You turned, but there are no gumballs"); + } + + public void dispense() { + System.out.println("No gumball dispensed"); + } + + public void refill() { + gumballMachine.setState(gumballMachine.getNoQuarterState()); + } + + public String toString() { + return "sold out"; + } +} diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/SoldState.java b/src/edu/nd/se2018/examples/state/gumballmachine/SoldState.java new file mode 100644 index 00000000..bc8b8174 --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/SoldState.java @@ -0,0 +1,40 @@ +package edu.nd.se2018.examples.state.gumballmachine; + +public class SoldState implements State { + + GumballMachine gumballMachine; + + public SoldState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + public void insertQuarter() { + System.out.println("Please wait, we're already giving you a gumball"); + } + + public void ejectQuarter() { + System.out.println("Sorry, you already turned the crank"); + } + + public void turnCrank() { + System.out.println("Turning twice doesn't get you another gumball!"); + } + + public void dispense() { + gumballMachine.releaseBall(); + if (gumballMachine.getCount() > 0) { + gumballMachine.setState(gumballMachine.getNoQuarterState()); + } else { + System.out.println("Oops, out of gumballs!"); + gumballMachine.setState(gumballMachine.getSoldOutState()); + } + } + + public void refill() { } + + public String toString() { + return "dispensing a gumball"; + } +} + + diff --git a/src/edu/nd/se2018/examples/state/gumballmachine/State.java b/src/edu/nd/se2018/examples/state/gumballmachine/State.java new file mode 100644 index 00000000..593a53df --- /dev/null +++ b/src/edu/nd/se2018/examples/state/gumballmachine/State.java @@ -0,0 +1,11 @@ +package edu.nd.se2018.examples.state.gumballmachine; + +public interface State { + + public void insertQuarter(); + public void ejectQuarter(); + public void turnCrank(); + public void dispense(); + + public void refill(); +} diff --git a/src/images/ColumbusShip.png b/src/images/ColumbusShip.png new file mode 100644 index 00000000..ab23f309 Binary files /dev/null and b/src/images/ColumbusShip.png differ diff --git a/src/images/Train.PNG b/src/images/Train.PNG new file mode 100644 index 00000000..b8b7386d Binary files /dev/null and b/src/images/Train.PNG differ diff --git a/src/images/bluecar.PNG b/src/images/bluecar.PNG new file mode 100644 index 00000000..c6934564 Binary files /dev/null and b/src/images/bluecar.PNG differ diff --git a/src/images/drone4.png b/src/images/drone4.png new file mode 100644 index 00000000..07465343 Binary files /dev/null and b/src/images/drone4.png differ diff --git a/src/images/graycar.PNG b/src/images/graycar.PNG new file mode 100644 index 00000000..fc3c4020 Binary files /dev/null and b/src/images/graycar.PNG differ diff --git a/src/images/greencar.PNG b/src/images/greencar.PNG new file mode 100644 index 00000000..3678ad33 Binary files /dev/null and b/src/images/greencar.PNG differ diff --git a/src/images/pirateship.gif b/src/images/pirateship.gif new file mode 100644 index 00000000..c3bbfa28 Binary files /dev/null and b/src/images/pirateship.gif differ diff --git a/src/images/redcar.PNG b/src/images/redcar.PNG new file mode 100644 index 00000000..d8729e97 Binary files /dev/null and b/src/images/redcar.PNG differ diff --git a/src/images/target.png b/src/images/target.png new file mode 100644 index 00000000..798d0afd Binary files /dev/null and b/src/images/target.png differ diff --git a/src/images/win.gif b/src/images/win.gif new file mode 100644 index 00000000..db273ef2 Binary files /dev/null and b/src/images/win.gif differ