line between two vector algorithm impl + tests

This commit is contained in:
wgroeneveld 2018-12-14 19:50:47 +01:00
parent 598c62976b
commit 6fe6187d2c
9 changed files with 221 additions and 34 deletions

View File

@ -4,44 +4,23 @@
#include <libgba-sprite-engine/background/text_stream.h>
#include <libgba-sprite-engine/gba/tonc_bios.h>
#include <cmath>
#include <libgba-sprite-engine/math.h>
#include "bullet.h"
#define FIXED_POINT_BASE 1000
#define SPEED_DELTA 1
void Bullet::setDestination(VECTOR destination) {
auto currentPos = sprite->getPos();
this->dest = destination;
diff.x = (destination.x - currentPos.x);
diff.y = (destination.y - currentPos.y);
directionToCover.x = diff.x * FIXED_POINT_BASE;
directionToCover.y = diff.y * FIXED_POINT_BASE;
TextStream::instance().setText(std::string("covering: ") + std::to_string(directionToCover.x) + std::string(",") + std::to_string(directionToCover.y), 15, 1);
this->coords = Math::bresenhamLineBetween(currentPos, destination);
}
void Bullet::tick() {
auto x = sprite->getX(), y = sprite->getY();
if(directionToCover.x != 0) {
if(directionToCover.x > 0) {
directionToCover.x -= FIXED_POINT_BASE;
x += SPEED_DELTA;
} else {
directionToCover.x += FIXED_POINT_BASE;
x -= SPEED_DELTA;
}
}
if(directionToCover.y != 0) {
if(directionToCover.y > 0) {
directionToCover.y -= FIXED_POINT_BASE;
y += SPEED_DELTA;
} else {
directionToCover.y += FIXED_POINT_BASE;
y -= SPEED_DELTA;
}
if(coords.empty()) {
return;
}
sprite->moveTo(x, y);
sprite->moveTo(coords.front());
coords.pop_front();
}

View File

@ -8,13 +8,13 @@
#include <libgba-sprite-engine/sprites/sprite.h>
#include <libgba-sprite-engine/gba/tonc_math.h>
#include <deque>
class Bullet {
private:
std::unique_ptr<Sprite> sprite;
std::deque<VECTOR> coords;
VECTOR dest;
VECTOR diff;
VECTOR directionToCover;
public:
Bullet(std::unique_ptr<Sprite> sprite) : sprite(std::move(sprite)), dest(VECTOR()) {}

View File

@ -128,5 +128,5 @@ void FoodScene::load() {
bulletCooldown = BULLET_COOLDOWN_START;
// rotation of a point on a circle within the resolution means our radius should be big enough
defaultBulletTarget = { GBA_SCREEN_WIDTH / 2, GBA_SCREEN_HEIGHT + (GBA_SCREEN_WIDTH / 2) - avatar->getCenter().y};
defaultBulletTarget = { GBA_SCREEN_WIDTH / 2, GBA_SCREEN_HEIGHT + (GBA_SCREEN_WIDTH / 2) - avatar->getCenter().y + 40};
}

View File

@ -18,7 +18,7 @@ add_library(${PROJECT_NAME}
src/background/text_stream.cpp
src/background/background.cpp
src/effects/fade_out_scene.cpp
src/sound_control.cpp src/scene.cpp src/timer.cpp)
src/sound_control.cpp src/scene.cpp src/timer.cpp include/libgba-sprite-engine/math.h src/math.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>

View File

@ -0,0 +1,16 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_MATH_H
#define GBA_SPRITE_ENGINE_PROJECT_MATH_H
#include <deque>
#include <libgba-sprite-engine/gba/tonc_math.h>
class Math {
public:
static std::deque<VECTOR> bresenhamLineBetween(VECTOR src, VECTOR dest);
};
#endif //GBA_SPRITE_ENGINE_PROJECT_MATH_H

71
engine/src/math.cpp Normal file
View File

@ -0,0 +1,71 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#include <libgba-sprite-engine/math.h>
std::deque<VECTOR> Math::bresenhamLineBetween(VECTOR src, VECTOR dest) {
// https://www.coranac.com/tonc/text/bitmaps.htm - Bresenham's line algorithm with fixed points
VECTOR step, delta;
std::deque<VECTOR> coords;
if(src.x > dest.x) {
step.x = -1;
delta.x = (src.x - dest.x);
} else {
step.x = +1;
delta.x = (dest.x - src.x);
}
if(src.y > dest.y) {
step.y = -1;
delta.y = (src.y - dest.y);
} else {
step.y = +1;
delta.y = (dest.y - src.y);
}
int dd, x = src.x, y = src.y, ii;
if(delta.y == 0) {
// horizontal
for(ii = 0; ii <= delta.x; ii++) {
coords.push_back({x, y});
x += step.x;
}
} else if(delta.x == 0) {
// vertical
for(ii = 0; ii <= delta.y; ii++) {
coords.push_back({x, y});
y += step.y;
}
} else if(delta.x >= delta.y) {
// Diagonal, slope <= 1
dd = 2 * delta.y - delta.x;
for(ii = 0; ii <= delta.x; ii++) {
coords.push_back({x, y});
if(dd >= 0) {
dd -= 2 * delta.x;
y += step.y;
}
dd += 2 * delta.y;
x += step.x;
}
} else {
// Diagonal, slope > 1
dd = 2 * delta.x - delta.y;
for(ii = 0; ii <= delta.y; ii++) {
coords.push_back({x, y});
if(dd >= 0) {
dd -= 2 * delta.y;
x += step.x;
}
dd += 2 * delta.x;
y += step.y;
}
}
return coords;
}

View File

@ -35,6 +35,7 @@ add_executable(unittest
../engine/src/allocator.cpp
../engine/src/palette/combined_palette.cpp
../engine/src/timer.cpp
timertest.cpp)
../engine/src/math.cpp
timertest.cpp mathtest.cpp)
target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a)

