package squidpony.squidmath;
import squidpony.squidgrid.Direction;
import java.util.ArrayList;
import java.util.List;
/**
* A drunkard's-walk-like algorithm for line drawing "wobbly" paths.
* The line() methods here use an RNG (and will make their own if they don't take one as a parameter) to make a choice
* between orthogonal directions to travel in. Because they can go around the target instead of straight to it, they
* also need a width and height for the map so they don't wander over the edge. You can also pass a weight to one of the
* line() methods, which affects how straight the wobbly path will be (1.0 being just about perfectly straight, 0.5
* being very chaotic, and less than 0.5 being almost unrecognizable as a path).
* <br>
* Based on Michael Patraw's C code, used for cave carving in his map generator. http://mpatraw.github.io/libdrunkard/
* Created by Tommy Ettinger on 1/10/2016.
*/
public class WobblyLine {
private WobblyLine(){}
/**
* Draws a line from (startX, startY) to (endX, endY) using the Drunkard's Walk algorithm. Returns a List of Coord
* in order.
* <br>
* Equivalent to calling {@code line(startX, startY, endX, endY, width, height, 0.75, new RNG())} .
* @param startX x of starting point
* @param startY y of starting point
* @param endX x of ending point
* @param endY y of ending point
* @param width maximum map width
* @param height maximum map height
* @return List of Coord, including (startX, startY) and (endX, endY) and all points walked between
*/
public static List<Coord> line(int startX, int startY, int endX, int endY, int width, int height) {
return line(startX, startY, endX, endY, width, height, 0.75, new RNG());
}
/**
* Draws a line from (startX, startY) to (endX, endY) using the Drunkard's Walk algorithm. Returns a List of Coord
* in order.
* @param startX x of starting point
* @param startY y of starting point
* @param endX x of ending point
* @param endY y of ending point
* @param width maximum map width
* @param height maximum map height
* @param weight between 0.5 and 1.0, usually. 0.6 makes very random walks, 0.9 is almost a straight line.
* @param rng the random number generator to use
* @return List of Coord, including (startX, startY) and (endX, endY) and all points walked between
*/
public static List<Coord> line(int startX, int startY, int endX, int endY,
int width, int height, double weight, RNG rng) {
List<Coord> pts = new ArrayList<>();
Coord start = Coord.get(startX, startY);
Direction dir;
do {
pts.add(start);
dir = stepWobbly(start.x, start.y, endX, endY, weight, width, height, rng);
start = start.translate(dir);
if(start.x < 1 || start.y < 1 || start.x >= width - 1 || start.y >= height - 1)
break;
}while (dir != Direction.NONE);
return pts;
}
/**
* Internal use. Drunkard's walk algorithm, single step. Based on Michael Patraw's C code, used for cave carving.
* http://mpatraw.github.io/libdrunkard/
* @param currentX the x coordinate of the current point
* @param currentY the y coordinate of the current point
* @param targetX the x coordinate of the point to wobble towards
* @param targetY the y coordinate of the point to wobble towards
* @param weight between 0.5 and 1.0, usually. 0.6 makes very random walks, 0.9 is almost a straight line.
* @param width maximum map width
* @param height maximum map height
* @param rng the random number generator to use
* @return a Direction, either UP, DOWN, LEFT, or RIGHT if we should move, or NONE if we have reached our target
*/
private static Direction stepWobbly(int currentX, int currentY, int targetX, int targetY, double weight,
int width, int height, RNG rng)
{
int dx = targetX - currentX;
int dy = targetY - currentY;
if (dx > 1) dx = 1;
if (dx < -1) dx = -1;
if (dy > 1) dy = 1;
if (dy < -1) dy = -1;
double r = rng.nextDouble();
Direction dir;
if (dx == 0 && dy == 0)
{
return Direction.NONE;
}
else if (dx == 0 || dy == 0)
{
int dx2 = (dx == 0) ? dx : dy, dy2 = (dx == 0) ? dy : dx;
if (r >= (weight * 0.5))
{
r -= weight * 0.5;
if (r < weight * (1.0 / 6) + (1 - weight) * (1.0 / 3))
{
dx2 = -1;
dy2 = 0;
}
else if (r < weight * (2.0 / 6) + (1 - weight) * (2.0 / 3))
{
dx2 = 1;
dy2 = 0;
}
else
{
dx2 = 0;
dy2 *= -1;
}
}
dir = Direction.getCardinalDirection(dx2, -dy2);
}
else
{
if (r < weight * 0.5)
{
dy = 0;
}
else if (r < weight)
{
dx = 0;
}
else if (r < weight + (1 - weight) * 0.5)
{
dx *= -1;
dy = 0;
}
else
{
dx = 0;
dy *= -1;
}
dir = Direction.getCardinalDirection(dx, -dy);
}
if(currentX + dir.deltaX <= 0 || currentX + dir.deltaX >= width - 1) {
if (currentY < targetY) dir = Direction.DOWN;
else if (currentY > targetY) dir = Direction.UP;
}
else if(currentY + dir.deltaY <= 0 || currentY + dir.deltaY >= height - 1) {
if (currentX < targetX) dir = Direction.RIGHT;
else if (currentX > targetX) dir = Direction.LEFT;
}
return dir;
}
/**
* Draws a line from start to end using the Drunkard's Walk algorithm. Returns a List of Coord in order.
* @param start starting point
* @param end ending point
* @param width maximum map width
* @param height maximum map height
* @return List of Coord, including start and end and all points walked between
*/
public static List<Coord> line(Coord start, Coord end, int width, int height)
{
return line(start.x, start.y, end.x, end.y, width, height);
}
}