Skip to content
Snippets Groups Projects
Commit cc167c70 authored by jez04's avatar jez04
Browse files

feat: :tada: solution lab04

parent 65be0bc2
Branches
No related merge requests found
......@@ -5,16 +5,15 @@ import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
public class Background {
public class Background extends WorldEntity {
private Scene scene;
private Point2D[] imagePosition;
private Image[] image;
private Point2D speed;
private static final double[] speedMultiplier = new double[] { 0.6, 0.8, 1, 1 };
public Background(Scene scene) {
this.scene = scene;
super(scene, new Point2D(0, 0));
image = new Image[4];
imagePosition = new Point2D[4];
image[0] = new Image(Background.class.getResourceAsStream("cityfar400.png"));
......@@ -28,7 +27,8 @@ public class Background {
speed = new Point2D(100, 0);
}
public void draw(GraphicsContext gc) {
@Override
public void drawInternal(GraphicsContext gc) {
gc.setFill(Color.DARKBLUE);
gc.fillRect(0, 0, scene.getSize().getWidth(), scene.getSize().getHeight());
for (int i = 0; i < image.length; i++) {
......@@ -44,6 +44,7 @@ public class Background {
}
}
@Override
public void simulate(double deltaTime) {
Point2D baseSpeed = speed.multiply(deltaTime / 1_000_000_000);
for (int i = 0; i < image.length; i++) {
......
package lab;
import java.util.Random;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
public class Boat {
public class Boat extends WorldEntity implements Collisionable {
private Scene scene;
private Point2D position;
private static final Random RANDOM = new Random();
private Point2D speed;
private Image image;
public Boat(Scene scene, Point2D position) {
this.scene = scene;
this.position = position;
super(scene, position);
image = new Image(Boat.class.getResourceAsStream("ship-boat.gif"));
speed = new Point2D(0, 0);
}
public void draw(GraphicsContext gc) {
@Override
public void drawInternal(GraphicsContext gc) {
gc.drawImage(image, position.getX(), position.getY());
gc.strokeRect(position.getX(), position.getY(), image.getWidth(), image.getHeight());
}
@Override
public void simulate(double deltaTime) {
position = position.add(speed.multiply(deltaTime / 1_000_000_000));
speed = speed.multiply(0.99);
}
@Override
public Rectangle2D getBoundingBox() {
return new Rectangle2D(position.getX(), position.getY(), image.getWidth(), image.getHeight());
}
@Override
public boolean intersect(Rectangle2D another) {
return getBoundingBox().intersects(another);
}
@Override
public void hitBy(Collisionable another) {
if(another instanceof LochNess lochNess) {
int direction = RANDOM.nextBoolean()?-1:1;
speed = new Point2D(-20, direction * RANDOM.nextDouble(50, 100));
}
}
}
package lab;
import javafx.geometry.Rectangle2D;
public interface Collisionable {
Rectangle2D getBoundingBox();
boolean intersect(Rectangle2D another);
void hitBy(Collisionable another);
}
package lab;
import javafx.scene.canvas.GraphicsContext;
public interface DrawableSimulable {
void draw(GraphicsContext gc);
void simulate(double deltaTime);
}
......@@ -7,16 +7,15 @@ import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
public class LochNess {
public class LochNess extends WorldEntity implements Collisionable {
private static final Random RANDOM = new Random();
private Scene scene;
private Point2D position;
private Point2D speed;
private Image image;
public LochNess(Scene scene) {
super(scene, new Point2D(0, 0));
this.scene = scene;
image = new Image(LochNess.class.getResourceAsStream("LochNess.gif"));
position = new Point2D(RANDOM.nextDouble(scene.getSize().getWidth() * 0.3, scene.getSize().getWidth()),
......@@ -24,22 +23,25 @@ public class LochNess {
speed = new Point2D(-RANDOM.nextDouble(50, 150), 0);
}
public void draw(GraphicsContext gc) {
@Override
public void drawInternal(GraphicsContext gc) {
gc.drawImage(image, position.getX(), position.getY());
gc.strokeRect(position.getX(), position.getY(), image.getWidth(), image.getHeight());
}
@Override
public void simulate(double deltaTime) {
position = position.add(speed.multiply(deltaTime / 1_000_000_000));
if (position.getX() + image.getWidth() < 0) {
position = new Point2D(scene.getSize().getWidth(), position.getY());
}
if (position.getX() > scene.getSize().getWidth()+5) {
if (position.getX() > scene.getSize().getWidth() + 5) {
position = new Point2D(scene.getSize().getWidth(), position.getY());
speed = speed.multiply(-1);
}
}
@Override
public Rectangle2D getBoundingBox() {
return new Rectangle2D(position.getX(), position.getY(), image.getWidth(), image.getHeight());
}
......@@ -47,4 +49,17 @@ public class LochNess {
public void changeDirection() {
speed = speed.multiply(-1);
}
@Override
public boolean intersect(Rectangle2D another) {
return getBoundingBox().intersects(another);
}
@Override
public void hitBy(Collisionable another) {
if (another instanceof Boat) {
changeDirection();
}
}
}
......@@ -7,10 +7,8 @@ import javafx.scene.paint.Color;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Rotate;
public class Rock {
public class Rock extends WorldEntity {
private Scene scene;
private Point2D position;
private Dimension2D size;
private double angle = 0;
......@@ -19,13 +17,12 @@ public class Rock {
}
public Rock(Scene scene, Point2D position, Dimension2D size) {
this.scene = scene;
this.position = position;
super(scene, position);
this.size = size;
}
public void draw(GraphicsContext gc) {
gc.save();
@Override
public void drawInternal(GraphicsContext gc) {
gc.setFill(Color.GRAY);
gc.setStroke(Color.GREEN);
Point2D center = position.add(size.getWidth() / 2, size.getHeight() / 2);
......@@ -33,7 +30,6 @@ public class Rock {
gc.setTransform(new Affine(rotateMatrix));
gc.fillRect(position.getX(), position.getY(), size.getWidth(), size.getHeight());
gc.strokeRect(position.getX(), position.getY(), size.getWidth(), size.getHeight());
gc.restore();
}
public void simulate(double deltaTime) {
......
......@@ -7,19 +7,16 @@ import javafx.scene.canvas.GraphicsContext;
public class Scene {
private Dimension2D size;
private Background background;
private Rock rock;
private Boat boat;
private LochNess[] lochNesses;
private DrawableSimulable[] sceneEntitites;
public Scene(double width, double height) {
size = new Dimension2D(width, height);
background = new Background(this);
rock = new Rock(this, new Point2D(300, 300), new Dimension2D(30, 50));
boat = new Boat(this, new Point2D(20, 200));
lochNesses = new LochNess[5];
for (int i = 0; i < lochNesses.length; i++) {
lochNesses[i] = new LochNess(this);
sceneEntitites = new DrawableSimulable[8];
sceneEntitites[0] = new Background(this);
sceneEntitites[1] = new Rock(this, new Point2D(300, 300), new Dimension2D(30, 50));
sceneEntitites[2] = new Boat(this, new Point2D(20, 200));
for (int i = 3; i < sceneEntitites.length; i++) {
sceneEntitites[i] = new LochNess(this);
}
}
......@@ -28,22 +25,25 @@ public class Scene {
}
public void draw(GraphicsContext gc) {
background.draw(gc);
rock.draw(gc);
boat.draw(gc);
for (LochNess lochNess : lochNesses) {
lochNess.draw(gc);
for (DrawableSimulable entity : sceneEntitites) {
entity.draw(gc);
}
}
public void simulate(double deltaTime) {
background.simulate(deltaTime);
rock.simulate(deltaTime);
boat.simulate(deltaTime);
for (LochNess lochNess : lochNesses) {
lochNess.simulate(deltaTime);
if (lochNess.getBoundingBox().intersects(boat.getBoundingBox())) {
lochNess.changeDirection();
for (DrawableSimulable drawableSimulable : sceneEntitites) {
drawableSimulable.simulate(deltaTime);
}
for (int i = 0; i < sceneEntitites.length; i++) {
if (sceneEntitites[i] instanceof Collisionable c1) {
for (int j = i + 1; j < sceneEntitites.length; j++) {
if (sceneEntitites[j] instanceof Collisionable c2) {
if (c1.intersect(c2.getBoundingBox())) {
c1.hitBy(c2);
c2.hitBy(c1);
}
}
}
}
}
}
......
package lab;
import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
public abstract class WorldEntity implements DrawableSimulable {
protected Scene scene;
protected Point2D position;
public WorldEntity(Scene scene, Point2D position) {
this.scene = scene;
this.position = position;
}
public Point2D getPosition() {
return position;
}
public Scene getScene() {
return scene;
}
@Override
public final void draw(GraphicsContext gc) {
gc.save();
drawInternal(gc);
gc.restore();
}
public abstract void drawInternal(GraphicsContext gc);
}
package jez04.structure.test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.reflections.Configuration;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;
import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.GraphicsContext;
class ClassStructureTest {
private static final String drawableSimulableName = "DrawableSimulable";
private static final String collisionableName = "Collisionable";
Set<String> allClasses = getNameOfAllClasses();
@Test
void interfaceExistencePropertiesTest() {
classExist(drawableSimulableName);
Class<?> c = getClass(drawableSimulableName);
isInterface(c);
hasMethod(c, "draw", void.class, GraphicsContext.class);
hasMethod(c, "simulate", void.class);
}
@Test
void interface2ExistencePropertiesTest() {
classExist(collisionableName);
Class<?> c = getClass(collisionableName);
isInterface(c);
hasMethod(c, "getBoundingBox", Rectangle2D.class);
hasMethod(c, "intersect", boolean.class, Rectangle2D.class);
hasMethod(c, "hitBy", void.class, c);
}
@Test
void movingClassTest() {
classExist("LochNess");
Class<?> c = getClass("LochNess");
hasImplements(c, collisionableName);
hasExtends(c, "WorldEntity");
hasMethod(c, "draw", void.class, GraphicsContext.class);
hasMethod(c, "drawInternal", void.class, GraphicsContext.class);
}
@Test
void abstractClassTest() {
classExist("WorldEntity");
Class<?> c = getClass("WorldEntity");
hasImplements(c, drawableSimulableName);
hasMethod(c, true, false, "draw", void.class, GraphicsContext.class);
hasMethod(c, false, true, "drawInternal", void.class, GraphicsContext.class);
}
@Test
void groupClassPropertyTest() {
classExist("lab.Scene");
Class<?> c = getClass("lab.Scene");
hasMethod(c, "draw");
hasMethod(c, "simulate");
Class<?> drawable = getClass(drawableSimulableName);
hasProperty(c, ".*", drawable, true);
}
@Test
void drawingClassExistenceTest() {
Set<String> allClasses = getNameOfAllClasses();
int detectedClassCount = 0;
for (String className : allClasses) {
try {
Class<?> clazz = Class.forName(className);
List<Constructor<?>> constructorList = Arrays.asList(clazz.getConstructors());
List<Method> methodList = Arrays.asList(clazz.getMethods());
long drawMethodCount = methodList.stream().filter(m -> m.getName().contains("draw")).count();
long simulateMethodCount = methodList.stream().filter(m -> m.getName().contains("sim")).count();
if (!constructorList.isEmpty() && (drawMethodCount > 0 || simulateMethodCount > 0)) {
System.out.println("DETECT:" + className);
detectedClassCount++;
}
} catch (ClassNotFoundException e) {
System.out.println(String.format("Class '%s' cannot be loaded: %s", className, e.getMessage()));
}
}
assertTrue(detectedClassCount >= 2, "");
}
private void isInterface(Class<?> c) {
assertTrue(c.isInterface(), c.getName() + " have to be interface.");
}
private void classExist(String name) {
assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Interface " + name + " not found");
}
private Class<?> getClass(String name) {
String className = allClasses.stream().filter(c -> c.endsWith(name)).findAny().orElse(null);
if (className == null) {
Assertions.fail("Class " + name + " not found.");
}
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream ps = new PrintStream(baos, true)) {
e.printStackTrace(ps);
} catch (Exception e2) {
Assertions.fail(e2.getMessage());
}
String stackTrace = baos.toString();
Assertions.fail("Class " + name + " not found.\n" + stackTrace);
return null;
}
}
private void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) {
List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
assertTrue(fields.stream().anyMatch(f -> {
if (f.getName().matches(propertyNameRegexp)) {
if (array) {
return f.getType().isArray() && f.getType().getComponentType().equals(type);
} else {
return f.getType().equals(type);
}
}
return false;
}), "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class "
+ classDef.getName());
}
private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) {
List<Method> methods = Arrays.asList(interfaceDef.getMethods());
assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
assertTrue(
methods.stream().filter(m -> m.getName().contains(methodName))
.anyMatch(m -> m.getReturnType().equals(returnType)),
"Method " + methodName + " not return " + returnType.getName());
}
private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) {
List<Method> methods = Arrays.asList(interfaceDef.getMethods());
assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
assertTrue(
methods.stream().filter(m -> m.getName().contains(methodName))
.filter(m -> m.getReturnType().equals(returnType))
.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
"Method " + methodName + " has no all parrams:"
+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
}
private void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName,
Class<?> returnType, Class<?>... params) {
List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
assertTrue(
methods.stream().filter(m -> m.getName().contains(methodName))
.filter(m -> m.getReturnType().equals(returnType)
&& (Modifier.isAbstract(m.getModifiers()) == abstractTag)
&& (Modifier.isFinal(m.getModifiers()) == finalTag))
.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
"Method " + methodName + " has no all params:"
+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
}
private void hasImplements(Class<?> clazz, String... interfaceNames) {
List<Class<?>> interfaces = new ArrayList<>();
Arrays.asList(interfaceNames).stream().map(name -> getClass(name)).forEach(c -> interfaces.add(c));
assertTrue(Arrays.asList(clazz.getInterfaces()).containsAll(interfaces), "Class not implements all interfaces:"
+ interfaces.stream().map(Class::getName).collect(Collectors.joining(", ")));
}
private void hasExtends(Class<?> clazz, String parentName) {
Class<?> parent = getClass(parentName);
assertTrue(clazz.getSuperclass().equals(parent),
"Class " + clazz.getName() + " not extends class " + parentName);
}
private void hasMethod(Class<?> interfaceDef, String methodName) {
List<Method> methods = Arrays.asList(interfaceDef.getMethods());
assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
}
private Set<String> getNameOfAllClasses() {
List<String> initClassesName = Arrays.asList("lab.Routines", "lab.App", "lab.DrawingThread");
for (String className : initClassesName) {
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
System.out.println(String.format("Class '%s' cannot be loaded: %s", className, e.getMessage()));
}
}
Set<String> allClasses = new HashSet<>();
for (Package p : Package.getPackages()) {
if (p.getName().startsWith("java.") || p.getName().startsWith("com.") || p.getName().startsWith("jdk.")
|| p.getName().startsWith("javafx.") || p.getName().startsWith("org.")
|| p.getName().startsWith("sun.") || p.getName().startsWith("javax.")
|| p.getName().startsWith("javassist")) {
continue;
}
System.out.println(p.getName());
Configuration conf = new ConfigurationBuilder().addScanners(Scanners.SubTypes.filterResultsBy(pc -> true))
.forPackages(p.getName());
Reflections reflections = new Reflections(conf);
allClasses.addAll(reflections.getAll(Scanners.SubTypes.filterResultsBy(c -> {
System.out.println(c);
return true;
})));
}
return allClasses;
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment