Find the shortest route through a maze using linking and linked lists, the strategy is based on the
concept of breadth first search. (Code for maze provided) (Previously asked before with no
response)
--------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
--------------------------------
We are going to look at the problem of finding the shortest path through a maze. We will make
use of linking and linked lists to discover the shortest path from the entrance to the center of the
maze. The strategy we will use is based on the concept of breadth first search.
You will be provided with a GUI program that creates and draws a maze. It also provides a
method which you will use to draw the shortest path when you have discovered it. The following
illustrates the program running when it has been successfully completed.
The Maze
The maze is represented in the program by the Maze class (where you will add code to find and
draw the shortest path). It uses a 2 dimensional array of MazeCell objects to capture all the
important data about the maze itself. The MazeCell class is as follows:
class MazeCell {
// nth, sth, est, wst used to identify walls
// true indicates the wall is present
boolean nth, sth, wst, est;
// visited is used to indicate that a cell has been processed
boolean visited;
// row, col is used to facilitate finding the neighbors of a cell
int row, col;
// Constructor takes a row and column parameter for the cell
public MazeCell(int i, int j) {
row = i;
col = j;
// All walls are initially present for every cell
// The build process will remove some of the walls
nth = sth = est = wst = true;
// Initially no cells have been visited
visited = false;
}
}
Once the maze has been built, each cell contains its own row and column number. The walls of
the cell have been set to true or false. Any wall set to false means that there is a neighboring cell
in that direction which can be accessed. For example, if nth is set to false, then there is no wall
between this cell and its neighboring cell to the north. The orientation of the maze relative to the
GUI representation and relative to the directions is as follows:
0, n-1 North
n-1, n-1 West
East 0, 0
South n-1, 0
In the GUI representation, rows of the maze are vertical, columns are horizontal. Cell 0,0 of the
maze in drawn in the lower left corner. The following summarizes what to do to move in a
specific direction from any cell in the maze. Remember, each cell contains its own row, col
position.
North neighbor is at row, col + 1
South neighbor is at row, col – 1
East neighbor is at row + 1, col
West neighbor is at row – 1, col
The 2 dimensional array is set up with 2 additional rows and 2 additional columns to serve as a
border for the actual maze itself. The entry point to the maze is at cell 1, 1. The center of the
maze is.
Find the shortest route through a maze using linking and linked list.pdf
1. Find the shortest route through a maze using linking and linked lists, the strategy is based on the
concept of breadth first search. (Code for maze provided) (Previously asked before with no
response)
--------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
--------------------------------
We are going to look at the problem of finding the shortest path through a maze. We will make
use of linking and linked lists to discover the shortest path from the entrance to the center of the
maze. The strategy we will use is based on the concept of breadth first search.
You will be provided with a GUI program that creates and draws a maze. It also provides a
method which you will use to draw the shortest path when you have discovered it. The following
illustrates the program running when it has been successfully completed.
The Maze
The maze is represented in the program by the Maze class (where you will add code to find and
draw the shortest path). It uses a 2 dimensional array of MazeCell objects to capture all the
important data about the maze itself. The MazeCell class is as follows:
class MazeCell {
// nth, sth, est, wst used to identify walls
// true indicates the wall is present
boolean nth, sth, wst, est;
// visited is used to indicate that a cell has been processed
boolean visited;
// row, col is used to facilitate finding the neighbors of a cell
int row, col;
// Constructor takes a row and column parameter for the cell
public MazeCell(int i, int j) {
row = i;
col = j;
// All walls are initially present for every cell
// The build process will remove some of the walls
nth = sth = est = wst = true;
// Initially no cells have been visited
visited = false;
}
}
2. Once the maze has been built, each cell contains its own row and column number. The walls of
the cell have been set to true or false. Any wall set to false means that there is a neighboring cell
in that direction which can be accessed. For example, if nth is set to false, then there is no wall
between this cell and its neighboring cell to the north. The orientation of the maze relative to the
GUI representation and relative to the directions is as follows:
0, n-1 North
n-1, n-1 West
East 0, 0
South n-1, 0
In the GUI representation, rows of the maze are vertical, columns are horizontal. Cell 0,0 of the
maze in drawn in the lower left corner. The following summarizes what to do to move in a
specific direction from any cell in the maze. Remember, each cell contains its own row, col
position.
North neighbor is at row, col + 1
South neighbor is at row, col – 1
East neighbor is at row + 1, col
West neighbor is at row – 1, col
The 2 dimensional array is set up with 2 additional rows and 2 additional columns to serve as a
border for the actual maze itself. The entry point to the maze is at cell 1, 1. The center of the
maze is at cell N/2, N/2.The task is to find the shortest path from the entry point to the center,
then draw that path on the GUI as pictured above.
The Search Strategy
The basic approach is to start at the center and find all the accessible cells adjacent to the center
(neighbors). Each neighbor is placed on a list. When completed, this list contains all the cells that
are one step from the center. Once all the neighbors to the center are on this list, every cell in that
list is processed the same way. A new list is created which contains all the neighbors of all the
cells on the first list. When the first list is completely processed, the second list contains all the
cells that are 2 steps away from the center. This process is repeated until the entrance is located.
As each cell is added to the second list, it is important to remember the cell that is one step closer
to the center. For instance if cell 20, 20 is the center, and it has a north neighbor at cell 20, 21
then when we add cell 20,21 to the list of neighbors, we need to remember that we reached this
neighbor coming from cell 20, 20. This information will be used to track the shortest path back
to the center when we have reached the entrance.
To facilitate the mapping the maze, we need a data structure to store a reference to a neighbor
cell and a reference to the piece of maze map information that contains a reference to the cell that
3. is one step closer to the center. The following data structure accomplishes this.
class MazeMap {
// Reference to neighboring cell
MazeCell nFromCenter = null;
// Reference to map info which contains a reference
// to the cell that is one step closer to the center
MazeMap nMinus1FromCtr = null;
The Maze class contains a method called findShortestPath that you must implement. The
following is a description of the steps needed to successfully find and display the shortest path
thru the maze.
Create two LinkedLists of MazeMap objects. One will be the list of map objects for cells at
distance x from the center. The other will be the list of map objects for cells at distance x + 1.
Initialize the list at distance x to contain a MazeMap object which refers to the center cell of the
maze. Since this map object refers to the center cell, it does not need to refer to any other
MazeMap object, so its MazeMap reference can be set to null. Set the center cell’s visited field
to true to indicate that this cell has already been added to the map.
Set up a loop to run until the maze entrance is mapped. This loop does the following:
Set up a loop to process each map object from the list at distance x. This loop does the following:
i.Get the cell from the map object
ii.If this cell is the entrance cell, set the entrance and break out of the loop!
iii.Check each direction from this cell and for an accessible neighbor cell
Get a reference to that neighbor cell
If the neighbor cell has not already been visited
Set the cell’s visited field to true so its only processed once
Create a MazeMap object that refers to this neighbor cell
Make this MazeMap object refer to the current map object being processed – ie the cell one step
closer to center
Add this MazeMap object to the list at distance x + 1
List at distance x has been processed. The list at distance x + 1 is now complete, so make the list
at distance x refer to list at distance x + 1. Then create a new empty list for the list at distance x +
1.
At this point, the entrance points at the map object which refers to cell 1,1. That map object also
refers to a map object which is one step closer to the center. Write a loop that traverses this trail
of references until it reaches the end (a null reference). For each map object:
Get the cell referred to.
Call the drawCircle method with a color, the cell’s row and column, and SMALL.
4. Call the delay method with a SHORT delay. This slows down the drawing process so you can
watch the path being displayed.
Advance to the next map object in the list.
Testing
Once you have gotten your solution to compile, run the program for a list of size 20 and observe
whether your program displays a valid path from the entrance to the center. If you have a
problem, it is very likely that you will need to make use of breakpoints to step through how your
algorithm is working, or to find where it is getting hung up.
PROVIDED CODE
public class Maze {
private int N; // dimension of maze
private MazeCell[][] maze;
private Random rand = new Random();
// Used to signal the maze has been solved
private boolean done;
// time in milliseconds (from currentTimeMillis()) when we can draw again
// used to control the frame rate
private long nextDraw = -1;
private MazeApp mf;
// Define constants for the circle size
private final double BIG = 0.375;
private final double SMALL = 0.25;
// Define constants for the delay times
private final int SHORT = 30;
private final int LONG = 500;
public Maze(MazeApp ma, int n) {
N = n;
mf = ma;
mf.setXscale(0, N + 2);
mf.setYscale(0, N + 2);
}
public void buildAndDrawMaze() {
createMaze();
buildMaze();
drawMaze();
}
5. // create the initial data structures that contain the maze data
private void createMaze() {
maze = new MazeCell[N + 2][N + 2];
for (int i = 0; i < N + 2; i++) {
for (int j = 0; j < N + 2; j++) {
maze[i][j] = new MazeCell(i, j);
}
}
// initialize border cells as already visited
for (int x = 0; x < N + 2; x++) {
maze[x][0].visited = true;
maze[x][N + 1].visited = true;
}
for (int y = 0; y < N + 2; y++) {
maze[0][y].visited = true;
maze[N + 1][y].visited = true;
}
}
// build the maze
private void buildMaze(int x, int y) {
maze[x][y].visited = true;
// while there is an unvisited neighbor
while (!maze[x][y + 1].visited || !maze[x + 1][y].visited
|| !maze[x][y - 1].visited || !maze[x - 1][y].visited) {
// pick random neighbor (could use Knuth's trick instead)
while (true) {
int r = rand.nextInt(4);
if (r == 0 && !maze[x][y + 1].visited) {
maze[x][y].nth = false;
maze[x][y + 1].sth = false;
buildMaze(x, y + 1);
break;
} else if (r == 1 && !maze[x + 1][y].visited) {
maze[x][y].est = false;
maze[x + 1][y].wst = false;
buildMaze(x + 1, y);
6. break;
} else if (r == 2 && !maze[x][y - 1].visited) {
maze[x][y].sth = false;
maze[x][y - 1].nth = false;
buildMaze(x, y - 1);
break;
} else if (r == 3 && !maze[x - 1][y].visited) {
maze[x][y].wst = false;
maze[x - 1][y].est = false;
buildMaze(x - 1, y);
break;
}
}
}
}
// build the maze starting from lower left
private void buildMaze() {
buildMaze(1, 1);
// Make sure visited is reset to false
for (int x = 1; x < N + 1; x++) {
for (int y = 1; y < N + 1; y++) {
maze[x][y].visited = false;
}
}
// delete some random walls
for (int i = 0; i < N; i++) {
int x = 1 + rand.nextInt(N - 1);
int y = 1 + rand.nextInt(N - 1);
maze[x][y].nth = maze[x][y + 1].sth = false;
}
}
// draw the initial maze
private void drawMaze() {
drawCircle(Color.RED, N / 2, N / 2, BIG);
drawCircle(Color.RED, 1, 1, BIG);
// Draw the walls in black
7. mf.setPenColor(Color.BLACK);
for (int x = 1; x <= N; x++) {
for (int y = 1; y <= N; y++) {
if (maze[x][y].sth) {
mf.line(x, y, x + 1, y);
}
if (maze[x][y].nth) {
mf.line(x, y + 1, x + 1, y + 1);
}
if (maze[x][y].wst) {
mf.line(x, y, x, y + 1);
}
if (maze[x][y].est) {
mf.line(x + 1, y, x + 1, y + 1);
}
}
}
delay(LONG);
}
private void delay(int t) {
// sleep until the next time we're allowed to draw
long millis = System.currentTimeMillis();
if (millis < nextDraw) {
try {
Thread.sleep(nextDraw - millis);
} catch (InterruptedException e) {
System.out.println("Error sleeping");
}
millis = nextDraw;
}
// when are we allowed to draw again
nextDraw = millis + t;
}
private void drawCircle(Color c, double x, double y, double size) {
mf.setFillColor(c);
mf.filledCircle(x + 0.5, y + 0.5, size);
8. }
public void findShortestPath() {
// Your code goes here!!!!!
}
}
class MazeCell {
// nth, sth, est, wst used to identify walls - true indicates wall present
boolean nth, sth, wst, est;
// used to indicate that a cell has already been processed
boolean visited;
// used to facilitate finding the neighbors of a cell
int row, col;
public MazeCell(int i, int j) {
row = i;
col = j;
// All walls are initially present for every cell
nth = sth = est = wst = true;
// Initially no cells have been visited
visited = false;
}
}
class MazeMap {
MazeCell nFromCenter = null;
MazeMap nMinus1FromCenter = null;
}
--------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------
public class MazeApp extends Application
{
// default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE
private static final int DEFAULT_SIZE = 768;
private int width = DEFAULT_SIZE;
private int height = DEFAULT_SIZE;
// The graphics context is needed to enable drawing on the canvas
private GraphicsContext gc;
9. // boundary of drawing canvas, 0% border
// private static final double BORDER = 0.05;
private static final double BORDER = 0.00;
private double xmin, ymin, xmax, ymax;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
Group root = new Group();
Canvas canvas = new Canvas(width, height);
gc = canvas.getGraphicsContext2D();
gc.setLineWidth(2);
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, width, height);
root.getChildren().add(canvas);
TextInputDialog tid = new TextInputDialog();
tid.setTitle("Maze Size");
tid.setHeaderText("Enter maze size between 10 and 50");
tid.showAndWait();
int size = Integer.parseInt(tid.getResult());
if (size > 50)
size = 50;
if (size < 10)
size = 10;
primaryStage.setTitle("Maze Application");
primaryStage.setScene(new Scene(root));
primaryStage.setResizable(false);
// Make sure that the application goes away when then window is closed
primaryStage.setOnCloseRequest(e -> System.exit(0));
primaryStage.show();
10. Maze maze = new Maze(this, size);
// Must solve the maze in a separate thread or else
// the GUI wont update until the end.....
Thread solver = new Thread(
new Runnable () {
public void run()
{
while(true)
{
maze.buildAndDrawMaze();
maze.findShortestPath();
try
{
Thread.sleep(5000);
}
catch(Exception e) { }
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, width, height);
}
}
});
solver.start();
}
/**
* Sets the pen color to the specified color.
*
* @param color the color to make the pen
*/
public void setPenColor(Color color) {
gc.setStroke(color);
}
/**
* Sets the pen color to the specified color.
*
* @param color the color to make the pen
11. */
public void setFillColor(Color color) {
gc.setFill(color);
}
/**
* Sets the x-scale to the specified range.
*
* @param min the minimum value of the x-scale
* @param max the maximum value of the x-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public void setXscale(double min, double max) {
double size = max - min;
if (size == 0.0) {
throw new IllegalArgumentException("the min and max are the same");
}
xmin = min - BORDER * size;
xmax = max + BORDER * size;
}
/**
* Sets the y-scale to the specified range.
*
* @param min the minimum value of the y-scale
* @param max the maximum value of the y-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public void setYscale(double min, double max) {
double size = max - min;
if (size == 0.0) {
throw new IllegalArgumentException("the min and max are the same");
}
ymin = min - BORDER * size;
ymax = max + BORDER * size;
}
// helper functions that scale from user coordinates to screen coordinates and back
private double scaleX(double x) {
12. return width * (x - xmin) / (xmax - xmin);
}
private double scaleY(double y) {
return height * (ymax - y) / (ymax - ymin);
}
private double factorX(double w) {
return w * width / Math.abs(xmax - xmin);
}
private double factorY(double h) {
return h * height / Math.abs(ymax - ymin);
}
private double userX(double x) {
return xmin + x * (xmax - xmin) / width;
}
private double userY(double y) {
return ymax - y * (ymax - ymin) / height;
}
/**
* Draws a line segment between (x0,
* y0) and (x1,
* y1).
*
* @param x0 the x-coordinate of one endpoint
* @param y0 the y-coordinate of one endpoint
* @param x1 the x-coordinate of the other endpoint
* @param y1 the y-coordinate of the other endpoint
*/
public void line(double x0, double y0, double x1, double y1) {
gc.strokeLine(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
}
/**
* Draws one pixel at (x, y). This method is private
* because pixels depend on the display. To achieve the same effect, set the
* pen radius to 0 and call {@code point()}.
*
* @param x the x-coordinate of the pixel
13. * @param y the y-coordinate of the pixel
*/
private void pixel(double x, double y) {
gc.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1);
}
/**
* Draws a filled circle of the specified radius, centered at (x,
* y).
*
* @param x the x-coordinate of the center of the circle
* @param y the y-coordinate of the center of the circle
* @param radius the radius of the circle
* @throws IllegalArgumentException if {@code radius} is negative
*/
public void filledCircle(double x, double y, double radius) {
double xs = scaleX(x);
double ys = scaleY(y);
double ws = factorX(2 * radius);
double hs = factorY(2 * radius);
if (ws <= 1 && hs <= 1) {
pixel(x, y);
} else {
gc.fillOval(xs - ws / 2, ys - hs / 2, ws, hs);
}
}
} Maze Application
Solution
public class Maze {
private int N; // dimension of maze
private MazeCell[][] maze;
private Random rand = new Random();
// Used to signal the maze has been solved
private boolean done;
// time in milliseconds (from currentTimeMillis()) when we can draw again
14. // used to control the frame rate
private long nextDraw = -1;
private MazeApp mf;
// Define constants for the circle size
private final double BIG = 0.375;
private final double SMALL = 0.25;
// Define constants for the delay times
private final int SHORT = 30;
private final int LONG = 500;
public Maze(MazeApp ma, int n) {
N = n;
mf = ma;
mf.setXscale(0, N + 2);
mf.setYscale(0, N + 2);
}
public void buildAndDrawMaze() {
createMaze();
buildMaze();
drawMaze();
}
// create the initial data structures that contain the maze data
private void createMaze() {
maze = new MazeCell[N + 2][N + 2];
for (int i = 0; i < N + 2; i++) {
for (int j = 0; j < N + 2; j++) {
maze[i][j] = new MazeCell(i, j);
}
}
// initialize border cells as already visited
for (int x = 0; x < N + 2; x++) {
maze[x][0].visited = true;
maze[x][N + 1].visited = true;
}
for (int y = 0; y < N + 2; y++) {
maze[0][y].visited = true;
maze[N + 1][y].visited = true;
15. }
}
// build the maze
private void buildMaze(int x, int y) {
maze[x][y].visited = true;
// while there is an unvisited neighbor
while (!maze[x][y + 1].visited || !maze[x + 1][y].visited
|| !maze[x][y - 1].visited || !maze[x - 1][y].visited) {
// pick random neighbor (could use Knuth's trick instead)
while (true) {
int r = rand.nextInt(4);
if (r == 0 && !maze[x][y + 1].visited) {
maze[x][y].nth = false;
maze[x][y + 1].sth = false;
buildMaze(x, y + 1);
break;
} else if (r == 1 && !maze[x + 1][y].visited) {
maze[x][y].est = false;
maze[x + 1][y].wst = false;
buildMaze(x + 1, y);
break;
} else if (r == 2 && !maze[x][y - 1].visited) {
maze[x][y].sth = false;
maze[x][y - 1].nth = false;
buildMaze(x, y - 1);
break;
} else if (r == 3 && !maze[x - 1][y].visited) {
maze[x][y].wst = false;
maze[x - 1][y].est = false;
buildMaze(x - 1, y);
break;
}
}
}
}
// build the maze starting from lower left
16. private void buildMaze() {
buildMaze(1, 1);
// Make sure visited is reset to false
for (int x = 1; x < N + 1; x++) {
for (int y = 1; y < N + 1; y++) {
maze[x][y].visited = false;
}
}
// delete some random walls
for (int i = 0; i < N; i++) {
int x = 1 + rand.nextInt(N - 1);
int y = 1 + rand.nextInt(N - 1);
maze[x][y].nth = maze[x][y + 1].sth = false;
}
}
// draw the initial maze
private void drawMaze() {
drawCircle(Color.RED, N / 2, N / 2, BIG);
drawCircle(Color.RED, 1, 1, BIG);
// Draw the walls in black
mf.setPenColor(Color.BLACK);
for (int x = 1; x <= N; x++) {
for (int y = 1; y <= N; y++) {
if (maze[x][y].sth) {
mf.line(x, y, x + 1, y);
}
if (maze[x][y].nth) {
mf.line(x, y + 1, x + 1, y + 1);
}
if (maze[x][y].wst) {
mf.line(x, y, x, y + 1);
}
if (maze[x][y].est) {
mf.line(x + 1, y, x + 1, y + 1);
}
}
17. }
delay(LONG);
}
private void delay(int t) {
// sleep until the next time we're allowed to draw
long millis = System.currentTimeMillis();
if (millis < nextDraw) {
try {
Thread.sleep(nextDraw - millis);
} catch (InterruptedException e) {
System.out.println("Error sleeping");
}
millis = nextDraw;
}
// when are we allowed to draw again
nextDraw = millis + t;
}
private void drawCircle(Color c, double x, double y, double size) {
mf.setFillColor(c);
mf.filledCircle(x + 0.5, y + 0.5, size);
}
public void findShortestPath() {
// Your code goes here!!!!!
}
}
class MazeCell {
// nth, sth, est, wst used to identify walls - true indicates wall present
boolean nth, sth, wst, est;
// used to indicate that a cell has already been processed
boolean visited;
// used to facilitate finding the neighbors of a cell
int row, col;
public MazeCell(int i, int j) {
row = i;
col = j;
18. // All walls are initially present for every cell
nth = sth = est = wst = true;
// Initially no cells have been visited
visited = false;
}
}
class MazeMap {
MazeCell nFromCenter = null;
MazeMap nMinus1FromCenter = null;
}
---------------------------------------------------------------------------------------
public class MazeApp extends Application
{
// default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE
private static final int DEFAULT_SIZE = 768;
private int width = DEFAULT_SIZE;
private int height = DEFAULT_SIZE;
// The graphics context is needed to enable drawing on the canvas
private GraphicsContext gc;
// boundary of drawing canvas, 0% border
// private static final double BORDER = 0.05;
private static final double BORDER = 0.00;
private double xmin, ymin, xmax, ymax;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
Group root = new Group();
Canvas canvas = new Canvas(width, height);
gc = canvas.getGraphicsContext2D();
gc.setLineWidth(2);
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, width, height);
19. root.getChildren().add(canvas);
TextInputDialog tid = new TextInputDialog();
tid.setTitle("Maze Size");
tid.setHeaderText("Enter maze size between 10 and 50");
tid.showAndWait();
int size = Integer.parseInt(tid.getResult());
if (size > 50)
size = 50;
if (size < 10)
size = 10;
primaryStage.setTitle("Maze Application");
primaryStage.setScene(new Scene(root));
primaryStage.setResizable(false);
// Make sure that the application goes away when then window is closed
primaryStage.setOnCloseRequest(e -> System.exit(0));
primaryStage.show();
Maze maze = new Maze(this, size);
// Must solve the maze in a separate thread or else
// the GUI wont update until the end.....
Thread solver = new Thread(
new Runnable () {
public void run()
{
while(true)
{
maze.buildAndDrawMaze();
maze.findShortestPath();
try
{
Thread.sleep(5000);
}
catch(Exception e) { }
gc.setFill(Color.WHITE);
20. gc.fillRect(0, 0, width, height);
}
}
});
solver.start();
}
/**
* Sets the pen color to the specified color.
*
* @param color the color to make the pen
*/
public void setPenColor(Color color) {
gc.setStroke(color);
}
/**
* Sets the pen color to the specified color.
*
* @param color the color to make the pen
*/
public void setFillColor(Color color) {
gc.setFill(color);
}
/**
* Sets the x-scale to the specified range.
*
* @param min the minimum value of the x-scale
* @param max the maximum value of the x-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public void setXscale(double min, double max) {
double size = max - min;
if (size == 0.0) {
throw new IllegalArgumentException("the min and max are the same");
}
xmin = min - BORDER * size;
21. xmax = max + BORDER * size;
}
/**
* Sets the y-scale to the specified range.
*
* @param min the minimum value of the y-scale
* @param max the maximum value of the y-scale
* @throws IllegalArgumentException if {@code (max == min)}
*/
public void setYscale(double min, double max) {
double size = max - min;
if (size == 0.0) {
throw new IllegalArgumentException("the min and max are the same");
}
ymin = min - BORDER * size;
ymax = max + BORDER * size;
}
// helper functions that scale from user coordinates to screen coordinates and back
private double scaleX(double x) {
return width * (x - xmin) / (xmax - xmin);
}
private double scaleY(double y) {
return height * (ymax - y) / (ymax - ymin);
}
private double factorX(double w) {
return w * width / Math.abs(xmax - xmin);
}
private double factorY(double h) {
return h * height / Math.abs(ymax - ymin);
}
private double userX(double x) {
return xmin + x * (xmax - xmin) / width;
}
private double userY(double y) {
return ymax - y * (ymax - ymin) / height;
}
22. /**
* Draws a line segment between (x0,
* y0) and (x1,
* y1).
*
* @param x0 the x-coordinate of one endpoint
* @param y0 the y-coordinate of one endpoint
* @param x1 the x-coordinate of the other endpoint
* @param y1 the y-coordinate of the other endpoint
*/
public void line(double x0, double y0, double x1, double y1) {
gc.strokeLine(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1));
}
/**
* Draws one pixel at (x, y). This method is private
* because pixels depend on the display. To achieve the same effect, set the
* pen radius to 0 and call {@code point()}.
*
* @param x the x-coordinate of the pixel
* @param y the y-coordinate of the pixel
*/
private void pixel(double x, double y) {
gc.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1);
}
/**
* Draws a filled circle of the specified radius, centered at (x,
* y).
*
* @param x the x-coordinate of the center of the circle
* @param y the y-coordinate of the center of the circle
* @param radius the radius of the circle
* @throws IllegalArgumentException if {@code radius} is negative
*/
public void filledCircle(double x, double y, double radius) {
double xs = scaleX(x);
double ys = scaleY(y);