2020-11-18 18:01:57 +01:00
# include <iostream>
2020-11-26 22:17:36 +01:00
# include <chrono>
# include <thread>
2020-11-18 18:01:57 +01:00
# include "Display.h"
2020-11-19 09:46:02 +01:00
# include "Point.h"
2020-11-26 22:17:36 +01:00
# include "Utility.h"
2020-11-18 18:01:57 +01:00
2020-11-30 15:43:42 +01:00
Display : : Display ( const std : : vector < Point > & pts , int stepSize , bool akl ) : m_stepSize ( stepSize ) , m_useAkl ( akl )
2020-11-26 22:17:36 +01:00
{
2020-11-29 04:04:25 +01:00
if ( ! m_font . loadFromFile ( " Resources/LiberationSans-Regular.ttf " ) )
2020-11-26 22:17:36 +01:00
{
2020-11-28 23:30:31 +01:00
std : : cerr < < " font LiberationSans-Regular.ttf could not be loaded! " < < std : : endl ;
2020-11-26 22:17:36 +01:00
}
m_textStatus . setPosition ( OFFSET , HEIGHT - 2 * OFFSET ) ;
m_textStatus . setFont ( m_font ) ;
m_textStatus . setString ( " initializing... " ) ;
m_textStatus . setCharacterSize ( 12 ) ;
m_textStatus . setFillColor ( sf : : Color : : Black ) ;
2020-11-27 03:09:50 +01:00
size_t points = pts . size ( ) ;
for ( size_t i = 0 ; i < points ; + + i )
{
const Point & pt = pts [ i ] ;
sf : : CircleShape shape ( OFFSET ) ;
shape . setOrigin ( OFFSET , OFFSET ) ;
shape . setPosition ( pt . x ( ) , pt . y ( ) ) ;
shape . setFillColor ( sf : : Color : : Green ) ;
shape . setOutlineThickness ( 1.f ) ;
shape . setOutlineColor ( sf : : Color : : Black ) ;
m_points . push_back ( shape ) ;
sf : : Text label ;
label . setOrigin ( OFFSET / 2 - 1 , OFFSET / 2 + 3 ) ;
label . setPosition ( pt . x ( ) , pt . y ( ) ) ;
label . setFont ( m_font ) ;
label . setString ( std : : to_string ( i ) ) ;
label . setCharacterSize ( 12 ) ;
label . setFillColor ( sf : : Color : : Black ) ;
m_labels . push_back ( label ) ;
}
2020-11-26 22:17:36 +01:00
}
// multiple options possible:
// a) draw every frame, using elapsed time for updates
// --> not really needed because we have no animations... also uses a lot of performance
// b) checking every frame if elapsed time since last tick is larger than a specified offset, then draw the field
// --> mostly doing nothing but still uses a lot of performance O_o
// c) just start a simple (c++11 <3) non-busy sleep
// --> may delay user input / events but don't care atm :p
2020-11-18 18:01:57 +01:00
void Display : : show ( )
{
2020-11-26 22:17:36 +01:00
sf : : ContextSettings settings ;
2020-11-27 03:09:50 +01:00
settings . antialiasingLevel = 8 ;
2020-11-26 22:17:36 +01:00
sf : : RenderWindow window ( sf : : VideoMode ( WIDTH , HEIGHT ) , " ALGO Prog2: Quickhull - visualization " , sf : : Style : : Default , settings ) ;
2020-11-27 03:09:50 +01:00
/* b)
window . setFramerateLimit ( 0.2 ) ;
2020-11-26 22:17:36 +01:00
2020-11-27 03:09:50 +01:00
sf : : Clock clock ;
sf : : Time frameTime = sf : : seconds ( 2 ) ; //sf::seconds(1.f / 60.f)
sf : : Time timeSinceLastUpdate = frameTime ; //sf::Time::Zero;*/
2020-11-18 18:01:57 +01:00
while ( window . isOpen ( ) )
{
sf : : Event event ;
while ( window . pollEvent ( event ) )
{
if ( event . type = = sf : : Event : : Closed )
window . close ( ) ;
}
2020-11-27 03:09:50 +01:00
// a)
//update(elapsed);
//render(window);
2020-11-26 22:17:36 +01:00
/* b)
timeSinceLastUpdate + = clock . restart ( ) ;
// only handle every few seconds because we need no animations
2020-11-27 03:09:50 +01:00
if ( timeSinceLastUpdate > = frameTime )
2020-11-26 22:17:36 +01:00
{
2020-11-27 03:09:50 +01:00
std : : cout < < " entering update and render " < < std : : endl ;
2020-11-26 22:17:36 +01:00
// start by getting the most left and right point
timeSinceLastUpdate - = frameTime ;
2020-11-27 03:09:50 +01:00
update ( ) ;
render ( window ) ;
} */
2020-11-26 22:17:36 +01:00
// c)
2020-11-27 03:09:50 +01:00
// choose a simple sleep
update ( ) ;
render ( window ) ;
2020-11-29 14:51:55 +01:00
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( m_stepSize ) ) ;
2020-11-26 22:17:36 +01:00
}
}
2020-11-27 03:09:50 +01:00
void Display : : update ( )
2020-11-26 22:17:36 +01:00
{
2020-11-27 03:09:50 +01:00
unsigned int curStep = ( m_step > 5 ) ? ( ( m_step - 2 ) % 4 + 2 ) : ( m_step % 6 ) ; // skip init and first step after first run
2020-11-29 23:32:54 +01:00
if ( m_stepSize = = 0 & & curStep > 0 )
{
std : : cout < < " any key to continue with next step... " ;
std : : cin . get ( ) ;
}
2020-11-27 03:09:50 +01:00
std : : string text = " ( " + std : : to_string ( m_step ) + " ) step " + std : : to_string ( curStep ) + " : " ;
if ( curStep = = 1 )
2020-11-26 22:17:36 +01:00
{
// first step: select min - max x coordinates
2020-11-27 03:09:50 +01:00
m_textStatus . setString ( text + " select min - max x coordinates... " ) ;
// if use Akl<6B> Toussaint heuristic
2020-11-30 15:43:42 +01:00
if ( m_useAkl )
2020-11-27 03:09:50 +01:00
{
sf : : Vector2f topLeft ( WIDTH , HEIGHT ) ;
sf : : Vector2f topRight ( 0 , HEIGHT ) ;
sf : : Vector2f botLeft ( WIDTH , 0 ) ;
sf : : Vector2f botRight ( 0 , 0 ) ;
for ( auto & pt : m_points )
{
sf : : Vector2f pos = pt . getPosition ( ) ;
float x = pos . x ;
float y = pos . y ;
2020-11-27 03:48:34 +01:00
// TODO: only check explicit x and y seperate!
2020-11-27 03:09:50 +01:00
if ( x < topLeft . x & & y < topLeft . y ) topLeft = pos ;
if ( x > topRight . x & & y < topLeft . y ) topRight = pos ;
if ( x < botLeft . x & & y > botLeft . y ) botLeft = pos ;
if ( x > botRight . x & & y > botRight . y ) botRight = pos ;
}
m_hull . setPrimitiveType ( sf : : LineStrip ) ;
m_hull . append ( sf : : Vertex ( topLeft , sf : : Color : : Blue ) ) ;
m_hull . append ( sf : : Vertex ( topRight , sf : : Color : : Blue ) ) ;
m_hull . append ( sf : : Vertex ( botRight , sf : : Color : : Blue ) ) ;
m_hull . append ( sf : : Vertex ( botLeft , sf : : Color : : Blue ) ) ;
m_hull . append ( sf : : Vertex ( topLeft , sf : : Color : : Blue ) ) ;
}
else
{
sf : : Vector2f left ( WIDTH , HEIGHT ) ;
sf : : Vector2f right ( 0 , 0 ) ;
2020-11-29 23:32:54 +01:00
int i_left = WIDTH ;
int i_right = 0 ;
size_t points = m_points . size ( ) ;
for ( size_t i = 0 ; i < points ; + + i )
2020-11-27 03:09:50 +01:00
{
2020-11-29 23:32:54 +01:00
sf : : Vector2f pos = m_points [ i ] . getPosition ( ) ;
2020-11-27 03:09:50 +01:00
float x = pos . x ;
2020-11-29 23:32:54 +01:00
if ( x < left . x )
{
i_left = i ;
left = pos ;
}
if ( x > right . x )
{
i_right = i ;
right = pos ;
}
2020-11-27 03:09:50 +01:00
}
2020-11-29 23:32:54 +01:00
// adding starting points from most x to left
if ( i_left < i_right )
{
std : : swap ( i_left , i_right ) ;
}
for ( int i = 0 ; i < 2 ; + + i )
{
int pos = ( i = = 0 ) ? i_left : i_right ;
m_points [ pos ] . setFillColor ( sf : : Color : : Blue ) ;
m_hullPoints . push_back ( m_points [ pos ] ) ;
m_points . erase ( m_points . begin ( ) + pos ) ;
}
2020-11-29 04:04:25 +01:00
m_lines . push_back ( Line ( Point ( left . x , left . y ) , Point ( right . x , right . y ) ) ) ;
m_lines . push_back ( Line ( Point ( right . x , right . y ) , Point ( left . x , left . y ) ) ) ; // add first line in both directions to work with "right side"
2020-11-29 23:32:54 +01:00
m_curLineIdx = 1 ;
m_curLine = m_lines [ 1 ] ;
2020-11-29 04:04:25 +01:00
// TODO: split points and use map
2020-11-27 03:09:50 +01:00
}
2020-11-26 22:17:36 +01:00
}
2020-11-27 03:09:50 +01:00
else if ( curStep = = 2 )
2020-11-26 22:17:36 +01:00
{
2020-11-29 04:04:25 +01:00
// second step: split board
m_textStatus . setString ( text + " split board... " ) ;
2020-11-29 23:32:54 +01:00
// get current line with points
while ( m_curLineIdx = = - 1 )
2020-11-29 04:04:25 +01:00
{
size_t lines = m_lines . size ( ) ;
2020-11-29 14:51:55 +01:00
if ( lines < 1 ) // no more open lines -> fin
{
2020-11-29 23:32:54 +01:00
std : : cout < < " ## no more open lines -> fin! " < < std : : endl ;
2020-11-29 14:51:55 +01:00
m_points . clear ( ) ;
2020-11-30 00:23:21 +01:00
if ( m_points . size ( ) = = 0 ) m_textStatus . setString ( text + " finished calculating convex hull in " + std : : to_string ( ( m_step / 4 + 1 ) ) + " cycles " ) ;
2020-11-29 14:51:55 +01:00
return ;
}
2020-11-29 04:04:25 +01:00
2020-11-29 23:32:54 +01:00
Line cand = m_lines [ lines - 1 ] ;
int positives = 0 ;
for ( auto & pt : m_points )
{
2020-11-30 00:39:15 +01:00
if ( cand . is_point_right ( Point ( pt . getPosition ( ) . x , pt . getPosition ( ) . y ) ) )
{
positives + + ;
break ; // sufficient as candidate if at least one point is right
}
/*positives += (sign(pt.getPosition().x, pt.getPosition().y,
2020-11-29 23:32:54 +01:00
cand . from ( ) . x ( ) , cand . from ( ) . y ( ) ,
2020-11-30 00:39:15 +01:00
cand . to ( ) . x ( ) , cand . to ( ) . y ( ) ) > 0 ) ? 1 : 0 ; */
2020-11-29 23:32:54 +01:00
}
2020-11-29 04:04:25 +01:00
2020-11-29 23:32:54 +01:00
if ( positives > 0 )
{
m_curLineIdx = lines - 1 ;
m_curLine = m_lines [ lines - 1 ] ;
}
else
{
m_lines . pop_back ( ) ;
}
2020-11-29 14:51:55 +01:00
}
2020-11-27 03:09:50 +01:00
for ( auto & pt : m_points )
{
2020-11-27 03:48:34 +01:00
pt . setFillColor ( sign (
pt . getPosition ( ) . x , pt . getPosition ( ) . y ,
2020-11-29 23:32:54 +01:00
m_lines [ m_curLineIdx ] . from ( ) . x ( ) , m_lines [ m_curLineIdx ] . from ( ) . y ( ) ,
m_lines [ m_curLineIdx ] . to ( ) . x ( ) , m_lines [ m_curLineIdx ] . to ( ) . y ( ) ) > 0 ? sf : : Color : : Red : sf : : Color : : Green ) ;
2020-11-27 03:09:50 +01:00
}
2020-11-18 18:01:57 +01:00
}
2020-11-27 03:09:50 +01:00
else if ( curStep = = 3 )
2020-11-26 22:17:36 +01:00
{
2020-11-29 04:04:25 +01:00
// get line with more than one point -> or use m_curLine
// calc furthest point
// create new lines
2020-11-26 22:17:36 +01:00
// third step: draw triangle, remove inner points
2020-11-27 03:09:50 +01:00
m_textStatus . setString ( text + " find furthest point and draw triangle... " ) ;
2020-11-27 03:48:34 +01:00
2020-11-29 23:32:54 +01:00
sf : : Vector2f pos ;
2020-11-27 03:48:34 +01:00
float maxDistance = 0 ;
2020-11-29 23:32:54 +01:00
int i_cand = 0 ;
size_t points = m_points . size ( ) ;
for ( size_t i = 0 ; i < points ; + + i )
2020-11-27 03:48:34 +01:00
{
2020-11-29 23:32:54 +01:00
if ( m_points [ i ] . getFillColor ( ) = = sf : : Color : : Green ) continue ;
2020-11-29 04:04:25 +01:00
2020-11-29 23:32:54 +01:00
sf : : Vector2f cand = m_points [ i ] . getPosition ( ) ;
2020-11-30 00:39:15 +01:00
float distance = m_lines [ m_curLineIdx ] . distance_squared_to ( Point ( cand . x , cand . y ) ) ;
2020-11-27 03:48:34 +01:00
if ( distance > maxDistance )
{
2020-11-29 23:32:54 +01:00
i_cand = i ;
pos = cand ;
2020-11-27 03:48:34 +01:00
maxDistance = distance ;
}
}
if ( maxDistance > 0 )
{
2020-11-29 23:32:54 +01:00
// move point from all points to hull points
m_points [ i_cand ] . setFillColor ( sf : : Color : : Blue ) ;
m_hullPoints . push_back ( m_points [ i_cand ] ) ;
std : : cout < < " removing pt " < < m_points [ i_cand ] . getPosition ( ) . x < < " , " < < m_points [ i_cand ] . getPosition ( ) . y < < " at index " < < std : : to_string ( i_cand ) < < " after inserted to hullpoints " < < std : : endl ;
m_points . erase ( m_points . begin ( ) + i_cand ) ;
Point to = m_lines [ m_curLineIdx ] . to ( ) ;
Point from = m_lines [ m_curLineIdx ] . from ( ) ;
m_lines . push_back ( Line ( Point ( pos . x , pos . y ) , to ) ) ;
m_lines [ m_lines . size ( ) - 2 ] = Line ( from , Point ( pos . x , pos . y ) ) ; // updates list entry
2020-11-29 04:04:25 +01:00
}
else
{
m_lines . pop_back ( ) ; // remove last element -> TODO: directly use stack?
2020-11-27 03:48:34 +01:00
}
2020-11-26 22:17:36 +01:00
}
2020-11-27 03:09:50 +01:00
else if ( curStep = = 4 )
2020-11-26 22:17:36 +01:00
{
// fourth step: remove inner points
2020-11-27 03:09:50 +01:00
m_textStatus . setString ( text + " remove inner points... " ) ;
2020-11-29 04:04:25 +01:00
2020-11-29 23:32:54 +01:00
size_t lines = m_lines . size ( ) - 1 ;
2020-11-30 00:23:21 +01:00
if ( lines > 0 )
2020-11-29 04:04:25 +01:00
{
2020-11-29 23:32:54 +01:00
// assure clockwise order
Point pt1 = m_lines [ lines - 1 ] . from ( ) ;
Point pt2 = m_lines [ lines - 1 ] . to ( ) ;
Point pt3 = m_lines [ lines ] . to ( ) ;
if ( sign ( pt2 , pt1 , pt3 ) > 0 )
2020-11-29 04:04:25 +01:00
{
2020-11-29 23:32:54 +01:00
std : : swap ( pt1 , pt2 ) ;
2020-11-29 04:04:25 +01:00
}
2020-11-29 23:32:54 +01:00
size_t points = m_points . size ( ) ;
for ( int i = points - 1 ; i > = 0 ; i - - )
{
Point pt ( m_points [ i ] . getPosition ( ) . x , m_points [ i ] . getPosition ( ) . y ) ;
if ( IsPointInTriangle ( pt , pt1 , pt2 , pt3 ) )
{
std : : cout < < " remove pt inside triangle -> " < < i < < " with pos: " < < pt . x ( ) < < " , " < < pt . y ( ) < < std : : endl ;
m_points . erase ( m_points . begin ( ) + i ) ;
}
else
{
m_points [ i ] . setFillColor ( sf : : Color : : Green ) ;
}
}
}
//m_curLine = nullptr;
//m_curLineIdx = -1;
2020-11-26 22:17:36 +01:00
}
2020-11-27 03:09:50 +01:00
else if ( curStep = = 5 )
2020-11-26 22:17:36 +01:00
{
2020-11-27 03:09:50 +01:00
// fifth step: adding new hull point
2020-11-26 22:17:36 +01:00
2020-11-29 23:32:54 +01:00
m_curLineIdx = - 1 ;
//m_curLine = m_lines[lines - 1];
2020-11-27 03:09:50 +01:00
2020-11-30 00:23:21 +01:00
if ( m_points . size ( ) = = 0 ) m_textStatus . setString ( text + " finished calculating convex hull in " + std : : to_string ( ( m_step / 4 + 1 ) ) + " cycles " ) ;
2020-11-27 03:09:50 +01:00
else m_textStatus . setString ( text + " adding new hull point... " ) ;
2020-11-26 22:17:36 +01:00
}
2020-11-29 14:51:55 +01:00
else if ( m_step > 0 ) m_textStatus . setString ( text + " invalid status! " ) ;
2020-11-27 03:09:50 +01:00
if ( curStep ! = 5 | | m_points . size ( ) > 0 ) m_step + + ;
2020-11-19 09:46:02 +01:00
}
2020-11-29 23:48:18 +01:00
bool less ( sf : : CircleShape a , sf : : CircleShape b )
{
float centerx = WIDTH / 2 ;
float centery = HEIGHT / 2 ;
float ax = a . getPosition ( ) . x ;
float ay = a . getPosition ( ) . y ;
float bx = b . getPosition ( ) . x ;
float by = b . getPosition ( ) . y ;
if ( ax - centerx > = 0 & & bx - centerx < 0 )
return true ;
if ( ax - centerx < 0 & & bx - centerx > = 0 )
return false ;
if ( ax - centerx = = 0 & & bx - centerx = = 0 ) {
if ( ay - centery > = 0 | | by - centery > = 0 )
return ay > by ;
return by > ay ;
}
// compute the cross product of vectors (center -> a) x (center -> b)
2020-11-30 16:06:30 +01:00
int det = ( int ) ( ( ax - centerx ) * ( by - centery ) - ( bx - centerx ) * ( ay - centery ) ) ;
2020-11-29 23:48:18 +01:00
if ( det < 0 )
return true ;
if ( det > 0 )
return false ;
// points a and b are on the same line from the center
// check which point is closer to the center
2020-11-30 16:06:30 +01:00
int d1 = ( int ) ( ( ax - centerx ) * ( ax - centerx ) + ( ay - centery ) * ( ay - centery ) ) ;
int d2 = ( int ) ( ( bx - centerx ) * ( bx - centerx ) + ( by - centery ) * ( by - centery ) ) ;
2020-11-29 23:48:18 +01:00
return d1 > d2 ;
}
2020-11-27 03:09:50 +01:00
void Display : : render ( sf : : RenderWindow & window )
2020-11-19 09:46:02 +01:00
{
2020-11-27 03:09:50 +01:00
window . clear ( sf : : Color : : White ) ;
2020-11-29 23:32:54 +01:00
// draw already calculated hull points
2020-11-29 23:48:18 +01:00
std : : sort ( m_hullPoints . begin ( ) , m_hullPoints . end ( ) , less ) ; // sort clockwise
2020-11-29 23:32:54 +01:00
size_t elements = m_hullPoints . size ( ) ;
for ( size_t i = 0 ; i < elements ; + + i )
2020-11-27 03:09:50 +01:00
{
2020-11-29 23:32:54 +01:00
window . draw ( m_hullPoints [ i ] ) ;
2020-11-29 14:51:55 +01:00
2020-11-29 23:32:54 +01:00
sf : : Vertex ptTo ;
if ( i < elements - 1 ) ptTo = sf : : Vertex ( m_hullPoints [ i + 1 ] . getPosition ( ) , sf : : Color : : Blue ) ;
else ptTo = sf : : Vertex ( m_hullPoints [ 0 ] . getPosition ( ) , sf : : Color : : Blue ) ;
sf : : Vertex line [ ] = { sf : : Vertex ( sf : : Vector2f ( m_hullPoints [ i ] . getPosition ( ) ) , sf : : Color : : Blue ) , ptTo } ;
2020-11-27 03:09:50 +01:00
2020-11-29 23:32:54 +01:00
window . draw ( line , 2 , sf : : Lines ) ;
2020-11-29 04:04:25 +01:00
}
// always print remaining points
2020-11-29 23:32:54 +01:00
elements = m_points . size ( ) ;
for ( size_t i = 0 ; i < elements ; + + i )
2020-11-27 03:09:50 +01:00
{
window . draw ( m_points [ i ] ) ;
2020-11-29 23:32:54 +01:00
}
// seperately print labels for points
elements = m_labels . size ( ) ;
for ( size_t i = 0 ; i < elements ; + + i )
{
2020-11-27 03:09:50 +01:00
window . draw ( m_labels [ i ] ) ;
}
2020-11-29 04:04:25 +01:00
// draw line the algorithm is currently working on
2020-11-29 23:32:54 +01:00
if ( m_curLineIdx ! = - 1 )
2020-11-27 03:09:50 +01:00
{
2020-11-29 04:04:25 +01:00
sf : : Vertex line [ ] =
{
2020-11-29 23:32:54 +01:00
sf : : Vertex ( sf : : Vector2f ( m_curLine . from ( ) . x ( ) , m_curLine . from ( ) . y ( ) ) , sf : : Color : : Red ) ,
sf : : Vertex ( sf : : Vector2f ( m_curLine . to ( ) . x ( ) , m_curLine . to ( ) . y ( ) ) , sf : : Color : : Red )
2020-11-29 04:04:25 +01:00
} ;
window . draw ( line , 2 , sf : : Lines ) ;
2020-11-27 03:09:50 +01:00
}
2020-11-29 04:04:25 +01:00
// show amount of steps and current status
2020-11-27 03:09:50 +01:00
window . draw ( m_textStatus ) ;
window . display ( ) ;
2020-11-18 18:01:57 +01:00
}