diff --git a/coresdk/src/coresdk/collisions.cpp b/coresdk/src/coresdk/collisions.cpp index e3969291..110503a7 100644 --- a/coresdk/src/coresdk/collisions.cpp +++ b/coresdk/src/coresdk/collisions.cpp @@ -17,6 +17,8 @@ #include "graphics.h" #include "utils.h" +constexpr double RAY_QUAD_LINE_THICKNESS = 1.0; + using std::function; namespace splashkit_lib @@ -274,6 +276,65 @@ namespace splashkit_lib return bitmap_circle_collision(bmp, cell, translation_matrix(x, y), circ); } + bool bitmap_quad_collision(bitmap bmp, int cell, const matrix_2d &translation, const quad& q) + { + if (INVALID_PTR(bmp, BITMAP_PTR)) + { + return false; + } + + quad q1 = quad_from(bitmap_cell_rectangle(bmp), translation); + rectangle rect = rectangle_around(q); + + if ( not quads_intersect(q1, q) ) return false; + + return _step_through_pixels(rect.width, rect.height, translation_matrix(rect.x, rect.y), bmp->cell_w, bmp->cell_h, translation, [&] (int ax, int ay, int bx, int by) + { + return pixel_drawn_at_point(bmp, cell, bx, by) && point_in_quad(point_at(rect.x + ax, rect.y + ay), q); + }); + } + + bool bitmap_ray_collision(bitmap bmp, int cell, const matrix_2d& translation, const point_2d& origin, const vector_2d& heading) + { + if (INVALID_PTR(bmp, BITMAP_PTR)) + { + return false; + } + + point_2d bmp_position = matrix_multiply(translation, point_at(0.0, 0.0)); + point_2d bmp_center = point_offset_by(bmp_position, vector_to(bitmap_center(bmp))); + circle bmp_bounding_circle = bitmap_bounding_circle(bmp, bmp_center); + + vector_2d unit_ray_heading = unit_vector(heading); + + // get point which will allow segment to fully pass through the bitmap + double distance_to_center = vector_magnitude(vector_point_to_point(origin, bmp_center)); + point_2d ray_end = point_offset_by(origin, vector_multiply(unit_ray_heading, distance_to_center + bmp_bounding_circle.radius)); + + quad ray_quad = quad_from(origin, ray_end, RAY_QUAD_LINE_THICKNESS); + return bitmap_quad_collision(bmp, cell, translation, ray_quad); + } + + bool bitmap_ray_collision(bitmap bmp, int cell, const point_2d& pt, const point_2d& origin, const vector_2d& heading) + { + return bitmap_ray_collision(bmp, cell, translation_matrix(pt), origin, heading); + } + + bool bitmap_ray_collision(bitmap bmp, int cell, double x, double y, const point_2d& origin, const vector_2d& heading) + { + return bitmap_ray_collision(bmp, cell, translation_matrix(x, y), origin, heading); + } + + bool bitmap_ray_collision(bitmap bmp, double x, double y, const point_2d& origin, const vector_2d& heading) + { + return bitmap_ray_collision(bmp, 0, translation_matrix(x, y), origin, heading); + } + + bool bitmap_ray_collision(bitmap bmp, const point_2d& pt, const point_2d& origin, const vector_2d& heading) + { + return bitmap_ray_collision(bmp, 0, translation_matrix(pt), origin, heading); + } + bool sprite_bitmap_collision(sprite s, bitmap bmp, int cell, double x, double y) { if (!rectangles_intersect(sprite_collision_rectangle(s), bitmap_cell_rectangle(bmp, point_at(x, y)))) @@ -329,6 +390,11 @@ namespace splashkit_lib return bitmap_rectangle_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), rect); } + + bool sprite_ray_collision(sprite s, const point_2d& origin, const vector_2d& heading) + { + return bitmap_ray_collision(sprite_collision_bitmap(s), sprite_current_cell(s), sprite_location_matrix(s), origin, heading); + } bool sprite_collision(sprite s1, sprite s2) { diff --git a/coresdk/src/coresdk/collisions.h b/coresdk/src/coresdk/collisions.h index 2c002c84..8a30b05d 100644 --- a/coresdk/src/coresdk/collisions.h +++ b/coresdk/src/coresdk/collisions.h @@ -296,7 +296,116 @@ namespace splashkit_lib * @attribute method circle_collision */ bool bitmap_circle_collision(bitmap bmp, const point_2d& pt, const circle& circ); + + /** + * Tests if a bitmap cell drawn using a passed in translation, will + * intersect with a quad. You can use this to detect collisions between + * bitmaps and quads. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param translation The matrix used to transfrom the bitmap when drawing + * @param q The quad to test + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with `q` when drawn. + * + * @attribute suffix for_cell_with_translation + * + * @attribute class bitmap + * @attribute method quad_collision + */ + bool bitmap_quad_collision(bitmap bmp, int cell, const matrix_2d &translation, const quad &q); + /** + * Tests if a bitmap cell drawn using a passed in translation, will + * intersect with a ray. You can use this to detect collisions between + * bitmaps and rays. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param translation The matrix used to transfrom the bitmap when drawing + * @param origin The origin of the ray + * @param heading The heading of the ray + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with the ray when drawn. + * + * @attribute suffix for_cell_with_translation + * + * @attribute class bitmap + * @attribute method ray_collision + */ + bool bitmap_ray_collision(bitmap bmp, int cell, const matrix_2d& translation, const point_2d& origin, const vector_2d& heading); + + /** + * Tests if a bitmap cell drawn at `pt` would intersect with a ray. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param pt The location where the bitmap is drawn + * @param origin The origin of the ray + * @param heading The heading of the ray + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with the ray when drawn. + * + * @attribute suffix for_cell_at_point + * + * @attribute class bitmap + * @attribute method ray_collision + */ + bool bitmap_ray_collision(bitmap bmp, int cell, const point_2d& pt, const point_2d& origin, const vector_2d& heading); + + /** + * Tests if a bitmap cell drawn at `x`, `y` would intersect with a ray. + * + * @param bmp The bitmap to test + * @param cell The cell of the bitmap to check + * @param x The x location where the bitmap is drawn + * @param y The y location where the bitmap is drawn + * @param origin The origin of the ray + * @param heading The heading of the ray + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with the ray when drawn. + * + * @attribute suffix for_cell + * + * @attribute class bitmap + * @attribute method ray_collision + */ + bool bitmap_ray_collision(bitmap bmp, int cell, double x, double y, const point_2d& origin, const vector_2d& heading); + + /** + * Tests if a bitmap drawn at `x`, `y` would intersect with a ray. + * + * @param bmp The bitmap to test + * @param x The x location where the bitmap is drawn + * @param y The y location where the bitmap is drawn + * @param origin The origin of the ray + * @param heading The heading of the ray + * @return True if a drawn pixel in the bitmap will + * intersect with the ray when drawn. + * + * @attribute class bitmap + * @attribute method ray_collision + */ + bool bitmap_ray_collision(bitmap bmp, double x, double y, const point_2d& origin, const vector_2d& heading); + + /** + * Tests if a bitmap drawn at `pt` would intersect with a ray. + * + * @param bmp The bitmap to test + * @param pt The location where the bitmap is drawn + * @param origin The origin of the ray + * @param heading The heading of the ray + * @return True if a drawn pixel in the cell of the bitmap will + * intersect with the ray when drawn. + * + * @attribute suffix at_point + * + * @attribute class bitmap + * @attribute method ray_collision + */ + bool bitmap_ray_collision(bitmap bmp, const point_2d& pt, const point_2d& origin, const vector_2d& heading); + /** * Tests if a sprite will collide with a bitmap drawn at the indicated * location. @@ -374,6 +483,19 @@ namespace splashkit_lib */ bool sprite_rectangle_collision(sprite s, const rectangle& rect); + /** + * Tests if a sprite is drawn along a given ray. + * + * @param s The sprite to test + * @param origin The origin of the ray + * @param heading The heading of the ray + * @return True if the sprite is drawn along the ray + * + * @attribute class sprite + * @attribute method ray_collision + */ + bool sprite_ray_collision(sprite s, const point_2d& origin, const vector_2d& heading); + /** * Tests if two given sprites `s1` and `s2` are collided * @param s1 the first `sprite` to test diff --git a/coresdk/src/coresdk/images.cpp b/coresdk/src/coresdk/images.cpp index d5291caf..239e6c82 100644 --- a/coresdk/src/coresdk/images.cpp +++ b/coresdk/src/coresdk/images.cpp @@ -388,7 +388,7 @@ namespace splashkit_lib return circle_at(0, 0, 0); } - return circle_at(pt, MAX(bmp->cell_w, bmp->cell_h) / 2.0f * scale); + return circle_at(pt, sqrt(pow(bmp->cell_w / 2.0, 2.0) + pow(bmp->cell_h / 2.0, 2.0)) * scale); } circle bitmap_cell_circle(bitmap bmp, const point_2d pt) @@ -408,7 +408,7 @@ namespace splashkit_lib return circle_at(0,0,0); } - return circle_at(pt, MAX(bmp->image.surface.width, bmp->image.surface.height)); + return circle_at(pt, sqrt(pow(bmp->image.surface.width / 2.0, 2.0) + pow(bmp->image.surface.height / 2.0, 2.0))); } int bitmap_cell_columns(bitmap bmp) diff --git a/coresdk/src/coresdk/quad_geometry.cpp b/coresdk/src/coresdk/quad_geometry.cpp index d6bdb9d6..c1edd5bb 100644 --- a/coresdk/src/coresdk/quad_geometry.cpp +++ b/coresdk/src/coresdk/quad_geometry.cpp @@ -59,6 +59,21 @@ namespace splashkit_lib return result; } + quad quad_from(const point_2d& line_origin, const point_2d& line_end, double width) + { + vector_2d heading = vector_point_to_point(line_origin, line_end); + vector_2d normal = vector_normal(heading); + vector_2d offset = vector_multiply(normal, width / 2.0); + + quad result; + result.points[0] = point_offset_by(line_origin, offset); + result.points[1] = point_offset_by(line_end, offset); + result.points[2] = point_offset_by(line_origin, vector_invert(offset)); + result.points[3] = point_offset_by(line_end, vector_invert(offset)); + + return result; + } + void set_quad_point(quad &q, int idx, const point_2d &value) { if (idx < 0 || idx > 3) diff --git a/coresdk/src/coresdk/quad_geometry.h b/coresdk/src/coresdk/quad_geometry.h index dd3fcc84..8330c1eb 100644 --- a/coresdk/src/coresdk/quad_geometry.h +++ b/coresdk/src/coresdk/quad_geometry.h @@ -73,6 +73,20 @@ namespace splashkit_lib const point_2d &p3, const point_2d &p4); + /** + * Returns a quad from the passed in line and width. + * The quad will be a rectangle with the line as the diagonal, + * and the width as the width of the rectangle. + * + * @param line_origin The origin of the line + * @param line_end The end of the line + * @param width The width of the quad + * @return A quad that represents the line with the given width + * + * @attribute suffix from_line + */ + quad quad_from(const point_2d& line_origin, const point_2d& line_end, double width); + /** * Change a point in a quad. * diff --git a/coresdk/src/test/test_geometry.cpp b/coresdk/src/test/test_geometry.cpp index 035bf675..7f7e34ee 100644 --- a/coresdk/src/test/test_geometry.cpp +++ b/coresdk/src/test/test_geometry.cpp @@ -239,10 +239,77 @@ void test_triangle() close_window(w1); } +void test_bitmap_ray_collision() +{ + window w1 = open_window("Bitmap Ray Collision", 800, 600); + bitmap bmp_1 = load_bitmap("on_med", "on_med.png"); + point_2d bmp_1_position = point_at(300.0, 300.0); + point_2d bmp_1_center = point_offset_by(bmp_1_position, vector_to(bitmap_center(bmp_1))); + bitmap bmp_2 = load_bitmap("rocket_sprt", "rocket_sprt.png"); + point_2d bmp_2_position = point_at(500.0, 300.0); + point_2d bmp_2_center = point_offset_by(bmp_2_position, vector_to(bitmap_center(bmp_2))); + bitmap bmp_3 = load_bitmap("up_pole", "up_pole.png"); + point_2d bmp_3_position = point_at(700.0, 300.0); + point_2d bmp_3_center = point_offset_by(bmp_3_position, vector_to(bitmap_center(bmp_3))); + point_2d ray_origin = point_at(100, 100); + vector_2d ray_heading = vector_to(200, 200); + + while ( !window_close_requested(w1) ) { + process_events(); + + clear_screen(COLOR_WHITE); + + if (key_down(UP_KEY)) + ray_origin.y -= 1.0; + if (key_down(DOWN_KEY)) + ray_origin.y += 1.0; + if (key_down(LEFT_KEY)) + ray_origin.x -= 1.0; + if (key_down(RIGHT_KEY)) + ray_origin.x += 1.0; + + bool collision_1 = bitmap_ray_collision(bmp_1, 0, bmp_1_position, ray_origin, ray_heading); + bool collision_2 = bitmap_ray_collision(bmp_2, 0, bmp_2_position, ray_origin, ray_heading); + bool collision_3 = bitmap_ray_collision(bmp_3, 0, bmp_3_position, ray_origin, ray_heading); + + draw_bitmap(bmp_1, bmp_1_position.x, bmp_1_position.y); + if (collision_1) + { + fill_circle(COLOR_RED, circle_at(bmp_1_center, 30.0)); + } + + draw_bitmap(bmp_2, bmp_2_position.x, bmp_2_position.y); + if (collision_2) + { + fill_circle(COLOR_RED, circle_at(bmp_2_center, 8.0)); + } + + draw_bitmap(bmp_3, bmp_3_position.x, bmp_3_position.y); + if (collision_3) + { + fill_circle(COLOR_RED, circle_at(bmp_3_center, 30.0)); + } + + ray_heading = vector_point_to_point(ray_origin, mouse_position()); + vector_2d normal_heading = unit_vector(ray_heading); + draw_line(COLOR_BLACK, ray_origin, point_offset_by(ray_origin, vector_multiply(normal_heading, 800.0))); + + circle mouse_circle = circle_at(mouse_position(), 3.0); + draw_circle(COLOR_GREEN, mouse_circle); + + circle ray_origin_circle = circle_at(ray_origin, 3.0); + draw_circle(COLOR_BLUE, ray_origin_circle); + + refresh_screen(); + } + close_window(w1); +} + void run_geometry_test() { test_rectangle(); test_points(); test_lines(); test_triangle(); + test_bitmap_ray_collision(); } \ No newline at end of file diff --git a/coresdk/src/test/test_sprites.cpp b/coresdk/src/test/test_sprites.cpp index 3034d8af..6b5e1c96 100644 --- a/coresdk/src/test/test_sprites.cpp +++ b/coresdk/src/test/test_sprites.cpp @@ -16,7 +16,7 @@ using namespace splashkit_lib; -void run_sprite_test() +void sprite_test() { sprite sprt, s2; triangle tri, init_tri; @@ -143,3 +143,74 @@ void run_sprite_test() close_all_windows(); } + +void test_sprite_ray_collision() +{ + window w1 = open_window("Sprite Ray Collision", 800, 600); + sprite s1 = create_sprite("on_med.png"); + sprite_set_position(s1, point_at(200.0, 200.0)); + sprite_set_rotation(s1, 45.0f); + sprite s2 = create_sprite("rocket_sprt.png"); + sprite_set_position(s2, point_at(500.0, 100.0)); + sprite s3 = create_sprite("up_pole.png"); + sprite_set_position(s3, point_at(600.0, 300.0)); + sprite_set_rotation(s3, 10.0f); + point_2d ray_origin = point_at(100, 100); + vector_2d ray_heading = vector_to(200, 200); + + while ( !window_close_requested(w1) ) { + process_events(); + + clear_screen(COLOR_WHITE); + + if (key_down(UP_KEY)) + ray_origin.y -= 1.0; + if (key_down(DOWN_KEY)) + ray_origin.y += 1.0; + if (key_down(LEFT_KEY)) + ray_origin.x -= 1.0; + if (key_down(RIGHT_KEY)) + ray_origin.x += 1.0; + + bool collision_1 = sprite_ray_collision(s1, ray_origin, ray_heading); + bool collision_2 = sprite_ray_collision(s2, ray_origin, ray_heading); + bool collision_3 = sprite_ray_collision(s3, ray_origin, ray_heading); + + draw_sprite(s1); + if (collision_1) + { + fill_circle(COLOR_RED, circle_at(sprite_collision_circle(s1).center, 30.0)); + } + + draw_sprite(s2); + if (collision_2) + { + fill_circle(COLOR_RED, circle_at(sprite_collision_circle(s2).center, 8.0)); + } + + draw_sprite(s3); + if (collision_3) + { + fill_circle(COLOR_RED, circle_at(sprite_collision_circle(s3).center, 30.0)); + } + + ray_heading = vector_point_to_point(ray_origin, mouse_position()); + vector_2d normal_heading = unit_vector(ray_heading); + draw_line(COLOR_BLACK, ray_origin, point_offset_by(ray_origin, vector_multiply(normal_heading, 800.0))); + + circle mouse_circle = circle_at(mouse_position(), 3.0); + draw_circle(COLOR_GREEN, mouse_circle); + + circle ray_origin_circle = circle_at(ray_origin, 3.0); + draw_circle(COLOR_BLUE, ray_origin_circle); + + refresh_screen(); + } + close_window(w1); +} + +void run_sprite_test() +{ + sprite_test(); + test_sprite_ray_collision(); +} \ No newline at end of file