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

feat: :tada: solution lab05

parent a78c57d6
Branches
No related merge requests found
package lab;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
......@@ -16,31 +15,28 @@ import javafx.stage.WindowEvent;
*/
public class App extends Application {
private GameController gameController;
public static void main(String[] args) {
launch(args);
}
private Canvas canvas;
private AnimationTimer timer;
@Override
public void start(Stage primaryStage) {
try {
System.out.println(getClass().getResource("/lab/LochNess.gif"));
// Construct a main window with a canvas.
Group root = new Group();
canvas = new Canvas(800, 400);
root.getChildren().add(canvas);
Scene scene = new Scene(root, 800, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
FXMLLoader gameLoader = new FXMLLoader(getClass().getResource("/lab/gameWindow.fxml"));
Parent root = gameLoader.load();
GameController gameController = gameLoader.getController();
Scene scene = new Scene(root);
String css = this.getClass().getResource("/lab/application.css").toExternalForm();
scene.getStylesheets().add(css);
primaryStage.setScene(scene);
primaryStage.resizableProperty().set(false);
primaryStage.setTitle("Java 1 - 1th laboratory");
primaryStage.show();
// Exit program when main window is closed
primaryStage.setOnCloseRequest(this::exitProgram);
timer = new DrawingThread(canvas);
timer.start();
} catch (Exception e) {
e.printStackTrace();
}
......@@ -48,7 +44,7 @@ public class App extends Application {
@Override
public void stop() throws Exception {
timer.stop();
gameController.stop();
super.stop();
}
......
......@@ -54,4 +54,8 @@ public class Background extends WorldEntity {
}
}
}
public void setSpeed(double doubleValue) {
speed = new Point2D(doubleValue, 0);
}
}
......@@ -49,4 +49,10 @@ public class Boat extends WorldEntity implements Collisionable {
}
}
public void setPosInPercentage(double doubleValue) {
position = new Point2D(
position.getX(),
scene.getSize().getHeight()*doubleValue/100);
}
}
......@@ -45,4 +45,8 @@ public class DrawingThread extends AnimationTimer {
gc.strokeText(Double.toString(1_000_000_000d/timeDelta), 10, 50);
}
public Scene getScene() {
return scene;
}
}
package lab;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Slider;
public class GameController {
@FXML
private Slider boatPosition;
private DrawingThread timer;
@FXML
private Canvas canvas;
@FXML
private Slider speed;
@FXML
void changePosition(ActionEvent event) {
timer.getScene().getRock().changePosition();
}
@FXML
void initialize() {
assert boatPosition != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'.";
assert canvas != null : "fx:id=\"canvas\" was not injected: check your FXML file 'gameWindow.fxml'.";
assert speed != null : "fx:id=\"speed\" was not injected: check your FXML file 'gameWindow.fxml'.";
timer = new DrawingThread(canvas);
timer.start();
boatPosition.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
timer.getScene().getBoat().setPosInPercentage(newValue.doubleValue());
}
});
speed.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
timer.getScene().getBackground().setSpeed(newValue.doubleValue());
}
});
}
public void stop() {
timer.stop();
}
}
package lab;
import java.util.Random;
import javafx.geometry.Dimension2D;
import javafx.geometry.Point2D;
import javafx.scene.canvas.GraphicsContext;
......@@ -9,6 +11,7 @@ import javafx.scene.transform.Rotate;
public class Rock extends WorldEntity {
private static final Random RANDOM = new Random();
private Dimension2D size;
private double angle = 0;
......@@ -36,4 +39,10 @@ public class Rock extends WorldEntity {
angle += 0.5;
}
public void changePosition() {
position = new Point2D(
RANDOM.nextDouble(scene.getSize().getWidth()*0.3, scene.getSize().getWidth()*0.9),
RANDOM.nextDouble(scene.getSize().getHeight()*0.5, scene.getSize().getHeight()*0.95));
}
}
......@@ -9,12 +9,19 @@ public class Scene {
private Dimension2D size;
private DrawableSimulable[] sceneEntitites;
private Boat boat;
private Rock rock;
private Background background;
public Scene(double width, double height) {
size = new Dimension2D(width, height);
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));
background = new Background(this);
rock = new Rock(this, new Point2D(300, 300), new Dimension2D(30, 50));
boat = new Boat(this, new Point2D(20, 200));
sceneEntitites[0] = background;
sceneEntitites[1] = rock;
sceneEntitites[2] = boat;
for (int i = 3; i < sceneEntitites.length; i++) {
sceneEntitites[i] = new LochNess(this);
}
......@@ -48,4 +55,16 @@ public class Scene {
}
}
public Boat getBoat() {
return boat;
}
public Rock getRock() {
return rock;
}
public Background getBackground() {
return background;
}
}
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
\ No newline at end of file
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
#changeButton {
-fx-background-image: url("LochNess.gif");
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="459.0" prefWidth="824.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.GameController">
<bottom>
<HBox alignment="TOP_CENTER" prefHeight="66.0" prefWidth="824.0" BorderPane.alignment="CENTER">
<children>
<Slider fx:id="speed" majorTickUnit="15.0" max="350.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" min="20.0" minorTickCount="5" prefHeight="116.0" prefWidth="717.0" showTickLabels="true" showTickMarks="true" style="-fx-background-image: url('LochNess.gif');" HBox.hgrow="ALWAYS" />
<Button fx:id="changeButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#changePosition" prefHeight="54.0" prefWidth="99.0" text="Change position" textAlignment="CENTER" textFill="#cf4700" textOverrun="CLIP" wrapText="true">
<font>
<Font name="System Bold" size="15.0" />
</font>
<cursor>
<Cursor fx:constant="CROSSHAIR" />
</cursor>
<opaqueInsets>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</opaqueInsets>
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<Canvas fx:id="canvas" height="340.0" width="749.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets />
</BorderPane.margin>
</Canvas>
</center>
<left>
<Slider fx:id="boatPosition" blockIncrement="5.0" majorTickUnit="10.0" max="80.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" min="20.0" minorTickCount="5" orientation="VERTICAL" prefHeight="331.0" prefWidth="38.0" showTickLabels="true" showTickMarks="true" BorderPane.alignment="CENTER" />
</left>
</BorderPane>
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;
......@@ -24,88 +21,63 @@ import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;
import javafx.geometry.Rectangle2D;
import javafx.scene.canvas.GraphicsContext;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
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);
void hasClassWithPropertyWithFxmlAnnotationTest() {
Set<String> names = getNameOfAllClasses();
long annotationCount = 0;
for (String name : names) {
Class<?> c = getClass(name);
annotationCount += countPropertyWithAnnotation(c, ".*", FXML.class);
annotationCount += countMethodWithAnnotation(c, ".*", FXML.class);
}
assertTrue(annotationCount >= 2, "Only " + annotationCount + " FXML annotation found. Requred atleast 2!");
}
@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);
void gameControllerExistenceTest() {
classExist("GameController");
Class<?> c = getClass("GameController");
hasPropertyWithAnnotation(c, ".*", FXML.class);
hasMethodRegexp(c, ".*", void.class, ActionEvent.class);
}
@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);
void gameControllerFxmlTest() {
classExist("GameController");
Class<?> c = getClass("GameController");
hasPropertyWithAnnotation(c, ".*", FXML.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);
void gameControllerActionEventTest() {
classExist("GameController");
Class<?> c = getClass("GameController");
hasMethodRegexp(c, ".*", void.class, ActionEvent.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);
void backgroundExistenceTest() {
classExist("Background");
}
@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, "");
void backgroundExistenceSetAngleTest() {
classExist("Background");
Class<?> c = getClass("Background");
hasMethod(c, "setSpeed");
}
@Test
void backgroundExistenceSetSpeedTest() {
classExist("Background");
Class<?> c = getClass("Background");
hasMethod(c, "setSpeed");
}
private void isInterface(Class<?> c) {
assertTrue(c.isInterface(), c.getName() + " have to be interface.");
}
......@@ -149,8 +121,35 @@ class ClassStructureTest {
+ classDef.getName());
}
private void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) {
List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
assertTrue(
fields.stream().filter(f -> f.getName().matches(propertyNameRegexp))
.flatMap(f -> Arrays.asList(
f.getAnnotations()).stream()).map(a -> a.annotationType()).anyMatch(a ->
a.equals(annotation)),
"No field " + propertyNameRegexp + " with annotation " + annotation.getName() + " in class "
+ classDef.getName());
}
private long countPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) {
List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
long propeertyFxmlAnnotationCount = fields.stream().filter(f -> f.getName().matches(propertyNameRegexp))
.flatMap(f -> Arrays.asList(
f.getAnnotations()).stream()).map(a -> a.annotationType()).filter(a ->
a.equals(annotation)).count();
return propeertyFxmlAnnotationCount;
}
private long countMethodWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) {
List<Method> methods = Arrays.asList(classDef.getDeclaredMethods());
long methodFxmlAnnotationCount = methods.stream().filter(f -> f.getName().matches(propertyNameRegexp))
.flatMap(m -> Arrays.asList(
m.getAnnotations()).stream()).map(a -> a.annotationType()).filter(a ->
a.equals(annotation)).count();
return methodFxmlAnnotationCount;
}
private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) {
List<Method> methods = Arrays.asList(interfaceDef.getMethods());
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))
......@@ -159,7 +158,7 @@ class ClassStructureTest {
}
private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) {
List<Method> methods = Arrays.asList(interfaceDef.getMethods());
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))
......@@ -169,6 +168,17 @@ class ClassStructureTest {
+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
}
private void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) {
List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)), "No method " + methodNameRegexp);
assertTrue(
methods.stream().filter(m -> m.getName().matches(methodNameRegexp))
.filter(m -> m.getReturnType().equals(returnType))
.anyMatch(m ->
Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
"Method " + methodNameRegexp + " 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());
......@@ -218,15 +228,13 @@ class ClassStructureTest {
|| 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;
}
}
}
\ No newline at end of file
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