View File

@ -69,7 +69,7 @@ TEST_F(AllocatorSuite, Allocate_Sprite_Pointers_Reservers_Some_Tile_Space) {
auto curr2 = Allocator::getCurrentSpriteIndex();
auto diff2 = curr2 - curr;
cout << "prev: " << prev << " - curr: " << curr << " (diff: " << diff << ")" << " and " << curr2 << endl;
cout << "prev: " << prev << " - curr: " << curr << " (delta: " << diff << ")" << " and " << curr2 << endl;
ASSERT_EQ(prev, allocated.currentAddress);
ASSERT_EQ(curr, allocated2.currentAddress);

120
test/mathtest.cpp Normal file
View File

@ -0,0 +1,120 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#include <gtest/gtest.h>
#include <libgba-sprite-engine/math.h>
class MathSuite : public ::testing::Test {
protected:
virtual void TearDown() {
}
virtual void SetUp() {
}
};
TEST_F(MathSuite, LineBetween_Diagonal_ToTopRightCorner) {
auto points = Math::bresenhamLineBetween({120, 80}, {240, 0});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(121, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(2);
ASSERT_EQ(122, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(10);
ASSERT_EQ(130, pt.x);
ASSERT_EQ(73, pt.y);
pt = points.at(100);
ASSERT_EQ(220, pt.x);
ASSERT_EQ(13, pt.y);
}
TEST_F(MathSuite, LineBetween_Diagonal_ToTopLeftCorner) {
auto points = Math::bresenhamLineBetween({120, 80}, {0, 0});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(119, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(2);
ASSERT_EQ(118, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(10);
ASSERT_EQ(110, pt.x);
ASSERT_EQ(73, pt.y);
pt = points.at(100);
ASSERT_EQ(20, pt.x);
ASSERT_EQ(13, pt.y);
}
TEST_F(MathSuite, LineBetween_Diagonal_ToBottomLeftCorner) {
auto points = Math::bresenhamLineBetween({120, 80}, {0, 160});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(119, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(2);
ASSERT_EQ(118, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(10);
ASSERT_EQ(110, pt.x);
ASSERT_EQ(87, pt.y);
pt = points.at(100);
ASSERT_EQ(20, pt.x);
ASSERT_EQ(147, pt.y);
}
TEST_F(MathSuite, LineBetween_Diagonal_ToBottomRightCorner) {
auto points = Math::bresenhamLineBetween({120, 80}, {240, 160});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(121, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(2);
ASSERT_EQ(122, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(10);
ASSERT_EQ(130, pt.x);
ASSERT_EQ(87, pt.y);
pt = points.at(100);
ASSERT_EQ(220, pt.x);
ASSERT_EQ(147, pt.y);
}
TEST_F(MathSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) {
auto points = Math::bresenhamLineBetween({120, 80}, {0, 80});
ASSERT_EQ(121, points.size());
for(int i = 0; i <= 120; i++) {
auto &vec = points.front();
ASSERT_EQ(80, vec.y);
ASSERT_EQ(120 - i, vec.x);
points.pop_front();
}
}
TEST_F(MathSuite, LineBetween_Vertical_FromCenterToHalfXFullY) {
auto points = Math::bresenhamLineBetween({120, 80}, {120, 160});
ASSERT_EQ(81, points.size());
for(int i = 0; i <= (160 - 80); i++) {
auto &vec = points.front();
ASSERT_EQ(120, vec.x);
ASSERT_EQ(80 + i, vec.y);
points.pop_front();
}
}