better curvature

This commit is contained in:
Bane Stojkovic
2026-09-13 21:27:52 +02:00
parent 556dedc9a3
commit 69bb77d307
3 changed files with 135 additions and 16 deletions
@@ -48,33 +48,103 @@ public class AbstractCurvedComponentTest {
}
@Test
public void stretchBetween_bowsTheIntermediatePointsOffTheChord() {
public void stretchBetween_movesTheIntermediatePointsOffTheChord() {
CurvedTestComponent c = new CurvedTestComponent();
c.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 0));
assertTrue("first handle should not sit on the chord", c.getControlPoint(1).getY() > 1);
assertTrue("second handle should not sit on the chord", c.getControlPoint(2).getY() > 1);
assertTrue("first handle should not sit on the chord",
Math.abs(c.getControlPoint(1).getY()) > 1);
assertTrue("second handle should not sit on the chord",
Math.abs(c.getControlPoint(2).getY()) > 1);
}
@Test
public void stretchBetween_bowIsSymmetric() {
public void stretchBetween_putsConsecutivePointsOnOppositeSides() {
CurvedTestComponent c = new CurvedTestComponent();
c.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 0));
assertEquals(c.getControlPoint(1).getY(), c.getControlPoint(2).getY(), DELTA);
assertTrue("the first excursion should point down the screen", c.getControlPoint(1).getY() > 0);
assertTrue("the second excursion should point the other way", c.getControlPoint(2).getY() < 0);
}
@Test
public void stretchBetween_alternatesSidesForEveryPointCount() {
for (PointCount pointCount : PointCount.values()) {
CurvedTestComponent c = new CurvedTestComponent();
c.setPointCount(pointCount);
c.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 0));
double previous = 0;
for (int i = 1; i < c.getControlPointCount() - 1; i++) {
double current = c.getControlPoint(i).getY();
// points that lie on the curve stay on the chord and do not take part in the alternation
if (Math.abs(current) < DELTA) {
continue;
}
if (previous != 0) {
assertTrue(pointCount + " handle " + i + " should sit opposite the previous one",
previous * current < 0);
}
previous = current;
}
}
}
@Test
public void stretchBetween_keepsTheOnCurvePointsOnTheStraightLine() {
for (PointCount pointCount : new PointCount[] {PointCount.FIVE, PointCount.SEVEN}) {
CurvedTestComponent c = new CurvedTestComponent();
c.setPointCount(pointCount);
c.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 0));
assertEquals(pointCount + " should keep its middle point on the chord", 0,
c.getControlPoint(middleIndex(pointCount)).getY(), DELTA);
}
}
/**
* The smoothing in {@code draw} mirrors the handles around a point that lies on the curve, and
* rewrites one of them when they are not already mirrored. Placing them mirrored to begin with
* is what keeps that pass from pulling the freshly created wave out of shape.
*/
@Test
public void stretchBetween_placesHandlesTheWayTheSmoothingRuleWants() {
for (PointCount pointCount : new PointCount[] {PointCount.FIVE, PointCount.SEVEN}) {
CurvedTestComponent c = new CurvedTestComponent();
c.setPointCount(pointCount);
c.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 60));
int middle = middleIndex(pointCount);
Point2D before = c.getControlPoint(middle - 1);
Point2D on = c.getControlPoint(middle);
Point2D after = c.getControlPoint(middle + 1);
assertEquals(pointCount + " handles should mirror horizontally", 2 * on.getX() - before.getX(),
after.getX(), DELTA);
assertEquals(pointCount + " handles should mirror vertically", 2 * on.getY() - before.getY(),
after.getY(), DELTA);
}
}
private static int middleIndex(PointCount pointCount) {
return pointCount == PointCount.FIVE ? 2 : 3;
}
@Test
public void stretchBetween_waveIsSymmetricAboutTheMiddle() {
CurvedTestComponent c = new CurvedTestComponent();
c.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 0));
assertEquals(-c.getControlPoint(1).getY(), c.getControlPoint(2).getY(), DELTA);
assertEquals(100 - c.getControlPoint(2).getX(), c.getControlPoint(1).getX(), DELTA);
}
@Test
public void stretchBetween_sagsTheSameWayRegardlessOfDrawingDirection() {
public void stretchBetween_wavesTheSameWayRegardlessOfDrawingDirection() {
CurvedTestComponent forward = new CurvedTestComponent();
forward.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(100, 0));
CurvedTestComponent backward = new CurvedTestComponent();
backward.stretchBetween(new Point2D.Double(100, 0), new Point2D.Double(0, 0));
assertEquals(forward.getControlPoint(1).getY(), backward.getControlPoint(2).getY(), DELTA);
assertEquals(forward.getControlPoint(2).getY(), backward.getControlPoint(1).getY(), DELTA);
for (int i = 1; i < forward.getControlPointCount() - 1; i++) {
assertEquals(forward.getControlPoint(i).getY(), backward.getControlPoint(i).getY(), DELTA);
}
}
@Test
public void stretchBetween_scalesTheBowWithTheDistance() {
public void stretchBetween_scalesTheWaveWithTheDistance() {
CurvedTestComponent shortWire = new CurvedTestComponent();
shortWire.stretchBetween(new Point2D.Double(0, 0), new Point2D.Double(50, 0));
CurvedTestComponent longWire = new CurvedTestComponent();