package nodebox.graphics;
import org.junit.Test;
import java.util.List;
import static junit.framework.TestCase.*;
import static nodebox.graphics.GraphicsTestUtils.addRect;
import static nodebox.graphics.GraphicsTestUtils.assertPointEquals;
/**
* Use cases for geometric operations.
*/
public class PathTest {
public static final double SIDE = GraphicsTestUtils.SIDE;
@Test
public void testEmptyPath() {
Path p = new Path();
assertEquals(0, p.getPoints().size());
}
@Test
public void testMakeEllipse() {
Path p = new Path();
p.ellipse(10, 20, 30, 40);
assertEquals(Rect.centeredRect(10, 20, 30, 40), p.getBounds());
}
// public void testTranslatePoints() {
// Path p = new Path();
// p.rect(10, 20, 30, 40);
// for (Point pt : p.getPoints()) {
// pt.move(5, 0);
// }
// assertEquals(Rect.centeredRect(15, 20, 30, 40), p.getBounds());
// }
// public void testMakeText() {
// Text t = new Text("A", 0, 20);
// Path p = t.getPath();
// // The letter "A" has 2 contours: the outer shape and the "hole".
// assertEquals(2, p.getContours().size());
// }
// public void testBooleanOperations() {
// // Create two overlapping shapes.
// Path p1 = new Path();
// Path p2 = new Path();
// p1.rect(0, 0, 20, 40);
// p2.rect(10, 0, 20, 40);
// Path result = p1.intersected(p2);
// assertEquals(new Rect(10, 0, 10, 40), result.getBounds());
// }
@Test
public void testCustomAttributes() {
// Add a velocity to each point of the path.
Path p = new Path();
p.rect(0, 0, 100, 100);
assertEquals(4, p.getPointCount());
}
@Test
public void testTransform() {
Path p = new Path();
p.rect(10, 20, 30, 40);
p.transform(Transform.translated(5, 7));
assertEquals(Rect.centeredRect(15, 27, 30, 40), p.getBounds());
}
/**
* How easy is it to convert the contours of a path to paths themselves?
*/
@Test
public void testContoursToPaths() {
// Create a path with two contours.
Path p = new Path();
p.rect(0, 0, 100, 100);
p.rect(150, 150, 100, 100);
// Create a new group that will contain the converted contours.
Geometry g = new Geometry();
// Convert each contour to a path of its own.
for (Contour c : p.getContours()) {
g.add(c.toPath());
}
}
@Test
public void testLength() {
testLength(0, 0);
testLength(200, 300);
Path p = new Path();
p.line(0, 0, 50, 0);
p.line(50, 0, 100, 0);
assertEquals(100.0, p.getLength());
}
private void testLength(float x, float y) {
Path p = new Path();
addRect(p, x, y, SIDE, SIDE);
assertEquals(SIDE * 3, p.getLength());
p.close();
assertEquals(SIDE * 4, p.getLength());
}
public void testLengthMultipleContours() {
Path p = new Path();
p.line(0, 0, 100, 0);
assertEquals(100.0, p.getLength());
p.line(0, 100, 100, 100);
assertEquals(200.0, p.getLength());
p.close();
assertEquals(300.0, p.getLength());
}
public void testPointAt() {
Path p = new Path();
p.line(0, 0, 50, 0);
p.line(50, 0, 100, 0);
assertPointEquals(0, 0, p.pointAt(0));
assertPointEquals(10, 0, p.pointAt(0.1));
assertPointEquals(25, 0, p.pointAt(0.25));
assertPointEquals(40, 0, p.pointAt(0.4));
assertPointEquals(50, 0, p.pointAt(0.5));
assertPointEquals(75, 0, p.pointAt(0.75));
assertPointEquals(80, 0, p.pointAt(0.8));
assertPointEquals(100, 0, p.pointAt(1));
}
public void testPointAtMultipleContours() {
// Create two contours that look like a single line.
Path p = new Path();
p.addPoint(0, 0);
p.addPoint(50, 0);
p.newContour();
p.addPoint(50, 0);
p.addPoint(100, 0);
assertEquals(2, p.getContours().size());
assertEquals(4, p.getPointCount());
assertEquals(100.0, p.getLength());
assertPointEquals(0, 0, p.pointAt(0));
assertPointEquals(25, 0, p.pointAt(0.25));
assertPointEquals(50, 0, p.pointAt(0.5));
assertPointEquals(100, 0, p.pointAt(1.0));
}
public void testContour() {
final double SIDE = 50;
Point[] points;
Path p = new Path();
addRect(p, 0, 0, SIDE, SIDE);
assertEquals(SIDE * 3, p.getLength());
points = p.makePoints(7);
assertPointEquals(0, 0, points[0]);
assertPointEquals(SIDE / 2, 0, points[1]);
assertPointEquals(SIDE, 0, points[2]);
assertPointEquals(0, SIDE, points[6]);
// Closing the contour will increase the length of the path and thus will also
// have an effect on point positions.
p.close();
assertEquals(SIDE * 4, p.getLength());
points = p.makePoints(8);
assertEquals(new Point(0, 0), points[0]);
assertPointEquals(SIDE / 2, 0, points[1]);
assertPointEquals(SIDE, 0, points[2]);
assertPointEquals(0, SIDE, points[6]);
assertPointEquals(0, SIDE / 2, points[7]);
}
public void testMultipleContours() {
Point[] points;
// Create a path with separate contours.
// Each contour describes a side of a rectangle.
Path path = new Path();
path.addPoint(0, 0);
path.addPoint(SIDE, 0);
path.newContour();
path.addPoint(SIDE, 0);
path.addPoint(SIDE, SIDE);
path.newContour();
path.addPoint(SIDE, SIDE);
path.addPoint(0, SIDE);
assertEquals(SIDE * 3, path.getLength());
points = path.makePoints(4);
assertPointEquals(0, 0, points[0]);
assertPointEquals(SIDE, 0, points[1]);
assertPointEquals(SIDE, SIDE, points[2]);
assertPointEquals(0, SIDE, points[3]);
// Get the same result by resampling the path.
Path resampled = path.resampleByAmount(4, false);
List<Point> resampledPoints = resampled.getPoints();
assertPointEquals(0, 0, resampledPoints.get(0));
assertPointEquals(SIDE, 0, resampledPoints.get(1));
assertPointEquals(SIDE, SIDE, resampledPoints.get(2));
assertPointEquals(0, SIDE, resampledPoints.get(3));
}
private Path cornerRect(float x, float y, float width, float height) {
Path p = new Path();
p.rect(x + width / 2, y + height / 2, width, height);
return p;
}
public void testIntersected() {
// Create two non-overlapping rectangles.
Path p1 = cornerRect(0, 0, 100, 100);
Path p2 = cornerRect(100, 0, 100, 100);
// The intersection of the two is empty.
assertEquals(new Rect(), p1.intersected(p2).getBounds());
// Create two paths were one is entirely enclosed within the other.
p1 = cornerRect(0, 0, 100, 100);
p2 = cornerRect(20, 30, 10, 10);
// The intersection is the smaller path.
assertEquals(p2.getBounds(), p1.intersected(p2).getBounds());
}
/**
* Path uses a contour length cache to speed up pointAt, makePoints and resample operations.
* Check if the cache is properly invalidated.
*/
public void testCacheInvalidation() {
Path p = new Path();
assertEquals(0.0, p.getLength());
p.line(0, 0, 50, 0);
assertEquals(50.0, p.getLength());
Contour c = new Contour();
c.addPoint(50, 0);
c.addPoint(75, 0);
p.add(c);
assertEquals(75.0, p.getLength());
// Add a point to the contour.
c.addPoint(100, 0);
// This change is not detected by the Path, and thus the length is not updated.
assertEquals(75.0, p.getLength());
// Manually invalidate the path.
p.invalidate();
// This time, the length is correct.
assertEquals(100.0, p.getLength());
}
public void testNegativeBounds() {
Path p1 = new Path();
p1.rect(10, 20, 30, 40);
assertEquals(Rect.centeredRect(10, 20, 30, 40), p1.getBounds());
Path p2 = new Path();
p2.rect(-80, -200, 100, 100);
assertEquals(Rect.centeredRect(-80, -200, 100, 100), p2.getBounds());
}
/**
* Check the bounds for an empty path.
*/
public void testEmptyBounds() {
Path p1 = new Path();
assertEquals(new Rect(), p1.getBounds());
// Construct a path with an empty contour.
Path p2 = new Path();
p2.add(new Contour());
assertEquals(new Rect(), p2.getBounds());
// Construct a path with an empty and filled contour.
Path p3 = new Path();
p3.add(new Contour());
Contour c2 = new Contour();
Rect r = new Rect(20, 30, 40, 50);
c2.addPoint(r.getX(), r.getY());
c2.addPoint(r.getX() + r.getWidth(), r.getY() + r.getHeight());
p3.add(c2);
assertEquals(r, p3.getBounds());
}
}