diff --git a/src/main/java/org/nusco/narjillos/core/geometry/BoundingBox.java b/src/main/java/org/nusco/narjillos/core/geometry/BoundingBox.java index 2316bec4..cd212e7b 100644 --- a/src/main/java/org/nusco/narjillos/core/geometry/BoundingBox.java +++ b/src/main/java/org/nusco/narjillos/core/geometry/BoundingBox.java @@ -11,7 +11,18 @@ public class BoundingBox { public final double bottom; public final double top; + public BoundingBox(SegmentShape segmentShape) { + Segment segment = segmentShape.toSegment(); + left = Math.min(segment.getStartPoint().x, segment.getEndPoint().x); + right = Math.max(segment.getStartPoint().x, segment.getEndPoint().x); + top = Math.max(segment.getStartPoint().y, segment.getEndPoint().y); + bottom = Math.min(segment.getStartPoint().y, segment.getEndPoint().y); + } + public BoundingBox(Set segmentShapes) { + if (segmentShapes.isEmpty()) + throw new RuntimeException("Empty bounding box"); + double minX = Double.POSITIVE_INFINITY; double minY = Double.POSITIVE_INFINITY; double maxX = Double.NEGATIVE_INFINITY; @@ -33,11 +44,6 @@ public BoundingBox(Set segmentShapes) { private List collectPoints(Set shapes) { List result = new LinkedList<>(); - - if (shapes.isEmpty()) { - result.add(Vector.ZERO); - return result; - } for (SegmentShape segmentShape : shapes) { Segment segment = segmentShape.toSegment(); diff --git a/src/main/java/org/nusco/narjillos/creature/Narjillo.java b/src/main/java/org/nusco/narjillos/creature/Narjillo.java index ced12034..81bac67a 100644 --- a/src/main/java/org/nusco/narjillos/creature/Narjillo.java +++ b/src/main/java/org/nusco/narjillos/creature/Narjillo.java @@ -3,6 +3,7 @@ import java.util.List; import org.nusco.narjillos.core.chemistry.Element; +import org.nusco.narjillos.core.geometry.BoundingBox; import org.nusco.narjillos.core.geometry.Segment; import org.nusco.narjillos.core.geometry.Vector; import org.nusco.narjillos.core.things.Energy; @@ -95,6 +96,10 @@ public Vector getCenterOfMass() { return body.getCenterOfMass(); } + public BoundingBox getBoundingBox() { + return body.getBoundingBox(); + } + public void feedOn(FoodPellet thing) { getEnergy().steal(thing.getEnergy()); thing.setEater(this); diff --git a/src/main/java/org/nusco/narjillos/creature/body/Body.java b/src/main/java/org/nusco/narjillos/creature/body/Body.java index c01b919c..53bacb90 100644 --- a/src/main/java/org/nusco/narjillos/creature/body/Body.java +++ b/src/main/java/org/nusco/narjillos/creature/body/Body.java @@ -3,12 +3,16 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.nusco.narjillos.core.chemistry.Element; import org.nusco.narjillos.core.geometry.Angle; +import org.nusco.narjillos.core.geometry.BoundingBox; import org.nusco.narjillos.core.geometry.Segment; +import org.nusco.narjillos.core.geometry.SegmentShape; import org.nusco.narjillos.core.geometry.Vector; import org.nusco.narjillos.core.geometry.ZeroVectorAngleException; import org.nusco.narjillos.core.utilities.Configuration; @@ -34,6 +38,7 @@ public class Body { private transient List organs; private transient Vector cachedCenterOfMass = null; + private transient BoundingBox cachedBoundingBox = null; private transient double cachedRadius = Double.NaN; public Body(MovingOrgan head) { @@ -106,16 +111,6 @@ public int getEggInterval() { return getHead().getEggInterval(); } - // Creatures with a prevalence of red, green and blue mass breathe oxygen, - // hydrogen and nitrogen, respectively. - private Element getBreathedElementFromFibers() { - if (redMass > greenMass && redMass > blueMass) - return Element.OXYGEN; - if (greenMass > blueMass) - return Element.HYDROGEN; - return Element.NITROGEN; - } - public Element getByproduct() { return getHead().getByproduct(); } @@ -140,6 +135,12 @@ public synchronized Vector getCenterOfMass() { return cachedCenterOfMass; } + public BoundingBox getBoundingBox() { + if (cachedBoundingBox == null) + cachedBoundingBox = calculateBoundingBox(); + return cachedBoundingBox; + } + public void forcePosition(Vector position, double angle) { getHead().forcePosition(position, angle); resetCaches(); @@ -234,6 +235,7 @@ public void growToAdultForm() { private synchronized void resetCaches() { cachedCenterOfMass = null; + cachedBoundingBox = null; cachedRadius = Double.NaN; } @@ -262,6 +264,23 @@ private Vector calculateCenterOfMass() { return Vector.cartesian(totalX / mass, totalY / mass); } + private BoundingBox calculateBoundingBox() { + Set organShapes = new LinkedHashSet<>(); + for (ConnectedOrgan connectedOrgan : getOrgans()) + organShapes.add(connectedOrgan); + return new BoundingBox(organShapes); + } + + // Creatures with a prevalence of red, green and blue mass breathe oxygen, + // hydrogen and nitrogen, respectively. + private Element getBreathedElementFromFibers() { + if (redMass > greenMass && redMass > blueMass) + return Element.OXYGEN; + if (greenMass > blueMass) + return Element.HYDROGEN; + return Element.NITROGEN; + } + private void tick_step1_updateAngles(Vector targetDirection) { double angleToTarget = getAngleTo(targetDirection); getHead().tick(angleToTarget); diff --git a/src/test/java/org/nusco/narjillos/core/geometry/BoundingBoxTest.java b/src/test/java/org/nusco/narjillos/core/geometry/BoundingBoxTest.java index 9f3eb97a..29645621 100644 --- a/src/test/java/org/nusco/narjillos/core/geometry/BoundingBoxTest.java +++ b/src/test/java/org/nusco/narjillos/core/geometry/BoundingBoxTest.java @@ -16,9 +16,22 @@ public void definesTheBoundariesOfASegment() { BoundingBox boundingBox = createBoundingBox(10, 20, 30, 40); assertEquals(10, boundingBox.left, 0.0); - assertEquals(30, boundingBox.right, 0.0); + assertEquals(40, boundingBox.right, 0.0); assertEquals(20, boundingBox.bottom, 0.0); - assertEquals(40, boundingBox.top, 0.0); + assertEquals(60, boundingBox.top, 0.0); + } + + @Test + public void definesTheBoundariesOfMultipleSegments() { + Set shapes = new LinkedHashSet<>(); + shapes.add(createSegmentShape(10, 20, 30, 40)); + shapes.add(createSegmentShape(15, 30, -100, 60)); + BoundingBox boundingBox = new BoundingBox(shapes); + + assertEquals(-85, boundingBox.left, 0.0); + assertEquals(40, boundingBox.right, 0.0); + assertEquals(20, boundingBox.bottom, 0.0); + assertEquals(90, boundingBox.top, 0.0); } @Test @@ -26,29 +39,24 @@ public void worksWithNegativeBoundaries() { BoundingBox boundingBox = createBoundingBox(-10, -20, 30, 40); assertEquals(-10, boundingBox.left, 0.0); - assertEquals(30, boundingBox.right, 0.0); + assertEquals(20, boundingBox.right, 0.0); assertEquals(-20, boundingBox.bottom, 0.0); - assertEquals(40, boundingBox.top, 0.0); + assertEquals(20, boundingBox.top, 0.0); } @Test public void worksWithSegmentsInAnyOrientation() { - BoundingBox boundingBox = createBoundingBox(30, 40, -10, -20); + BoundingBox boundingBox = createBoundingBox(35, 40, -10, -20); - assertEquals(-10, boundingBox.left, 0.0); - assertEquals(30, boundingBox.right, 0.0); - assertEquals(-20, boundingBox.bottom, 0.0); + assertEquals(25, boundingBox.left, 0.0); + assertEquals(35, boundingBox.right, 0.0); + assertEquals(20, boundingBox.bottom, 0.0); assertEquals(40, boundingBox.top, 0.0); } - @Test - public void isEmptyIfThereAreNoSegments() { - BoundingBox boundingBox = new BoundingBox(new LinkedHashSet()); - - assertEquals(0, boundingBox.left, 0.0); - assertEquals(0, boundingBox.right, 0.0); - assertEquals(0, boundingBox.bottom, 0.0); - assertEquals(0, boundingBox.top, 0.0); + @Test(expected = RuntimeException.class) + public void cannotBeEmpty() { + new BoundingBox(new LinkedHashSet()); } @Test @@ -91,13 +99,6 @@ public void doesNotOverlapIfItIsAboveTheOtherBoundingBox() { assertFalse(boundingBox1.overlaps(boundingBox2)); } - @Test - public void overlapsItself() { - BoundingBox boundingBox = createBoundingBox(0, 0, 10, 10); - - assertFalse(boundingBox.overlaps(boundingBox)); - } - @Test public void doesNotOverlapIfItTouchesOnTheEdge() { BoundingBox boundingBox1 = createBoundingBox(0, 0, 10, 10); @@ -106,16 +107,23 @@ public void doesNotOverlapIfItTouchesOnTheEdge() { assertFalse(boundingBox1.overlaps(boundingBox2)); } + @Test + public void overlapsItself() { + BoundingBox boundingBox = createBoundingBox(0, 0, 10, 10); + + assertTrue(boundingBox.overlaps(boundingBox)); + } + @Test public void neverOverlapsIfBothBoxesHaveAreaZero() { BoundingBox boundingBox = createBoundingBox(10, 10, 0, 0); - + assertFalse(boundingBox.overlaps(boundingBox)); } @Test public void canStillOverlapIfTheFirstBoxHasAreaZero() { - BoundingBox boundingBox1 = createBoundingBox(10, 10, 0, 0); + BoundingBox boundingBox1 = createBoundingBox(5, 5, 0, 0); BoundingBox boundingBox2 = createBoundingBox(-10, -10, 20, 20); assertTrue(boundingBox1.overlaps(boundingBox2)); @@ -130,13 +138,15 @@ public void canStillOverlapIfTheSecondBoxHasAreaZero() { } private BoundingBox createBoundingBox(int x, int y, int width, int height) { - Set shapes = new LinkedHashSet<>(); - shapes.add(new SegmentShape() { + return new BoundingBox(createSegmentShape(x, y, width, height)); + } + + private SegmentShape createSegmentShape(int x, int y, int width, int height) { + return new SegmentShape() { @Override public Segment toSegment() { return new Segment(Vector.cartesian(x, y), Vector.cartesian(width, height)); } - }); - return new BoundingBox(shapes); + }; } } diff --git a/src/test/java/org/nusco/narjillos/creature/body/BodyTest.java b/src/test/java/org/nusco/narjillos/creature/body/BodyTest.java index e5991c66..6847f5d3 100644 --- a/src/test/java/org/nusco/narjillos/creature/body/BodyTest.java +++ b/src/test/java/org/nusco/narjillos/creature/body/BodyTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.nusco.narjillos.core.chemistry.Element; +import org.nusco.narjillos.core.geometry.BoundingBox; import org.nusco.narjillos.core.geometry.Vector; public class BodyTest { @@ -37,7 +38,7 @@ public void hasAMassProportionalToItsSize() { double expectedMassInGrams = 212; assertEquals(expectedMassInGrams, body.getAdultMass(), 0.001); } - + @Test public void hasACenterOfMassAndARadius() { Head head = new Head(new HeadParameters(10, 10)); @@ -51,6 +52,22 @@ public void hasACenterOfMassAndARadius() { assertEquals(Vector.cartesian(12.5, 0), body.getCenterOfMass()); assertEquals(17.5, body.getRadius(), 0.0); } + + @Test + public void hasABoundingBox() { + Head head = new Head(new HeadParameters(10, 1)); + head.addChild(new BodyPart(20, 1, 0, 0, 0, head, 0, 90, 0, 0)); + Body body = new Body(head); + + body.growToAdultForm(); + body.forcePosition(Vector.cartesian(3, 4), 0); + + BoundingBox boundingBox = body.getBoundingBox(); + assertEquals(3, boundingBox.left, 0.0); + assertEquals(13, boundingBox.right, 0.0); + assertEquals(4, boundingBox.bottom, 0.0); + assertEquals(24, boundingBox.top, 0.0); + } @Test public void itsMinimumRadiusIsOne() {