Skip to content

jakalm/Line_router

Repository files navigation

Line router

A very simple C++ application routing lines to not intersect each other. The Graphical User Interface (GUI) consists of a 600 x 600 pixel map in which it is possible to draw lines by mouse clicks. The first click will set the start point of the line and the second click will set the end point and draw the line. The line will be drawn so that it will not intersect with any other line nor the pixel map border.

Requirements

This has only been tested on Ubuntu Linux 18.04 LTS running

  • CMake 3.10.2
  • QT 5.9.5
  • libcanberra-gtk-module 0.30
  • gcc/g++ 7.3.0
  • GNU Make 4.1

with common software development packages. It is not configured to build for Windows yet.

Build

All the commands listed here are assumed to be run in a Linux shell.
To configure the CMake build environment (or to clean it), go to the Build directory under the root path and run

./cmake_build.sh

This should only have to be run once initially or when wanting to clean the CMake environment. To make the Line router application run

make line_router

On success the binary file will end up in the Bin/line_router folder under the root path.

Unit tests

Google Test 1.8.0 has been integrated with the source code and will be built when building the tests. Configure the CMake build environment if not already done so (see the Build section above)

./cmake_build.sh

To build and run the tests run

make all test

Code structure

CMake and GNU Make is used as a build environment to generate the executable. The source code consists of three main parts

  • Line router main
  • UI (QT 5)
  • Path planner (A*)

and grid help classes under Grid.

Line router main

The main function decides what kind of path planner to use (currently there is only A_star_planner available) and its grid size. Right now it creates a 600 x 600 A_star_planner (Path_planner). It will also create and start the UI window Line_router_window and pass along the Path_planner.

UI (QT 5)

The UI is using the QT 5 toolkit. It consists of one window, the Line_router_window. It sets up the window, creates a Line_router_paint_widget and passes along the Path_planner.
The Line_router_paint_widget sets up the pixel map environment where it is possible to draw lines. It will wait for a mouse click on a point that is inside of the pixel map and set that point to be the start point. Then the start point is marked with a red dot on the pixel map. The second mouse click inside the pixel map will set the end point and will pass the start and end point to the Path_planner. If the Path_planner is successful in finding a path from start to end a line will be drawn on the given path and all the points passed will be set to blocked in the Path_planner. If the Path_planner is unsuccessful in finding a path it will just unmark the first point and wait for the first mouse click again. Would have been nice with a dialog popup in those cases.

Path planner (A*)

The algorithm to find the route from start to end is based on the search algorithm A*. It is designed to find the path from the start point to the given end point which has the smallest cost. In this implementation the cost is the pixel-distance traveled.

The algorithm has to determine, for each point visited, the cost to the point where it is currently at and an estimate of the cost to the end point from the current point. More specifically:

f(p) = g(p) + h(p)

where g(p) is the cost from the start point to the point p and h(p) is the estimated cost from point p to the end point. f(p) is the total minimal cost from start to end going through the point p.

In this implementation the horizontal and vertical travel of one point increases the g(p) by one. A diagonal move, however, increases the g(p,s) by

sqrt( 1^2 + 1^2 ) ~= 1.4142136.

The estimate of the cost to the end point is calculated as the coordinate distance from point p to the end point, such -that

h(p) = sqrt( dx^2 + dy^2 )

where dx is the x-coordinate distance from start point to point p and dy is the y-coordinate distance from start point to point p.

The algorithm will calculate and store the total cost f(p) for the point currently visited, p, and its eight neighbors (horizontal, vertical and diagonal). Neighbors outside border are not considered. It will then decide what point to visit by choosing the point with the currently cheapest total minimal cost f(p). It could be a neighbor to the currently visited point or a point visited previously or its neighbors.

If a point has another line crossing it, it will not be considered for visit and the cost will remain infinite.
For more general information, see A* search algorithm.

Grid

There are three grids implemented (if not counting the QPixmap)

  • Availability grid
  • Path cost grid
  • Path grid

All grids are implemented as flattened fixed sized std::vector. In a width x height grid the index in the std::vector for the width coordinate x and height coordinate y is as follows

index = x + y * width

where x and y are zero indexed. And for calculating x and y from index

x = modulus( index, width )
y = floor( index / width )

All grids have the same size as the QPixmap, right now it is 600 x 600. The total size of all the 600 x 600 grids, discarding std::vector overhead, is

sizeof(bool)x600x600 + sizeof(float)x600x600 + sizeof(size_t)x600x600

On my 64-bit system with g++ 7.3.0 this makes a total cost of

1x600x600 + 4x600x600 + 8x600x600 ~= 4.46 MB

Availability grid

This bool grid is used to set available/blocked grid points. It is initialized with all true values, i.e. available. Once a line has been drawn all the points it has been passing and all of their neighbors will be marked as false, i.e. blocked. The neighbors are marked in order to more clearly see that the lines are not intersecting.

Path cost grid

This float grid is used to keep track of the path cost g(p) (see Path finding algorithm section) for each visited point. It is initialized to infinity, except for the starting position which has a zero value. The path cost grid will be updated for the currently visited point and its available neighbors.

Path grid

This size_t grid consists of flattened grid indexes that point to a previous neighbor point visited. It is updated by the currently visited point that sets all its available neighbors to the currently visited flattened grid index. When the end point has been reached this grid can be used to backtrack the path to the start point.

The grid is initialized to zero but the initial value does not matter since the path grid won't be used (only updated) until the end point has been reached.

Performance

The code has been tested on Ubuntu Linux 18.04 LTS running on a Dell XPS 15 Infinity (9560) with the following specifications:

  • CPU: Intel Core i7 7th Gen. 2.8 Ghz.
  • GPU: Nvidia GeForce GTX 1050 Ti Max-Q.
  • RAM: 16GB DDR4/2,400MHz.

For a 600 x 600 grid with all points available it takes around 0.4 ms to find a path from point (0, 0) to (599, 599).
For a 2000 x 2000 grid with all points available it takes around 2.3 ms to find a path from point (0, 0) to (1999, 1999).
For a 600 x 600 grid with a diagonal block from point (1, 598) to (599, 0) it takes around 85 ms to find a path from point (0, 0) o (599, 599).
For a 2000 x 2000 grid with a diagonal block from point (1, 1998) to (1999, 0) it takes around 1.1 s.

The most costly parts of the code is when checking if neighbors are available and when adjusting the heap of the priority queue where the points to visit are located.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published