2019-06-03 10:09:00 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
# include <math.h>
# include <time.h>
# include <gdk/gdkkeysyms.h>
# include <gtk/gtk.h>
# define DEFAULT_WIDTH 800
# define DEFAULT_HEIGHT 800
struct ball {
2019-06-03 22:24:06 +02:00
double x ;
double y ;
2019-06-03 10:09:00 +02:00
unsigned int radius ;
2019-06-03 22:24:06 +02:00
double v_x ;
double v_y ;
2019-06-05 14:31:11 +02:00
2019-12-18 21:18:21 +01:00
unsigned int rotation_steps ;
double angle ;
double v_angle ;
cairo_surface_t * * faces ;
2019-06-03 10:09:00 +02:00
} ;
2019-08-20 16:15:29 +02:00
double delta = 0.01 ; /* seconds */
2019-08-10 11:11:14 +02:00
2019-08-20 16:15:29 +02:00
unsigned int width = DEFAULT_WIDTH ;
unsigned int height = DEFAULT_HEIGHT ;
2019-06-03 10:09:00 +02:00
2019-08-20 16:15:29 +02:00
unsigned int radius_min = 5 ;
unsigned int radius_max = 10 ;
2019-06-03 10:09:00 +02:00
2019-08-20 16:15:29 +02:00
unsigned int v_max = 100 ;
unsigned int v_min = 0 ;
2019-06-03 10:09:00 +02:00
2019-12-18 21:18:21 +01:00
unsigned int v_angle_min = 0 ;
unsigned int v_angle_max = 100 ;
2019-06-03 10:09:00 +02:00
struct ball * balls = 0 ;
2019-06-04 10:13:02 +02:00
unsigned int n_balls = 50 ;
2019-06-03 10:09:00 +02:00
2019-08-20 16:15:29 +02:00
double g_y = 20 ;
double g_x = 0 ;
2019-06-03 10:09:00 +02:00
2019-08-20 16:15:29 +02:00
double clear_alpha = 1.0 ;
2019-07-29 15:48:15 +02:00
2019-08-20 16:15:29 +02:00
void random_velocity ( struct ball * p ) {
2019-07-05 15:58:10 +02:00
double r2 ;
do {
p - > v_x = v_min + rand ( ) % ( v_max + 1 - v_min ) ;
p - > v_y = v_min + rand ( ) % ( v_max + 1 - v_min ) ;
r2 = p - > v_x * p - > v_x + p - > v_y * p - > v_y ;
} while ( r2 > v_max * v_max | | r2 < v_min * v_min ) ;
}
2019-06-03 22:24:06 +02:00
void balls_init_state ( ) {
2019-06-03 10:09:00 +02:00
srand ( time ( NULL ) ) ;
static const unsigned int border = 10 ;
unsigned int w = width < 2 * border ? 1 : width - 2 * border ;
unsigned int h = height < 2 * border ? 1 : height - 2 * border ;
2019-06-04 10:13:02 +02:00
2019-06-03 10:09:00 +02:00
for ( unsigned int i = 0 ; i < n_balls ; + + i ) {
balls [ i ] . x = border + rand ( ) % w ;
balls [ i ] . y = border + rand ( ) % h ;
2019-07-05 15:58:10 +02:00
random_velocity ( balls + i ) ;
2019-06-05 14:51:02 +02:00
if ( rand ( ) % 2 )
balls [ i ] . v_x = - balls [ i ] . v_x ;
if ( rand ( ) % 2 )
balls [ i ] . v_y = - balls [ i ] . v_y ;
2019-06-04 10:13:02 +02:00
balls [ i ] . radius = radius_min + rand ( ) % ( radius_max + 1 - radius_min ) ;
2019-12-18 21:18:21 +01:00
unsigned int v_angle_360 = ( v_angle_min + rand ( ) % ( v_angle_max + 1 - v_angle_min ) ) % 360 ;
balls [ i ] . v_angle = 2 * M_PI * v_angle_360 / 360 ;
balls [ i ] . angle = ( rand ( ) % 360 ) * 2 * M_PI / 360 ;
2019-06-03 22:24:06 +02:00
}
}
2019-08-20 16:15:29 +02:00
void ball_collision ( struct ball * p , struct ball * q ) {
2019-06-03 22:24:06 +02:00
double dx = q - > x - p - > x ;
double dy = q - > y - p - > y ;
double d2 = dx * dx + dy * dy ;
double r = p - > radius + q - > radius ;
if ( d2 < = r * r ) {
double dv_x = q - > v_x - p - > v_x ;
double dv_y = q - > v_y - p - > v_y ;
double mp = p - > radius * p - > radius ;
double mq = q - > radius * q - > radius ;
double f = dv_x * dx + dv_y * dy ;
if ( f < 0 ) {
f / = d2 * ( mp + mq ) ;
p - > v_x + = 2 * mq * f * dx ;
p - > v_y + = 2 * mq * f * dy ;
q - > v_x - = 2 * mp * f * dx ;
q - > v_y - = 2 * mp * f * dy ;
}
2019-06-03 10:09:00 +02:00
}
}
2019-08-19 10:45:55 +02:00
void ball_update_state ( struct ball * p ) {
2019-06-03 10:09:00 +02:00
p - > x + = delta * p - > v_x + delta * delta * g_x / 2.0 ;
p - > v_x + = delta * g_x ;
p - > y + = delta * p - > v_y + delta * delta * g_y / 2.0 ;
p - > v_y + = delta * g_y ;
if ( p - > x + p - > radius > width ) {
2019-06-03 22:24:06 +02:00
if ( p - > v_x > 0 ) {
p - > x - = p - > x + p - > radius - width ;
p - > v_x = - p - > v_x ;
}
2019-06-03 10:09:00 +02:00
} else if ( p - > x < p - > radius ) {
2019-06-03 22:24:06 +02:00
if ( p - > v_x < 0 ) {
p - > x + = p - > radius - p - > x ;
p - > v_x = - p - > v_x ;
}
2019-06-03 10:09:00 +02:00
}
if ( p - > y + p - > radius > height ) {
2019-06-03 22:24:06 +02:00
if ( p - > v_y > 0 ) {
p - > y - = p - > y + p - > radius - height ;
p - > v_y = - p - > v_y ;
}
2019-06-03 10:09:00 +02:00
} else if ( p - > y < p - > radius ) {
2019-06-03 22:24:06 +02:00
if ( p - > v_y < 0 ) {
p - > y + = p - > radius - p - > y ;
p - > v_y = - p - > v_y ;
}
2019-06-03 10:09:00 +02:00
}
2019-12-18 21:18:21 +01:00
p - > angle + = delta * p - > v_angle ;
if ( p - > angle > = 2 * M_PI )
p - > angle - = 2 * M_PI ;
2019-06-03 10:09:00 +02:00
}
2019-08-19 10:45:55 +02:00
void movement_and_borders ( ) {
2019-06-03 10:09:00 +02:00
for ( int i = 0 ; i < n_balls ; + + i )
ball_update_state ( balls + i ) ;
}
2019-08-20 16:15:29 +02:00
/* Collision check with index
*/
struct rectangle {
double min_x ; /* left */
double min_y ; /* bottom */
double max_x ; /* right */
double max_y ; /* top */
} ;
struct bt_node {
struct ball * ball ;
struct rectangle r ;
struct bt_node * left ;
struct bt_node * right ;
} ;
struct bt_node * c_index = 0 ;
static struct bt_node * c_index_init_node ( struct bt_node * n , struct ball * b ) {
n - > ball = b ;
n - > r . min_x = b - > x - b - > radius ;
n - > r . min_y = b - > y - b - > radius ;
n - > r . max_x = b - > x + b - > radius ;
n - > r . max_y = b - > y + b - > radius ;
n - > left = 0 ;
n - > right = 0 ;
return n ;
}
static void c_index_add_ball ( struct bt_node * n , const struct ball * b ) {
if ( n - > r . min_x > b - > x - b - > radius )
n - > r . min_x = b - > x - b - > radius ;
if ( n - > r . min_y > b - > y - b - > radius )
n - > r . min_y = b - > y - b - > radius ;
if ( n - > r . max_x < b - > x + b - > radius )
n - > r . max_x = b - > x + b - > radius ;
if ( n - > r . max_y < b - > y + b - > radius )
n - > r . max_y = b - > y + b - > radius ;
}
static void c_index_insert ( struct bt_node * t , struct bt_node * n , struct ball * b ) {
double w = width ;
double h = height ;
double ref_x = 0.0 ;
double ref_y = 0.0 ;
2019-08-20 17:23:54 +02:00
c_index_init_node ( n , b ) ;
2019-08-20 16:15:29 +02:00
for ( ; ; ) {
c_index_add_ball ( t , b ) ;
if ( w > h ) { /* horizontal split */
if ( b - > x < = t - > ball - > x ) {
if ( t - > left ) {
w = t - > ball - > x - ref_x ;
t = t - > left ;
} else {
2019-08-20 17:23:54 +02:00
t - > left = n ;
2019-08-20 16:15:29 +02:00
return ;
}
} else {
if ( t - > right ) {
w - = t - > ball - > x - ref_x ;
ref_x = t - > ball - > x ;
t = t - > right ;
} else {
2019-08-20 17:23:54 +02:00
t - > right = n ;
2019-08-20 16:15:29 +02:00
return ;
}
}
} else { /* vertical split */
if ( b - > y < = t - > ball - > y ) {
if ( t - > left ) {
h = t - > ball - > y - ref_y ;
t = t - > left ;
} else {
2019-08-20 17:23:54 +02:00
t - > left = n ;
2019-08-20 16:15:29 +02:00
return ;
}
} else {
if ( t - > right ) {
h - = t - > ball - > y - ref_y ;
ref_y = t - > ball - > y ;
t = t - > right ;
} else {
2019-08-20 17:23:54 +02:00
t - > right = n ;
2019-08-20 16:15:29 +02:00
return ;
}
}
}
}
}
void c_index_build ( ) {
c_index_init_node ( c_index , balls ) ;
2019-08-20 17:23:54 +02:00
for ( int i = 1 ; i < n_balls ; + + i )
2019-08-20 16:15:29 +02:00
c_index_insert ( c_index , c_index + i , balls + i ) ;
}
struct bt_node * * c_index_stack = 0 ;
unsigned int c_index_stack_top = 0 ;
2019-08-21 10:26:58 +02:00
static void c_index_stack_clear ( ) {
2019-08-20 16:15:29 +02:00
c_index_stack_top = 0 ;
}
static void c_index_stack_push ( struct bt_node * n ) {
c_index_stack [ c_index_stack_top + + ] = n ;
}
static struct bt_node * c_index_stack_pop ( ) {
if ( c_index_stack_top > 0 )
return c_index_stack [ - - c_index_stack_top ] ;
else
return 0 ;
}
static int c_index_ball_in_rectangle ( const struct bt_node * n , const struct ball * b ) {
return n - > r . min_x < = b - > x + b - > radius
& & n - > r . max_x > = b - > x - b - > radius
& & n - > r . min_y < = b - > y + b - > radius
& & n - > r . max_y > = b - > y - b - > radius ;
}
static int c_index_must_check ( const struct bt_node * n , const struct ball * b ) {
return n ! = 0 & & n - > ball < b & & c_index_ball_in_rectangle ( n , b ) ;
}
void c_index_check_collisions ( ) {
for ( struct ball * b = balls + 1 ; b < balls + n_balls ; + + b ) {
c_index_stack_clear ( ) ;
struct bt_node * n = c_index ;
do {
ball_collision ( n - > ball , b ) ;
if ( c_index_must_check ( n - > left , b ) ) {
2019-08-21 10:26:58 +02:00
if ( c_index_must_check ( n - > right , b ) )
2019-08-20 16:15:29 +02:00
c_index_stack_push ( n - > right ) ;
n = n - > left ;
} else if ( c_index_must_check ( n - > right , b ) ) {
n = n - > right ;
} else {
n = c_index_stack_pop ( ) ;
}
} while ( n ) ;
}
}
int c_index_init ( ) {
if ( ! c_index )
c_index = malloc ( sizeof ( struct bt_node ) * n_balls ) ;
if ( ! c_index )
return 0 ;
if ( ! c_index_stack )
c_index_stack = malloc ( sizeof ( struct bt_node * ) * n_balls ) ;
if ( ! c_index_stack )
return 0 ;
return 1 ;
}
void c_index_destroy ( ) {
if ( c_index )
free ( c_index ) ;
if ( c_index_stack )
free ( c_index_stack ) ;
c_index = 0 ;
c_index_stack = 0 ;
}
/* Trivial collision check
*/
void check_collisions_simple ( ) {
2019-08-19 10:45:55 +02:00
for ( int i = 0 ; i < n_balls ; + + i )
for ( int j = i + 1 ; j < n_balls ; + + j )
ball_collision ( balls + i , balls + j ) ;
}
2019-08-20 16:15:29 +02:00
void check_collisions_with_index ( ) {
c_index_build ( ) ;
c_index_check_collisions ( ) ;
}
void ( * check_collisions ) ( ) = 0 ;
void update_state ( ) {
if ( check_collisions )
2019-08-19 10:45:55 +02:00
check_collisions ( ) ;
movement_and_borders ( ) ;
}
2019-07-29 19:25:45 +02:00
/* Graphics System
*/
2019-08-20 16:15:29 +02:00
GtkWidget * window ;
2019-12-05 16:34:31 +01:00
GtkWidget * canvas ;
2019-07-05 16:47:29 +02:00
2019-08-20 16:15:29 +02:00
int gravity_vector_countdown = 0 ;
int gravity_vector_init = 300 ;
2019-07-29 15:49:42 +02:00
2019-12-05 16:34:31 +01:00
void draw_gravity_vector ( cairo_t * cr ) {
2019-07-29 15:49:42 +02:00
if ( gravity_vector_countdown ! = 0 ) {
cairo_new_path ( cr ) ;
cairo_move_to ( cr , width / 2 , height / 2 ) ;
cairo_line_to ( cr , width / 2 + g_x , height / 2 + g_y ) ;
cairo_set_source_rgb ( cr , 1.0 , 1.0 , 1.0 ) ;
cairo_set_line_width ( cr , 1.0 ) ;
cairo_stroke ( cr ) ;
cairo_arc ( cr , width / 2 + g_x , height / 2 + g_y , 3 , 0 , 2 * M_PI ) ;
cairo_fill ( cr ) ;
if ( gravity_vector_countdown > 0 )
- - gravity_vector_countdown ;
}
2019-07-28 15:03:27 +02:00
}
2019-07-29 22:45:32 +02:00
const char * face_filename = 0 ;
2021-01-03 13:05:23 +01:00
int face_rotation = 0 ;
2019-07-29 22:45:32 +02:00
2019-12-18 21:18:21 +01:00
static const double linear_rotation_unit = 2.0 ;
2019-07-29 22:45:32 +02:00
2021-01-03 13:05:23 +01:00
void init_ball_face ( struct ball * b , cairo_surface_t * face , int rotation ) {
if ( face & & rotation ) {
2019-12-18 21:18:21 +01:00
b - > rotation_steps = 2 * M_PI * b - > radius / linear_rotation_unit ;
} else {
b - > rotation_steps = 1 ;
}
b - > faces = malloc ( sizeof ( cairo_surface_t * ) * b - > rotation_steps ) ;
assert ( b - > faces ) ;
for ( int i = 0 ; i < b - > rotation_steps ; + + i ) {
b - > faces [ i ] = gdk_window_create_similar_surface ( window - > window ,
CAIRO_CONTENT_COLOR_ALPHA ,
2 * b - > radius , 2 * b - > radius ) ;
assert ( b - > faces [ i ] ) ;
cairo_t * ball_cr = cairo_create ( b - > faces [ i ] ) ;
cairo_translate ( ball_cr , b - > radius , b - > radius ) ;
cairo_arc ( ball_cr , 0.0 , 0.0 , b - > radius , 0 , 2 * M_PI ) ;
2019-07-29 22:45:32 +02:00
cairo_clip ( ball_cr ) ;
2019-07-29 23:04:26 +02:00
2019-12-18 21:18:21 +01:00
if ( face ) {
int face_x_offset = cairo_image_surface_get_width ( face ) / 2 ;
int face_y_offset = cairo_image_surface_get_height ( face ) / 2 ;
cairo_rotate ( ball_cr , i * 2 * M_PI / b - > rotation_steps ) ;
cairo_scale ( ball_cr , 1.0 * b - > radius / face_x_offset , 1.0 * b - > radius / face_y_offset ) ;
cairo_set_source_surface ( ball_cr , face , - face_x_offset , - face_y_offset ) ;
2019-07-29 22:45:32 +02:00
cairo_paint ( ball_cr ) ;
2021-01-09 20:33:40 +01:00
} else {
cairo_set_source_rgb ( ball_cr , 1.0 * ( rand ( ) % 256 ) / 255 , 1.0 * ( rand ( ) % 256 ) / 255 , 1.0 * ( rand ( ) % 256 ) / 255 ) ;
cairo_paint ( ball_cr ) ;
2019-07-29 22:45:32 +02:00
}
2019-12-18 21:18:21 +01:00
cairo_surface_flush ( b - > faces [ i ] ) ;
2019-07-29 22:45:32 +02:00
cairo_destroy ( ball_cr ) ;
}
2019-12-18 21:18:21 +01:00
}
void init_graphics ( ) {
cairo_surface_t * face_surface = 0 ;
if ( face_filename ) {
face_surface = cairo_image_surface_create_from_png ( face_filename ) ;
if ( cairo_surface_status ( face_surface ) ! = CAIRO_STATUS_SUCCESS ) {
cairo_surface_destroy ( face_surface ) ;
face_surface = 0 ;
fprintf ( stderr , " could not create surface from PNG file %s \n " , face_filename ) ;
}
}
for ( int i = 0 ; i < n_balls ; + + i )
2021-01-03 13:05:23 +01:00
init_ball_face ( & ( balls [ i ] ) , face_surface , face_rotation ) ;
2019-12-18 21:18:21 +01:00
2019-07-29 22:45:32 +02:00
if ( face_surface )
cairo_surface_destroy ( face_surface ) ;
2019-07-29 19:25:45 +02:00
}
2019-08-20 16:15:29 +02:00
void destroy_graphics ( ) {
2019-12-18 21:18:21 +01:00
for ( int i = 0 ; i < n_balls ; + + i ) {
for ( int j = 0 ; j < balls [ i ] . rotation_steps ; + + i )
cairo_surface_destroy ( balls [ i ] . faces [ j ] ) ;
free ( balls [ i ] . faces ) ;
}
2019-07-29 19:25:45 +02:00
}
2019-06-03 10:09:00 +02:00
2019-08-20 16:15:29 +02:00
void draw_balls_onto_window ( ) {
2019-06-03 10:09:00 +02:00
/* clear pixmap */
2019-12-05 16:34:31 +01:00
cairo_t * cr = gdk_cairo_create ( canvas - > window ) ;
2019-07-29 15:48:15 +02:00
cairo_set_source_rgba ( cr , 0.0 , 0.0 , 0.0 , clear_alpha ) ;
2019-07-28 14:55:39 +02:00
cairo_paint ( cr ) ;
2019-06-03 10:09:00 +02:00
2019-12-05 16:34:31 +01:00
draw_gravity_vector ( cr ) ;
2019-07-28 15:03:27 +02:00
2019-06-04 10:13:02 +02:00
/* draw balls */
2019-06-03 10:09:00 +02:00
for ( int i = 0 ; i < n_balls ; + + i ) {
2019-07-29 22:45:32 +02:00
cairo_save ( cr ) ;
cairo_translate ( cr , balls [ i ] . x - balls [ i ] . radius , balls [ i ] . y - balls [ i ] . radius ) ;
2019-12-18 21:18:21 +01:00
unsigned int face_id ;
if ( balls [ i ] . rotation_steps = = 1 )
face_id = 0 ;
else {
face_id = balls [ i ] . rotation_steps * balls [ i ] . angle / M_PI ;
if ( face_id > = balls [ i ] . rotation_steps )
face_id % = balls [ i ] . rotation_steps ;
}
cairo_set_source_surface ( cr , balls [ i ] . faces [ face_id ] , 0 , 0 ) ;
2019-07-29 22:45:32 +02:00
cairo_paint ( cr ) ;
cairo_restore ( cr ) ;
2019-06-03 10:09:00 +02:00
}
2019-12-05 16:34:31 +01:00
cairo_destroy ( cr ) ;
2019-06-03 10:09:00 +02:00
}
2019-12-05 16:34:31 +01:00
gint configure_event ( GtkWidget * widget , GdkEventConfigure * event ) {
2019-06-03 10:09:00 +02:00
if ( width = = widget - > allocation . width & & height = = widget - > allocation . height )
return FALSE ;
2019-07-28 14:55:39 +02:00
width = widget - > allocation . width ;
height = widget - > allocation . height ;
2019-06-03 10:09:00 +02:00
return TRUE ;
}
2019-08-20 16:15:29 +02:00
gint keyboard_input ( GtkWidget * widget , GdkEventKey * event ) {
2019-06-03 10:09:00 +02:00
if ( event - > type ! = GDK_KEY_PRESS )
return FALSE ;
switch ( event - > keyval ) {
2019-07-28 15:03:27 +02:00
case GDK_KEY_Up :
g_y - = 10 ;
2019-07-29 15:49:42 +02:00
gravity_vector_countdown = gravity_vector_init ;
2019-07-28 15:03:27 +02:00
break ;
case GDK_KEY_Down :
g_y + = 10 ;
2019-07-29 15:49:42 +02:00
gravity_vector_countdown = gravity_vector_init ;
2019-07-28 15:03:27 +02:00
break ;
case GDK_KEY_Left :
g_x - = 10 ;
2019-07-29 15:49:42 +02:00
gravity_vector_countdown = gravity_vector_init ;
2019-07-28 15:03:27 +02:00
break ;
case GDK_KEY_Right :
g_x + = 10 ;
2019-07-29 15:49:42 +02:00
gravity_vector_countdown = gravity_vector_init ;
2019-07-28 15:03:27 +02:00
break ;
2019-07-29 15:49:42 +02:00
case GDK_KEY_G :
case GDK_KEY_g :
gravity_vector_countdown = gravity_vector_init ;
2019-07-28 15:03:27 +02:00
break ;
2019-06-03 10:09:00 +02:00
case GDK_KEY_Q :
case GDK_KEY_q :
gtk_main_quit ( ) ;
break ;
default :
return FALSE ;
}
return TRUE ;
}
2019-08-20 16:15:29 +02:00
gboolean expose_event ( GtkWidget * widget , GdkEventExpose * event , gpointer data ) {
2019-12-05 16:34:31 +01:00
draw_balls_onto_window ( ) ;
2019-06-03 10:09:00 +02:00
return TRUE ;
}
2019-08-20 16:15:29 +02:00
void destroy_window ( void ) {
2019-06-03 10:09:00 +02:00
gtk_main_quit ( ) ;
}
2019-06-04 10:18:50 +02:00
void print_usage ( const char * progname ) {
2019-06-03 10:09:00 +02:00
fprintf ( stderr ,
2019-07-28 14:55:39 +02:00
" usage: %s [options...] \n "
" options: \n "
" \t <width>x<height> \n "
2019-12-01 22:06:31 +01:00
" \t n=<number of balls> \n "
2019-07-28 14:55:39 +02:00
" \t fx=<x-force> \n "
" \t fy=<y-force> \n "
" \t radius=<min-radius>-<max-radius> \n "
" \t v=<min-velocity>-<max-velocity> \n "
2019-07-29 15:48:15 +02:00
" \t delta=<frame-delta-time> \n "
2019-08-10 11:13:26 +02:00
" \t face=<filename> \n "
2019-07-29 22:57:21 +02:00
" \t clear=<clear-alpha> \n "
2019-08-21 12:14:25 +02:00
" \t stats=<sample-count> :: rendering timing statitstics (0=disabled, default) \n "
2021-01-03 13:05:23 +01:00
" \t collisions=<C> :: n=no collisions, s=simple, i=index \n "
" \t -r :: activate face rotation \n " ,
2019-06-03 10:09:00 +02:00
progname ) ;
}
2019-08-21 12:14:25 +02:00
unsigned int stats_sampling = 0 ;
2019-07-29 22:57:21 +02:00
2019-06-04 10:18:50 +02:00
gboolean timeout ( gpointer user_data ) {
2019-07-29 22:57:21 +02:00
guint64 start = 0 , elapsed_usec ;
2019-08-21 12:14:25 +02:00
if ( stats_sampling > 0 )
2019-07-29 22:57:21 +02:00
start = g_get_monotonic_time ( ) ;
2019-06-03 10:09:00 +02:00
update_state ( ) ;
2019-07-28 14:55:39 +02:00
draw_balls_onto_window ( ) ;
2019-06-03 10:09:00 +02:00
2019-08-21 12:14:25 +02:00
if ( stats_sampling > 0 ) {
2019-07-29 22:57:21 +02:00
elapsed_usec = g_get_monotonic_time ( ) - start ;
2019-06-03 10:09:00 +02:00
2019-07-29 22:57:21 +02:00
static guint64 elapsed_usec_total = 0 ;
static unsigned int samples = 0 ;
2019-08-21 12:14:25 +02:00
if ( samples = = stats_sampling ) {
printf ( " \r frame rendering: time = %lu usec, max freq = %.2f (avg over %u samples) " , elapsed_usec_total / samples , ( 1000000.0 * samples ) / elapsed_usec_total , samples ) ;
2019-07-29 22:57:21 +02:00
fflush ( stdout ) ;
samples = 0 ;
elapsed_usec_total = 0 ;
}
+ + samples ;
elapsed_usec_total + = elapsed_usec ;
2019-06-03 10:09:00 +02:00
}
return TRUE ;
}
2019-06-04 10:18:50 +02:00
int main ( int argc , const char * argv [ ] ) {
2019-06-03 10:09:00 +02:00
int w = DEFAULT_WIDTH ;
int h = DEFAULT_HEIGHT ;
2019-06-04 10:18:50 +02:00
for ( int i = 1 ; i < argc ; + + i ) {
2019-06-03 10:09:00 +02:00
if ( sscanf ( argv [ i ] , " %dx%d " , & w , & h ) = = 2 )
continue ;
if ( sscanf ( argv [ i ] , " n=%u " , & n_balls ) = = 1 )
continue ;
2019-06-03 22:24:06 +02:00
if ( sscanf ( argv [ i ] , " fx=%lf " , & g_x ) = = 1 )
2019-06-03 10:09:00 +02:00
continue ;
2019-06-03 22:24:06 +02:00
if ( sscanf ( argv [ i ] , " fy=%lf " , & g_y ) = = 1 )
2019-06-03 10:09:00 +02:00
continue ;
2019-06-04 10:13:02 +02:00
if ( sscanf ( argv [ i ] , " radius=%u-%u " , & radius_min , & radius_max ) = = 2 )
continue ;
if ( sscanf ( argv [ i ] , " v=%u-%u " , & v_min , & v_max ) = = 2 )
2019-06-03 10:09:00 +02:00
continue ;
2019-06-03 22:24:06 +02:00
if ( sscanf ( argv [ i ] , " delta=%lf " , & delta ) = = 1 )
2019-06-03 10:09:00 +02:00
continue ;
2019-07-29 14:21:55 +02:00
if ( strncmp ( argv [ i ] , " face= " , 5 ) = = 0 ) {
2019-07-29 22:45:32 +02:00
face_filename = argv [ i ] + 5 ;
2019-07-29 14:21:55 +02:00
continue ;
}
2019-07-29 15:48:15 +02:00
if ( sscanf ( argv [ i ] , " clear=%lf " , & clear_alpha ) = = 1 )
continue ;
2019-08-21 12:14:25 +02:00
if ( sscanf ( argv [ i ] , " stats=%u " , & stats_sampling ) = = 1 )
2019-07-29 22:57:21 +02:00
continue ;
2019-08-20 16:15:29 +02:00
char collisions ;
if ( sscanf ( argv [ i ] , " collisions=%c " , & collisions ) = = 1 ) {
switch ( collisions ) {
case ' i ' :
case ' I ' :
check_collisions = check_collisions_with_index ;
continue ;
case ' 0 ' :
case ' N ' :
case ' n ' :
check_collisions = 0 ;
continue ;
case ' s ' :
case ' S ' :
check_collisions = check_collisions_simple ;
continue ;
}
2019-08-10 11:11:14 +02:00
}
2021-01-03 13:05:23 +01:00
if ( strcmp ( argv [ i ] , " -r " ) = = 0 ) {
face_rotation = 1 ;
continue ;
}
2019-06-03 10:09:00 +02:00
print_usage ( argv [ 0 ] ) ;
return 1 ;
}
balls = malloc ( sizeof ( struct ball ) * n_balls ) ;
assert ( balls ) ;
2019-08-20 16:15:29 +02:00
assert ( c_index_init ( ) ) ;
2019-06-03 10:09:00 +02:00
balls_init_state ( ) ;
gtk_init ( 0 , 0 ) ;
2019-12-05 16:34:31 +01:00
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL ) ;
gtk_window_set_default_size ( GTK_WINDOW ( window ) , width , height ) ;
2019-07-05 16:47:29 +02:00
gtk_window_set_position ( GTK_WINDOW ( window ) , GTK_WIN_POS_CENTER ) ;
2019-12-05 16:34:31 +01:00
gtk_window_set_title ( GTK_WINDOW ( window ) , " Game " ) ;
2019-07-05 16:47:29 +02:00
g_signal_connect ( window , " destroy " , G_CALLBACK ( destroy_window ) , NULL ) ;
2019-12-05 16:34:31 +01:00
g_signal_connect ( G_OBJECT ( window ) , " delete-event " , G_CALLBACK ( destroy_window ) , NULL ) ;
2019-07-29 19:25:45 +02:00
g_signal_connect ( window , " key-press-event " , G_CALLBACK ( keyboard_input ) , NULL ) ;
2019-06-03 10:09:00 +02:00
gtk_widget_set_events ( window , GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK ) ;
2019-12-05 16:34:31 +01:00
canvas = gtk_drawing_area_new ( ) ;
g_signal_connect ( G_OBJECT ( canvas ) , " expose-event " , G_CALLBACK ( expose_event ) , NULL ) ;
g_signal_connect ( G_OBJECT ( canvas ) , " configure-event " , G_CALLBACK ( configure_event ) , NULL ) ;
gtk_container_add ( GTK_CONTAINER ( window ) , canvas ) ;
g_timeout_add ( delta * 1000 , timeout , canvas ) ;
2019-06-03 10:09:00 +02:00
gtk_widget_show_all ( window ) ;
2019-12-05 16:34:31 +01:00
init_graphics ( ) ;
2019-06-03 10:09:00 +02:00
gtk_main ( ) ;
2019-08-21 12:14:25 +02:00
if ( stats_sampling > 0 )
2019-07-29 22:57:21 +02:00
printf ( " \n " ) ;
2019-07-29 22:45:32 +02:00
destroy_graphics ( ) ;
2019-08-20 16:15:29 +02:00
c_index_destroy ( ) ;
2019-06-03 10:09:00 +02:00
free ( balls ) ;
2019-07-05 16:47:29 +02:00
2019-06-03 10:09:00 +02:00
return 0 ;
}