Commit 2bd83405 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Initial code from the previous year.

parent 788a3c3a
PB162 Java
=============
## Třetí iterace
At the left upper part you can see the list of branches with the assignments.
Cvičení zaměřené na přetěžování vlastních konstruktorů a metod.
You can find all necessary information [here](https://gitlab.fi.muni.cz/pb162/pb162-course-info/wikis/home).
1. Ve třídě `Vertex2D`:
* Udělejte třídu `Vertex2D` neměnnou (_immutable_), tj. odstraňte settery a nastavte všechny attributy `final`.
* Přidejte metodu `double distance(Vertex2D vertex)`, která vezme jiný 2D bod jako vstupní parametr a vrátí jeho
eukleidovskou vzdálenost. Vzdálenost bodů se vypočítá jako:
![vzorec](images/03a.png)
* Pokud je vstupní argument `null`, pak metoda vrátí hodnotu `-1.0` jako indikátor chyby (vzdálenost je vždy
nezáporná).
**Private information for teachers is [here](https://gitlab.fi.muni.cz/pb162/pb162-teachers-info/wikis/home)**.
2. Vytvořte třídu `Circle`.
* Třída bude mít konstruktor se dvěma parametry (v tomto pořadí): _střed_ (center) typu `Vertex2D`
a _poloměr_ (radius) typu `double`.
Atributy budou neměnné.
* Třída bude mít _bezparametrický konstruktor_, který vytvoří jednotkovou kružnici se středem v počátku
souřadného systému (střed `[0, 0]`, poloměr `1`).
* **Bezparametrický konstruktor bude volat parametrický konstruktor** a předá mu potřebné hodnoty.
* Pro poloměr a střed vygenerujte gettery `getRadius()` a `getCenter()`.
* Metoda `toString` bude vracet řetězec ve formátu:
# Current branch `master`
"Circle: center=[<x>, <y>], radius=<radius>"
This branch consist of working _Hello world_ project with the passing tests. Try it!
\ No newline at end of file
kde `<x>` a `<y>` jsou hodnoty příslušných souřadnic středu a `<radius>` je hodnota poloměru.
3. Upravte třídu `Triangle` následujícím způsobem:
* Odstraňte setter, nastavte atributy jako `final`.
Třída nemůže být neměnná, protože metoda `divide` mění vlastnosti trojúhelníka.
* Přidejte metodu `boolean isEquilateral()`, která vrátí `true`, jestliže je trojúhelník rovnostranný.
Protože pracujeme s reálnými čísly, nelze jednoduše porovnávat délky stran pomocí `d1 == d2`.
Je nutné použít test, který bude považovat dvě reálná čísla za shodná, pokud se liší jen málo:
Math.abs(d1-d2) < 0.001
kde `0.001` je tolerovaná absolutní odchylka a **bude definovaná jako privátní konstanta**.
* Vytvořte přetíženou metodu `boolean divide(int depth)`, která rozdělí trojúhelník na podtrojúhelníky.
Výsledkem bude [_Sierpińského trojúhelník_](http://en.wikipedia.org/wiki/Sierpinski_triangle):
![Sierpińského trojúhelník](images/03b.png)
*Sierpińského trojúhelníky hloubky 0 až 4.*
* Parametr `depth` udává hloubku dělení. Nula značí žádné dělení (jsme na konci rekurze), 1 znamená,
že dojde k jednomu rozdělení původního trojúhelníka, atd.
* Jestli je `depth` nula, rekurze se ukončí vrácením `false` (trojúhelník na úrovni nula je již rozdělen).
* Záporná hodnota je považována za chybu, kterou metoda indikuje tím, že vrátí `false`.
* Metoda použije existující metodu `divide()`, a pak zavolá `divide(int depth)` na svých podtrojúhelnících
s parametrem `depth` o jedna nižší a pak vrátí `true`.
* Vytvořte konstruktor se 4 parametry, čtvrtý parametr reprezentuje hloubku zanoření.
Konstruktor zavolá předešlý konstruktor a pak rozdělí trojúhelník.
4. Po spuštění třídy `Draw` se na obrazovce [vykreslí _Sierpińského trojúhelníky_ hloubky 4 a červená
kružnice](https://gitlab.fi.muni.cz/pb162/pb162-course-info/wikis/draw-images).
### Hinty
- Metody pro matematické operátory jsou ve třídě `Math`.
Např. odmocnina se vypočítá pomocí statické metody `Math.sqrt()`.
- Volání konstruktoru v konstruktoru se provádí klíčovým slovem `this`.
- Není potřeba volat `toString()`, metoda se zavolá automaticky.
- Konstanta musí být pouze jedna (`static`) a neměnná (`final`).
package cz.muni.fi.pb162.project.demo;
import cz.muni.fi.pb162.project.geometry.Circle;
import cz.muni.fi.pb162.project.geometry.Triangle;
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
*/
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 TRIANGLE_COLOR = Color.BLUE;
private static final Circle CIRCLE = new Circle(new Vertex2D(0, -40), 200);
private static final Triangle DIVIDED_TRIANGLE = new Triangle(
new Vertex2D(-160, -160),
new Vertex2D(0, 160),
new Vertex2D(160, -160),
4);
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();
paintTriangle(DIVIDED_TRIANGLE);
paintSubTrianglesRecursively(DIVIDED_TRIANGLE);
paintCircle(CIRCLE);
}
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 void paintTriangle(Triangle triangle) {
if (triangle == null) return;
graphics.setColor(TRIANGLE_COLOR);
Polygon polygon = new Polygon();
for (int i = 0; i <= 2; i++) {
AbstractMap.SimpleEntry<Integer, Integer> pair = createTriangleLinePoints(triangle, i);
polygon.addPoint(pair.getKey(), pair.getValue());
}
graphics.drawPolygon(polygon);
}
private AbstractMap.SimpleEntry<Integer, Integer> createTriangleLinePoints(Triangle triangle, int index) {
int a1 = PANEL_WIDTH - ((int) Math.rint(HALF_WIDTH - triangle.getVertex(index).getX()));
int a2 = (int) Math.rint(HALF_HEIGHT - triangle.getVertex(index).getY());
return new AbstractMap.SimpleEntry<>(a1, a2);
}
private void paintSubTrianglesRecursively(Triangle triangle) {
for (int i = 0; i < 3; i++) {
Triangle subTriangle = triangle.getSubTriangle(i);
paintTriangle(subTriangle);
if (subTriangle.isDivided()) paintSubTrianglesRecursively(subTriangle);
}
}
private void paintCircle(Circle 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.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 =
"Triangle: vertices=[-100.0, 0.0] [0.0, 100.0] [100.0, -100.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;
/**
* 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;
@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() {
Circle actual = new Circle();
assertThat(actual.getRadius()).isEqualTo(1);
assertThat(actual.getCenter()).isEqualToComparingFieldByField(new Vertex2D(0, 0));
}
@Test
public void toStringMessage() {
assertThat(circle.toString())
.isEqualTo("Circle: center=" + circle.getCenter() + ", radius=" + circle.getRadius());
}
}
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;
/**
* 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 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);
}
private void checkDivisionDepth1(Triangle triangle) {
assertSubTriangles(triangle, new Triangle[]{
new Triangle(
new Vertex2D(-100.0, -100.0),
new Vertex2D(0.0, -100.0),
new Vertex2D(-50.0, 0.0)
),
new Triangle(
new Vertex2D(0.0, 100.0),
new Vertex2D(-50.0, 0.0),
new Vertex2D(50.0, 0.0)
),
new Triangle(
new Vertex2D(100.0, -100.0),
new Vertex2D(50.0, 0.0),
new Vertex2D(0.0, -100.0)
)
});
}
@Test
public void nestedDivisionNegativeInput() {
assertThat(triangle.divide(-5)).isFalse();
assertThat(triangle.divide(-1)).isFalse();
}
@Test
public void nestedDivisionZeroInput() {
assertThat(triangle.divide(0)).isFalse();
}
@Test
public void nestedDivisionDepth1() {
assertThat(triangle.divide(1)).isTrue();
checkDivisionDepth1(triangle);
}
@Test
public void nestedDivisionConstructor() {
Triangle t = new Triangle(triangle.getVertex(0), triangle.getVertex(1), triangle.getVertex(2), 2);
assertNestedDivision2(t);
}
@Test
public void nestedDivisionDepth2() {
Triangle second = copyTriangle(triangle);
assertThat(second.divide(2)).isTrue();
assertNestedDivision2(second);
}
// TODO cannot check depth 1, problem with equals: checkDivisionDepth1(actual);
private void assertNestedDivision2(Triangle actual) {
assertThat(triangle.divide(1)).isTrue();
// divide second depth manually
triangle.getSubTriangle(0).divide();
triangle.getSubTriangle(1).divide();
triangle.getSubTriangle(2).divide();
assertSubTriangles(actual.getSubTriangle(0), triangle.getSubTriangle(0));
assertSubTriangles(actual.getSubTriangle(1), triangle.getSubTriangle(1));
assertSubTriangles(actual.getSubTriangle(2), triangle.getSubTriangle(2));
}
private void assertSubTriangles(Triangle actual, Triangle expected) {
assertSubTriangles(actual, new Triangle[]{
expected.getSubTriangle(0),
expected.getSubTriangle(1),
expected.getSubTriangle(2)
});
}
private void assertSubTriangles(Triangle actual, Triangle[] subTriangles) {
Triangle[] actualTriangles = new Triangle[]{
actual.getSubTriangle(0),
actual.getSubTriangle(1),
actual.getSubTriangle(2)
};
assertThat(actualTriangles)
.usingElementComparator((t1, t2) -> {
if (t1 == null && t2 == null) return 0;
if (t1 == null || t2 == null) return 1;
return getHashCode(t1) - getHashCode(t2);
})
.containsExactlyInAnyOrder(subTriangles);
}
// TODO dirty workaround to simulate Triangle params permutation
private int getHashCode(Triangle t) {
int hash = 7;
hash += 31 * t.getVertex(0).getX() + t.getVertex(0).getY();
hash += 31 * t.getVertex(1).getX() + t.getVertex(1).getY();
hash += 31 * t.getVertex(2).getX() + t.getVertex(2).getY();
if (!t.isDivided()) return hash;
hash += t.getSubTriangle(0).hashCode();
hash += t.getSubTriangle(1).hashCode();
hash += t.getSubTriangle(2).hashCode();
return hash;
}
private Triangle copyTriangle(Triangle triangle) {
return new Triangle(triangle.getVertex(0), triangle.getVertex(1), triangle.getVertex(2));
}
}
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.assertThatCode;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
/**
* Simple Vertex2D tests.
*
* @author Marek Sabo
*/
public class Vertex2DTest {
private Vertex2D vertex2D;
private static final double X = -1.2;
private static final double Y = 2.4;
@Before
public void setUp() {
vertex2D = new Vertex2D(X, Y);
}
@Test
public void finalAttributes() {
BasicRulesTester.attributesFinal(Vertex2D.class);
}
@Test
public void distanceValidInput() {
double distance = new Vertex2D(-1.2, 1.2).distance(new Vertex2D(1.3, 1.3));
assertThat(distance).isBetween(2.5, 2.503);
}
@Test
public void distanceNullInput() {
assertThatCode(
() -> assertThat(vertex2D.distance(null)).isEqualTo(-1.0)
)
.as("Should return -1 as indicator of wrong input")
.doesNotThrowAnyException();
}
@Test
public void getters() {
assertThat(vertex2D.getX()).isEqualTo(X);
assertThat(vertex2D.getY()).isEqualTo(Y);
}
@Test
public void checkToString() {
assertThat(vertex2D.toString()).isEqualTo("[" + X + ", " + Y + "]");
}
@Test
public void createMiddle() {
Vertex2D v1 = new Vertex2D(-1.2, 2.4);
Vertex2D v2 = new Vertex2D(-0.8, 2.6);
Vertex2D res = new Vertex2D(-1, 2.5);
assertThat(v1.createMiddle(v2)).isEqualToComparingFieldByField(res);
}
}
Supports Markdown
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