Commit 5c4c1af2 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Initial code from the previous year.

parent 788a3c3a
PB162 Java
=============
## Čtvrtá iterace
At the left upper part you can see the list of branches with the assignments.
Cvičení zaměřené na statické metody, implementaci a použití rozhraní.
You can find all necessary information [here](https://gitlab.fi.muni.cz/pb162/pb162-course-info/wikis/home).
1. Vytvořte třídu `SimpleMath` v balíku `cz.muni.fi.pb162.project.utils`.
* Třída bude implementovat pouze _statické_ metody.
* Statická metoda `double minX(Triangle triangle)` vrátí nejmenší X-ovou souřadnici.
* Statická metoda `double minY(Triangle triangle)` vrátí nejmenší Y-ovou souřadnici.
* Obdobně pro metody `maxX` a `maxY`.
* Trojúhelník neobsahuje `null` prvky.
**Private information for teachers is [here](https://gitlab.fi.muni.cz/pb162/pb162-teachers-info/wikis/home)**.
2. Upravte třídy `Triangle` a `Circle` tak, aby implementovaly rozhraní `Measurable`.
* Výška/šířka trojúhelníku se vypočítá jako rozdíl maximální a minimální x-ové (u šířky) respektive y-ové
(u výšky) souřadnice vrcholů:
# Current branch `master`
![šířka objektů](images/04a.png)
* Využijte statické metody ze třídy `SimpleMath`.
This branch consist of working _Hello world_ project with the passing tests. Try it!
\ No newline at end of file
3. V balíku `utils` vytvořte třídu `Gauger` ("měřidlo") se dvěma statickými přetíženými metodami `printMeasurement`:
* První metoda vezme libovolný měřitelný objekt (tj. libovolný objekt implementující rozhraní `Measurable`) a
* na standardní výstup vypíše _"Width: \<w\>"_, kde \<w\> je hodnota šířky,
* na další řádek vypíše _"Height: \<h\>"_, kde \<h\> je hodnota výšky.
* Druhá metoda vezme trojúhelník (objekt typu `Triangle`) a
* na standardní výstup vypíše informace o objektu, viz metoda `toString()`,
* na další řádek vypíše _"Width: \<w\>"_, kde \<w\> je opět hodnota šířky,
* na další řádek vypíše _"Height: \<h\>"_, kde \<h\> je opět hodnota výšky.
* Vyhněte se opakování kódu tím, že druhá varianta metody bude volat tu první. Pozor ale, ať nevolá sebe sama.
Došlo by k zacyklení (`StackOverflowException`).
4. Třída `Circle` bude implementovat rozhraní `Circumcircle` (opsaná kružnice).
Opsanou kružnicí je kružnice sama, proto netřeba implementovat žádné nové metody.
Přidejte jenom anotaci `@Override`.
5. V balíku `geometry` vytvořte třídu `Square`, která reprezentuje čtverec otočený o 45°:
* První konstruktor bude obsahovat souřadnice středu opsané kružnice a průměr opsané kružnice.
* Druhý konstruktor bude obsahovat objekt typu `Circumcircle` (souřadnice středu a poloměr).
* Třída bude taky implementovat rozhraní `Circumcircle`:
* Metoda `Vertex2D getVertex(int index)` vrátí vrchol na daném indexu v pořadí: levý, dolní, pravý, horní.
Jestli je index mimo rozsah, vrátí metoda `null`.
* Nezapomeňte na metodu `toString()`:
"Square: vertices=[ax, ay] [bx, by] [cx, cy] [dx, dy]"
přičemž zpráva obsahuje jen 3 mezery mezi vrcholy.
6. V balíku `geometry` vytvořte třídu `Snowman`:
* Sněhulák se skládá ze **čtyř** opsaných kružnic.
Počet kružnic ale půjde lehce změnit v době překladu.
* Konstruktor bude jako svůj první parametr brát parametr typu `Circumcircle` (spodní kružnice), a jako druhý
parametr zmenšovací faktor (poloměru kružnice) pro kružnice nad ní (reálné číslo o rozsahu `(0..1>`).
V případě, že vstupní parametr nebude z požadovaného rozsahu, použije se neveřejná pojmenovaná konstanta `0.8`.
* První kružnice je první argument konstruktoru, druhá bude položená na první s poloměrem zmenšeným o zmenšovací
faktor, třetí i čtvrtá kružnice bude vytvořena stejným způsobem.
* Celý sněhulák vznikne v konstruktoru, nebojte se ale kód rozdělit do menších privátních metod.
* Metoda `Circumcircle[] getBalls()` vrátí pole kružnic, od nejspodnější po nejvyšší (nejmenší).
7. Demo vytvoří čtverec se středem `[0, 0]`, průměrem kružnice `100` a vypíše o něm informace na standardní výstup.
8. Draw vykreslí [sněhuláka, jehož spodní kružnice má v sobě vepsaný zelený
čtverec](https://gitlab.fi.muni.cz/pb162/pb162-course-info/wikis/draw-images).
### Hinty
- Minimální/maximální hodnota musí být nainicializovaná na první prvek, nebo na konstanty
`Double.POSITIVE_INFINITY`/`Double.NEGATIVE_INFINITY`.
- Při implementaci metod rozhraní používejte anotaci `@Override`.
- Při volání `printMeasurement` je nutno přetypovat objekt na rozhraní. Dojde k tzv. "ořezání" metod.
- Konstruktor `Square` bere konkrétní implementace rozhraní `Circumcircle`, představte si tam např. `Circle`.
- V `Square` v metodě `toString` se dá použít `Stringbuilder` a pak metoda `String#trim()`.
- `Snowman` bude obsahovat konstantu udávající kružnic.
package cz.muni.fi.pb162.project.demo;
import cz.muni.fi.pb162.project.geometry.Circumcircle;
import cz.muni.fi.pb162.project.geometry.Snowman;
import cz.muni.fi.pb162.project.geometry.Square;
import cz.muni.fi.pb162.project.geometry.Vertex2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.util.AbstractMap;
/**
* Class drawing 2D objects.
*
* @author Radek Oslejsek, Marek Sabo
*/
@SuppressWarnings("SameParameterValue")
public final class Draw extends JFrame {
private static final int PANEL_WIDTH = 800;
private static final int PANEL_HEIGHT = 600;
private static final int HALF_WIDTH = PANEL_WIDTH / 2;
private static final int HALF_HEIGHT = PANEL_HEIGHT / 2;
private static final Color CIRCLE_COLOR = Color.RED;
private static final Color SQUARE_COLOR = Color.GREEN;
private static final Square SQUARE = new Square(new Vertex2D(0,-160), 160);
private static final Snowman SNOWMAN = new Snowman(SQUARE, 0.8);
private Graphics graphics;
/**
* Draws 2D objects.
*
* @param args command line arguments, will be ignored
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(Draw::new);
}
private Draw() {
setBounds(350, 250, PANEL_WIDTH, PANEL_HEIGHT);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setTitle("Draw");
JPanel panel = new JPanel() {
@Override
public void paint(Graphics g) {
super.paint(g);
paintScene(g);
}
};
panel.setBackground(Color.WHITE);
add(panel);
setVisible(true);
}
private void paintScene(Graphics g) {
graphics = g;
paintCross();
paintSquare(SQUARE);
paintSnowman(SNOWMAN);
}
private void paintCross() {
graphics.setColor(Color.LIGHT_GRAY);
graphics.drawLine(0, HALF_HEIGHT, PANEL_WIDTH, HALF_HEIGHT);
graphics.drawLine(HALF_WIDTH, 0, HALF_WIDTH, PANEL_HEIGHT);
}
private AbstractMap.SimpleEntry<Integer, Integer> createLinePoints(Square square, int index) {
int a1 = PANEL_WIDTH - ((int) Math.rint(HALF_WIDTH - square.getVertex(index).getX()));
int a2 = (int) Math.rint(HALF_HEIGHT - square.getVertex(index).getY());
return new AbstractMap.SimpleEntry<>(a1, a2);
}
private void paintSquare(Square square) {
if (square == null) return;
graphics.setColor(SQUARE_COLOR);
Polygon polygon = new Polygon();
for (int i = 0; i < 4; i++) {
AbstractMap.SimpleEntry<Integer, Integer> pair = createLinePoints(square, i);
polygon.addPoint(pair.getKey(), pair.getValue());
}
graphics.drawPolygon(polygon);
}
private void paintSnowman(Snowman snowman) {
for(Circumcircle c : snowman.getBalls()) {
paintCircumcircle(c);
}
}
private void paintCircumcircle(Circumcircle c) {
int radius = (int) Math.rint(c.getRadius());
int x = PANEL_WIDTH - ((int) Math.rint(HALF_WIDTH - c.getCenter().getX()) + radius);
int y = (int) Math.rint(HALF_HEIGHT - c.getCenter().getY()) - radius;
int diameter = (int) Math.rint(c.getRadius() * 2.0);
graphics.setColor(CIRCLE_COLOR);
graphics.drawOval(x, y, diameter, diameter);
}
}
\ No newline at end of file
package cz.muni.fi.pb162.project.geometry;
/**
* Class represents circle that passes through every vertex of 2d geometry object.
*
* @author Marek Sabo
*/
public interface Circumcircle {
/**
* Returns the middle point of the circle.
*
* @return the middle point.
*/
Vertex2D getCenter();
/**
* Returns the radius of the circumcircle.
*
* @return circle's radius
*/
double getRadius();
}
package cz.muni.fi.pb162.project.geometry;
/**
* Measurable objects, i.e. 2D objects with width and height.
*
* @author Radek Oslejsek, Marek Sabo
*/
public interface Measurable {
/**
* Returns the width of the object,
* i.e. the width of the smallest bounding rectangle.
*
* @return object's width
*/
double getWidth();
/**
* Returns the height of the object,
* i.e. the height of the smallest bounding rectangle.
*
* @return object's height
*/
double getHeight();
}
package cz.muni.fi.pb162.project.demo;
import cz.muni.fi.pb162.project.Demo;
import cz.muni.fi.pb162.project.helper.OutputTester;
import org.junit.Test;
......@@ -13,7 +12,8 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class DemoTest {
private static final String EXPECTED_OUTPUT = "Hello world!" + System.lineSeparator();
private static final String EXPECTED_OUTPUT =
"Square: vertices=[-50.0, 0.0] [0.0, -50.0] [50.0, 0.0] [0.0, 50.0]" + System.lineSeparator();
@Test
public void testMainOutput() {
......
package cz.muni.fi.pb162.project.geometry;
import cz.muni.fi.pb162.project.helper.BasicRulesTester;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.within;
/**
* Tests Circle class.
*
* @author Marek Sabo
*/
public class CircleTest {
private static final Vertex2D CENTER = new Vertex2D(2, 1);
private static final double RADIUS = 2.5;
private Circle circle;
public static void assertCircle(Circumcircle actual, Circumcircle expected) {
assertThat(actual.getRadius()).isCloseTo(expected.getRadius(), within(0.001));
assertThat(actual.getCenter()).isEqualToComparingFieldByField(expected.getCenter());
}
@Before
public void setUp() {
circle = new Circle(CENTER, RADIUS);
}
@Test
public void attributes2AndFinal() {
BasicRulesTester.attributesAmount(Circle.class, 2);
BasicRulesTester.attributesFinal(Circle.class);
}
@Test
public void getters() {
assertThat(circle.getRadius()).isEqualTo(RADIUS);
assertThat(circle.getCenter()).isEqualToComparingFieldByField(CENTER);
}
@Test
public void constructorWithoutParameters() {
assertCircle(new Circle(), new Circle(new Vertex2D(0, 0), 1));
}
@Test
public void toStringMessage() {
assertThat(circle.toString())
.isEqualTo("Circle: center=" + circle.getCenter() + ", radius=" + circle.getRadius());
}
@Test
public void width() {
assertThat(circle.getWidth()).isEqualTo(2 * RADIUS);
}
@Test
public void height() {
assertThat(circle.getHeight()).isEqualTo(2 * RADIUS);
}
}
package cz.muni.fi.pb162.project.geometry;
import org.junit.Test;
import static cz.muni.fi.pb162.project.geometry.CircleTest.assertCircle;
/**
* Tests snowman class.
*
* @author Marek Sabo
*/
public class SnowmanTest {
private Circle bottomBall = new Circle(new Vertex2D(0, 0), 100);
private Circle secondBall = new Circle(new Vertex2D(0, 180), 80);
private Circle thirdBall = new Circle(new Vertex2D(0, 324), 64);
private Circle topBall = new Circle(new Vertex2D(0, 439.2), 51.2);
@Test
public void testDefaultDecreasingFactor() {
assertSnowman(new Snowman(bottomBall, 0.8));
assertSnowman(new Snowman(bottomBall, 0));
assertSnowman(new Snowman(bottomBall, 1.001));
assertSnowman(new Snowman(bottomBall, -1));
}
private void assertSnowman(Snowman snowman) {
assertCircle(snowman.getBalls()[0], bottomBall);
assertCircle(snowman.getBalls()[1], secondBall);
assertCircle(snowman.getBalls()[2], thirdBall);
assertCircle(snowman.getBalls()[3], topBall);
}
}
package cz.muni.fi.pb162.project.geometry;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Java6Assertions.within;
/**
* Tests 2D square class.
*
* @author Marek Sabo
*/
public class SquareTest {
private static final int CENTER_X = 12;
private static final int CENTER_Y = 6;
private static final Vertex2D CENTER = new Vertex2D(CENTER_X, CENTER_Y);
private static final double EDGE_LENGTH = 10;
private static final double HALF_EDGE = EDGE_LENGTH / 2.0;
private static final double DELTA = 0.001;
private final Square square1 = new Square(CENTER, EDGE_LENGTH);
private final Square square2 = new Square(new Circle(CENTER, HALF_EDGE));
private final Vertex2D[] vertices = new Vertex2D[] {
new Vertex2D(CENTER_X - HALF_EDGE, CENTER_Y), // left
new Vertex2D(CENTER_X, CENTER_Y - HALF_EDGE), // bottom
new Vertex2D(CENTER_X + HALF_EDGE, CENTER_Y), // right
new Vertex2D(CENTER_X, CENTER_Y + HALF_EDGE), // top
};
@Test
public void constructor1() {
testGetterOutOfRange(square1);
testGetterInRange(square1);
}
@Test
public void constructor2() {
testGetterOutOfRange(square2);
testGetterInRange(square2);
}
private void testGetterOutOfRange(Square square) {
assertThat(square.getVertex(-1)).isNull();
assertThat(square.getVertex(4)).isNull();
assertThat(square.getVertex(20)).isNull();
}
private void testGetterInRange(Square square) {
for (int i = 0; i < 4; i++) {
assertThat(square.getVertex(i)).isEqualToComparingFieldByField(vertices[i]);
}
}
@Test
public void getCenter() {
assertThat(square1.getCenter()).isEqualToComparingFieldByField(CENTER);
assertThat(square2.getCenter()).isEqualToComparingFieldByField(CENTER);
}
@Test
public void getRadius() {
assertThat(square1.getRadius()).isCloseTo(HALF_EDGE, within(DELTA));
assertThat(square2.getRadius()).isCloseTo(HALF_EDGE, within(DELTA));
}
}
package cz.muni.fi.pb162.project.geometry;
import cz.muni.fi.pb162.project.helper.BasicRulesTester;
import cz.muni.fi.pb162.project.utils.SimpleMath;
import cz.muni.fi.pb162.project.utils.SimpleMathTest;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.within;
/**
* Class testing Triangle implementation.
*
* @author Marek Sabo
*/
public class TriangleTest {
private Triangle triangle;
private final Vertex2D vertex1 = new Vertex2D(-100, -100);
private final Vertex2D vertex2 = new Vertex2D(0, 100);
private final Vertex2D vertex3 = new Vertex2D(100, -100);
@Before
public void setUp() {
triangle = new Triangle(vertex1, vertex2, vertex3);
}
@Test
public void finalAttributes() {
BasicRulesTester.attributesFinal(Triangle.class);
}
@Test
public void gettersInRange() {
assertThat(triangle.getVertex(0)).isEqualToComparingFieldByField(vertex1);
assertThat(triangle.getVertex(1)).isEqualToComparingFieldByField(vertex2);
assertThat(triangle.getVertex(2)).isEqualToComparingFieldByField(vertex3);
assertThat(triangle).isEqualToComparingFieldByField(new Triangle(vertex1, vertex2, vertex3));
}
@Test
public void gettersOutOfRange() {
assertThat(triangle.getVertex(3)).isNull();
assertThat(triangle.getVertex(4)).isNull();
assertThat(triangle.getVertex(-1)).isNull();
}
@Test
public void toStringMessage() {
assertThat(triangle.toString()).isEqualTo("Triangle: vertices=[-100.0, -100.0] [0.0, 100.0] [100.0, -100.0]");
Triangle t = new Triangle(
new Vertex2D(-1.2, 0.0),
new Vertex2D(1.2, 0.0),
new Vertex2D(0.0, 2.07846097)
);
assertThat(t.toString()).isEqualTo("Triangle: vertices=[-1.2, 0.0] [1.2, 0.0] [0.0, 2.07846097]");
}
@Test
public void testAllWidths() {
testWidth(triangle);
testWidth(new Triangle(new Vertex2D(-3, -1), new Vertex2D(-2, -2), new Vertex2D(-1, -1)));
}
@Test
public void testAllHeights() {
testHeight(triangle);
testHeight(new Triangle(new Vertex2D(-3, -1), new Vertex2D(-2, -2), new Vertex2D(-1, -1)));
}
private void testWidth(Triangle t) {
assertThat(t.getWidth()).isCloseTo(SimpleMathTest.triangleWidth(t), within(0.001));
}
private void testHeight(Triangle t) {
assertThat(t.getHeight()).isCloseTo(SimpleMathTest.triangleHeight(t), within(0.001));
}
@Test
public void equilateralTriangle() {
Triangle t = new Triangle(
new Vertex2D(-1.2, 0),
new Vertex2D(1.2, 0),
new Vertex2D(0, 2.07846097)
);
assertThat(t.isEquilateral()).isTrue();
}
@Test
public void nonEquilateralTriangle1() {
Triangle t = new Triangle(
new Vertex2D(-10, 0),
new Vertex2D(0, 0),
new Vertex2D(0, 10)
);
assertThat(t.isEquilateral()).isFalse();
}
@Test
public void nonEquilateralTriangle2() {
Triangle t = new Triangle(
new Vertex2D(-10, 0),
new Vertex2D(3, 2),
new Vertex2D(1, 10)
);
assertThat(t.isEquilateral()).isFalse();
}
@Test
public void checkIfDivided() {
assertThat(triangle.isDivided()).isFalse();
assertThat(triangle.divide()).isTrue();
assertThat(triangle.isDivided()).isTrue();
assertThat(triangle.divide()).isFalse();
}
@Test
public void subTriangleGettersNull() {
assertThat(triangle.getSubTriangle(-1)).isNull();
assertThat(triangle.getSubTriangle(0)).isNull();
assertThat(triangle.getSubTriangle(1)).isNull();
assertThat(triangle.getSubTriangle(2)).isNull();
assertThat(triangle.getSubTriangle(3)).isNull();
}
@Test
public void subTriangleGettersInRangeNotNull() {
triangle.divide();
assertThat(triangle.getSubTriangle(0)).isNotNull();
assertThat(triangle.getSubTriangle(1)).isNotNull();
assertThat(triangle.getSubTriangle(2)).isNotNull();
}
@Test
public void subTriangleGettersOutOfRangeNull() {
triangle.divide();
assertThat(triangle.getSubTriangle(-1)).isNull();
assertThat(triangle.getSubTriangle(3)).isNull();
}
@Test
public void division() {
triangle.divide();
checkDivisionDepth1(triangle);
}