From: Daniel Karbach Date: Tue, 14 Jan 2014 06:59:59 +0000 (+0100) Subject: basic gravity calculations X-Git-Url: http://git.localhorst.tv/?p=gworm.git;a=commitdiff_plain;h=587986123da991c9d640d43f26a8c7035cd7cec5 basic gravity calculations with dubious results --- 587986123da991c9d640d43f26a8c7035cd7cec5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c6699d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.swp +*.swp +/build/*/gworm +/build/*/src diff --git a/build/config.mk b/build/config.mk new file mode 100644 index 0000000..d5a8fab --- /dev/null +++ b/build/config.mk @@ -0,0 +1,29 @@ +# commands +CXX = g++ -std=c++11 +RM ?= rm -Rf +MKDIR ?= mkdir -p +RMDIR ?= rmdir -p --ignore-fail-on-non-empty + +# names and pathes +BUILD := $(dir $(lastword $(MAKEFILE_LIST))) +TOP := $(BUILD).. +srcdir = $(TOP)/src + +# flags +CPPFLAGS ?= +CXXFLAGS ?= +LDFLAGS ?= + +CXXFLAGS += -Wall -Werror + +# libraries +SDL_FLAGS = $(shell pkg-config --cflags sdl2) +SDL_LIBS = $(shell pkg-config --libs sdl2) + +CPPUNIT_FLAGS = $(shell pkg-config --cflags cppunit) +CPPUNIT_LIBS = $(shell pkg-config --libs cppunit) + +# set to empty to show tool invocations +VERBOSE = @ + +-include $(BUILD)local-config.mk diff --git a/build/debug/Makefile b/build/debug/Makefile new file mode 100644 index 0000000..90d5f8a --- /dev/null +++ b/build/debug/Makefile @@ -0,0 +1,7 @@ +include ../targets.mk +include ../config.mk +include ../gworm.mk + +CXXFLAGS += -O0 -g3 + +-include local.mk diff --git a/build/gworm.mk b/build/gworm.mk new file mode 100644 index 0000000..8a800ef --- /dev/null +++ b/build/gworm.mk @@ -0,0 +1,75 @@ +GWORM_DIRS := $(shell cd $(TOP) && find src -type d) +GWORM_SRCS := $(shell cd $(TOP) && find src -type f -name '*.cpp') +GWORM_DEPS = $(GWORM_SRCS:%.cpp=%.d) +GWORM_OBJS = $(GWORM_SRCS:%.cpp=%.o) +GWORM_EXES = gworm + +GWORM_TEST_DIRS := $(shell cd $(TOP) && find tests -type d) +GWORM_TEST_SRCS := $(shell cd $(TOP) && find tests -type f -name '*.cpp') +GWORM_TEST_DEPS := $(GWORM_TEST_SRCS:%.cpp=%.d) +GWORM_TEST_OBJS := $(GWORM_TEST_SRCS:%.cpp=%.o) +GWORM_TEST_EXES := test-all + +GWORM_FLAGS = $(strip \ + $(SDL_FLAGS) \ + ) +GWORM_LIBS = $(strip \ + $(SDL_LIBS) \ + ) + +GWORM_TEST_FLAGS = $(GWORM_FLAGS) $(CPPUNIT_FLAGS) +GWORM_TEST_LIBS = $(GWORM_LIBS) $(CPPUNIT_LIBS) + +-include $(GWORM_DEPS) +-include $(GWORM_TEST_DEPS) + +$(GWORM_OBJS): %.o: $(TOP)/%.cpp + -@$(MKDIR) "$(@D)" + @echo "compile: $@" + $(VERBOSE) $(CXX) -c -o "$@" -MMD -MP -MF"$*.d" -MT"$@" "$<" \ + $(GWORM_FLAGS) $(CPPFLAGS) $(CXXFLAGS) + +$(GWORM_EXES): $(GWORM_OBJS) + -@$(MKDIR) "$(@D)" + @echo "link: $@" + $(VERBOSE) $(CXX) -o "$@" $^ \ + $(GWORM_FLAGS) $(GWORM_LIBS) $(LDFLAGS) + +$(GWORM_TEST_OBJS): %.o: $(TOP)/%.cpp + -@$(MKDIR) "$(@D)" + @echo "compile: $@" + $(VERBOSE) $(CXX) -c -o "$@" -MMD -MP -MF"$*.d" -MT"$@" "$<" \ + $(GWORM_TEST_FLAGS) $(CPPFLAGS) $(CXXFLAGS) + +$(GWORM_TEST_EXES): $(GWORM_TEST_OBJS) $(filter-out src/main.o, $(GWORM_OBJS)) + -@$(MKDIR) "$(@D)" + @echo "link: $@" + $(VERBOSE) $(CXX) -o "$@" $^ \ + $(GWORM_TEST_FLAGS) $(GWORM_TEST_LIBS) $(LDFLAGS) + +gworm-all: $(GWORM_EXES) + +gworm-clean: + $(VERBOSE) -$(RM) $(GWORM_DEPS) + $(VERBOSE) -$(RM) $(GWORM_OBJS) + $(VERBOSE) -$(RM) $(GWORM_EXES) + $(VERBOSE) -$(RM) $(GWORM_TEST_DEPS) + $(VERBOSE) -$(RM) $(GWORM_TEST_OBJS) + $(VERBOSE) -$(RM) $(GWORM_TEST_EXES) + $(VERBOSE) -$(RMDIR) $(GWORM_DIRS) + $(VERBOSE) -$(RMDIR) $(GWORM_TEST_DIRS) + +gworm-tests: $(GWORM_TEST_EXES) + +gworm-test-all: test-all + @echo "test: test-all" + $(VERBOSE) ./test-all + +all: gworm-all +clean: gworm-clean +tests: gworm-tests +test: gworm-test-all + +.PHONY: gworm-all gworm-clean gworm-tests gworm-test-all + +-include $(BUILD)gworm-local.mk diff --git a/build/release/Makefile b/build/release/Makefile new file mode 100644 index 0000000..3a19407 --- /dev/null +++ b/build/release/Makefile @@ -0,0 +1,8 @@ +include ../targets.mk +include ../config.mk +include ../gworm.mk + +CPPFLAGS += -DNDEBUG +CXXFLAGS += -O2 + +-include local.mk diff --git a/build/targets.mk b/build/targets.mk new file mode 100644 index 0000000..5793443 --- /dev/null +++ b/build/targets.mk @@ -0,0 +1,10 @@ +all: + +clean: + @echo "clean" + +tests: + +test: + +.PHONY: all clean tests test diff --git a/src/app/Application.cpp b/src/app/Application.cpp new file mode 100644 index 0000000..e1ac201 --- /dev/null +++ b/src/app/Application.cpp @@ -0,0 +1,211 @@ +#include "Application.h" + +#include "../graphics/Canvas.h" +#include "../graphics/Color.h" +#include "../world/World.h" + +#include +#include +using namespace std; +using namespace chrono; + + +namespace gworm { + +Application::Application(Canvas &c, World &w) +: canvas(c) +, world(w) +, focus(Vector(5, 5), 25) +, cam(c.Size(), focus.Pos()) +, last(SDL_GetTicks()) +, running(false) +, paused(false) { + +} + + +void Application::Run() { + running = true; + while (running) { + Uint32 now = SDL_GetTicks(); + int delta = now - last; + Loop(delta); + last = now; + } +} + + +void Application::Loop(int delta) { + cout << "delta: " << delta << endl << endl; + HandleEvents(); + if (delta == 0) { + SDL_Delay(1); + return; + } + + if (!paused) { + Update(delta); + } + Render(); + canvas.Present(); +} + + +void Application::HandleEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + running = false; + break; + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_RESIZED) { + cam.Resize(event.window.data1, event.window.data2); + } + break; + case SDL_KEYDOWN: + if (!event.key.repeat) { + OnKeyDown(event.key); + } + break; + case SDL_KEYUP: + if (!event.key.repeat) { + OnKeyUp(event.key); + } + break; + default: + // skip event + break; + } + } +} + +void Application::OnKeyDown(const SDL_KeyboardEvent &e) { + switch (e.keysym.sym) { + case SDLK_UP: + focus.MoveUp(); + break; + case SDLK_DOWN: + focus.MoveDown(); + break; + case SDLK_LEFT: + focus.MoveLeft(); + break; + case SDLK_RIGHT: + focus.MoveRight(); + break; + case SDLK_p: + paused = !paused; + break; + default: + break; + } +} + +void Application::OnKeyUp(const SDL_KeyboardEvent &e) { + switch (e.keysym.sym) { + case SDLK_UP: + focus.StopUp(); + break; + case SDLK_DOWN: + focus.StopDown(); + break; + case SDLK_LEFT: + focus.StopLeft(); + break; + case SDLK_RIGHT: + focus.StopRight(); + break; + default: + break; + } +} + + +void Application::Update(int dt) { + const float delta = dt / 1e3; + cam.Update(delta); + world.Update(delta); + focus.Update(delta); +} + + +void Application::Render() { + auto enter = chrono::system_clock::now(); + RenderBackground(); + auto background = chrono::system_clock::now(); + cout << " background: " << duration_cast(background - enter).count() << endl; + RenderWorld(); + auto world = chrono::system_clock::now(); + cout << " world: " << duration_cast(world - background).count() << endl; + RenderEntities(); + auto entities = chrono::system_clock::now(); + cout << " entities: " << duration_cast(entities - world).count() << endl; + RenderUI(); + auto ui = chrono::system_clock::now(); + cout << " UI: " << duration_cast(ui - entities).count() << endl; +} + +void Application::RenderBackground() { + constexpr Color background(0x00, 0x00, 0x00); + + canvas.SetColor(background); + canvas.Fill(); +} + +void Application::RenderWorld() { + Vector begin(0, 0); + Vector end(world.Size()); + Vector topLeft = cam.ToScreen(Vector(begin)); + Vector bottomRight = cam.ToScreen(Vector(end)); + Vector clip(canvas.Size()); + + if (begin.x > clip.x || begin.y > clip.y || end.x < 0 || end.y < 0) { + return; + } + + if (topLeft.x < 0) { + begin.x -= topLeft.x; + topLeft.x = 0; + } + if (topLeft.y < 0) { + begin.y -= topLeft.y; + topLeft.y = 0; + } + if (bottomRight.x > clip.x) { + end.x -= bottomRight.x - clip.x; + bottomRight.x = clip.x; + } + if (bottomRight.y > clip.y) { + end.y -= bottomRight.y - clip.y; + bottomRight.y = clip.y; + } + + for (Vector pos(begin), cur(topLeft); pos.y < end.y; ++pos.y, ++cur.y) { + for (pos.x = begin.x, cur.x = topLeft.x; pos.x < end.x; ++pos.x, ++cur.x) { + canvas.SetColor(world.ColorAt(pos)); + canvas.Dot(cur); + } + } +} + +void Application::RenderEntities() { + +} + +void Application::RenderUI() { + constexpr Color focusColor(0xFA, 0xFA, 0x00); + constexpr Color forceColor(0xFA, 0x00, 0x00); + + canvas.SetColor(focusColor); + canvas.Cross(cam.ToScreen(focus.Pos()), 15); + + const Vector force = world.ForceAt(focus.Pos(), 1); + const Vector screenFocus = cam.ToScreen(focus.Pos()); + + canvas.SetColor(forceColor); + canvas.Arrow(screenFocus, screenFocus + Vector(force * 10.0f)); + cout << "force on 1kg at " << focus.Pos() << ": " << force << endl; +} + +} diff --git a/src/app/Application.h b/src/app/Application.h new file mode 100644 index 0000000..23815d3 --- /dev/null +++ b/src/app/Application.h @@ -0,0 +1,54 @@ +#ifndef GWORM_APPLICATION_H_ +#define GWORM_APPLICATION_H_ + +#include "../graphics/Camera.h" +#include "../graphics/Moveable.h" +#include "../graphics/Vector.h" + +#include + + +namespace gworm { + +class Canvas; +class World; + +class Application { + +public: + Application(Canvas &, World &); + +public: + void Run(); + +private: + void Loop(int delta_ms); + + void HandleEvents(); + void OnKeyDown(const SDL_KeyboardEvent &); + void OnKeyUp(const SDL_KeyboardEvent &); + + void Update(int delta_ms); + + void Render(); + void RenderBackground(); + void RenderWorld(); + void RenderEntities(); + void RenderUI(); + +private: + Canvas &canvas; + World &world; + + Moveable focus; + Camera cam; + + Uint32 last; + bool running; + bool paused; + +}; + +} + +#endif diff --git a/src/app/SDL.cpp b/src/app/SDL.cpp new file mode 100644 index 0000000..b0d087d --- /dev/null +++ b/src/app/SDL.cpp @@ -0,0 +1,19 @@ +#include "SDL.h" + +#include +#include + + +namespace gworm { + +SDL::SDL(Uint32 flags) { + if (SDL_Init(flags) != 0) { + throw std::runtime_error(std::string("init SDL: ") + SDL_GetError()); + } +} + +SDL::~SDL() { + SDL_Quit(); +} + +} diff --git a/src/app/SDL.h b/src/app/SDL.h new file mode 100644 index 0000000..e939888 --- /dev/null +++ b/src/app/SDL.h @@ -0,0 +1,22 @@ +#ifndef GWORM_SDL_H_ +#define GWORM_SDL_H_ + +#include + + +namespace gworm { + +class SDL { + +public: + explicit SDL(Uint32 flags); + ~SDL(); + + SDL(const SDL &) = delete; + SDL &operator =(const SDL &) = delete; + +}; + +} + +#endif diff --git a/src/graphics/Camera.cpp b/src/graphics/Camera.cpp new file mode 100644 index 0000000..e01c026 --- /dev/null +++ b/src/graphics/Camera.cpp @@ -0,0 +1,19 @@ +#include "Camera.h" + + +namespace gworm { + +Camera::Camera(Vector s, const Vector &t) +: target(&t) +, size(s) +, offset(size / 2) { + +} + + +void Camera::Resize(Vector s) { + size = s; + offset = size / 2; +} + +} diff --git a/src/graphics/Camera.h b/src/graphics/Camera.h new file mode 100644 index 0000000..a6a9772 --- /dev/null +++ b/src/graphics/Camera.h @@ -0,0 +1,42 @@ +#ifndef GWORM_CAMERA_H_ +#define GWORM_CAMERA_H_ + +#include "Vector.h" + + +namespace gworm { + +class Camera { + +public: + Camera(Vector size, const Vector &); + +public: + void SetTarget(const Vector &t) { target = &t; } + + Vector ScreenSize() const { return size; } + + void Resize(int w, int h) { Resize(Vector(w, h)); } + void Resize(Vector); + void Update(float deltaT) { } // unused + + /// transform v from world coords to screen coords + Vector ToScreen(Vector v) const { + return Vector(v - *target) + offset; + } + + /// transform v from screen coords to world coords + Vector FromScreen(Vector v) const { + return Vector(v - offset) + *target; + } + +private: + const Vector *target; + Vector size; + Vector offset; + +}; + +} + +#endif diff --git a/src/graphics/Canvas.cpp b/src/graphics/Canvas.cpp new file mode 100644 index 0000000..b461ab4 --- /dev/null +++ b/src/graphics/Canvas.cpp @@ -0,0 +1,246 @@ +#include "Canvas.h" + +#include +#include +#include +#include + +using std::runtime_error; + + +namespace gworm { + +Canvas::Canvas(SDL_Window *win, int index, Uint32 flags) +: canv(SDL_CreateRenderer(win, index, flags)) { + if (!canv) { + throw runtime_error(std::string("create canvas: ") + SDL_GetError()); + } +} + +Canvas::~Canvas() { + if (canv) SDL_DestroyRenderer(canv); +} + +Canvas::Canvas(Canvas &&other) +: canv(other.canv) { + other.canv = nullptr; +} + +Canvas &Canvas::operator =(Canvas &&other) { + std::swap(canv, other.canv); + return *this; +} + + +void Canvas::Present() { + SDL_RenderPresent(canv); +} + + +Vector Canvas::Size() const { + assert(canv); + Vector size; + SDL_GetRendererOutputSize(canv, &size.x, &size.y); + return size; +} + + +void Canvas::SetColor(Color c) { + SDL_SetRenderDrawColor(canv, c.r, c.g, c.b, c.a); +} + + +void Canvas::Fill() { + SDL_RenderClear(canv); +} + +void Canvas::Outline() { + SDL_RenderDrawRect(canv, nullptr); +} + + +void Canvas::Line(Vector from, Vector to) { + SDL_RenderDrawLine(canv, from.x, from.y, to.x, to.y); +} + +void Canvas::FillRect(Vector pos, Vector size) { + SDL_Rect destRect; + destRect.x = pos.x; + destRect.y = pos.y; + destRect.w = size.x; + destRect.h = size.y; + SDL_RenderFillRect(canv, &destRect); +} + +void Canvas::OutlineRect(Vector pos, Vector size) { + SDL_Rect destRect; + destRect.x = pos.x; + destRect.y = pos.y; + destRect.w = size.x; + destRect.h = size.y; + SDL_RenderDrawRect(canv, &destRect); +} + + +void Canvas::Dot(Vector pos) { + SDL_RenderDrawPoint(canv, pos.x, pos.y); +} + +void Canvas::Cross(Vector pos, int extent) { + Line( + Vector(pos.x - extent, pos.y), + Vector(pos.x + extent, pos.y)); + Line( + Vector(pos.x, pos.y - extent), + Vector(pos.x, pos.y + extent)); +} + +void Canvas::Arrow(Vector from, Vector to) { + Line(from, to); + Vector delta(to - from); + delta = delta / Length(delta); + + Line(to, to + Vector(Rotate90(delta) * 5.0f - (delta * 5.0f))); + Line(to, to + Vector(Rotate270(delta) * 5.0f - (delta * 5.0f))); +} + +void Canvas::Triangle(Vector v1, Vector v2, Vector v3) { + SDL_Point points[4] = { v1, v2, v3, v1 }; + SDL_RenderDrawLines(canv, points, 4); +} + +void Canvas::Quad(Vector v1, Vector v2, Vector v3, Vector v4) { + SDL_Point points[5] = { v1, v2, v3, v4, v1 }; + SDL_RenderDrawLines(canv, points, 5); +} + + +namespace { + +template +void GridImpl( + Canvas &canv, + Vector pos, + Vector size, + Vector step) { + Vector from(pos); + Vector to(pos + size); + Vector clip(canv.Size()); + + if (from.x > clip.x || from.y > clip.y || to.x < 0 || to.y < 0) { + return; + } + if (step.x <= 1 || step.y <= 1) { + canv.FillRect(pos, size); + return; + } + + if (from.x < -step.x) { + int skip = from.x / -step.x; + from.x += skip * step.x; + } + if (from.y < -step.y) { + int skip = from.y / -step.y; + from.y += skip * step.y; + } + if (to.x > clip.x + step.x) { + int skip = (to.x - clip.x) / step.x; + to.x -= skip * step.x; + } + if (to.y > clip.y + step.y) { + int skip = (to.y - clip.y) / step.y; + to.y -= skip * step.y; + } + + int width = to.x - from.x; + int height = to.y - from.y; + + for (Vector pos(from); pos.x <= to.x; pos.x += step.x) { + canv.Line(pos, Vector(pos.x, pos.y + height)); + } + for (Vector pos(from); pos.y <= to.y; pos.y += step.y) { + canv.Line(pos, Vector(pos.x + width, pos.y)); + } +} + +template +void Grid2Impl( + Canvas &canv, + Vector pos, + Vector size, + Vector step, + Vector n, + Color c1, + Color c2) { + Vector from(pos); + Vector to(pos + size); + Vector clip(canv.Size()); + + if (from.x > clip.x || from.y > clip.y || to.x < 0 || to.y < 0) { + return; + } + if (step.x <= 1 || step.y <= 1) { + canv.SetColor(c1); + canv.FillRect(pos, size); + canv.SetColor(c2); + GridImpl(canv, pos, size, step * Vector(n)); + return; + } + + Vector i(0, 0); + + if (from.x < -step.x) { + int skip = from.x / -step.x; + from.x += skip * step.x; + i.x += skip; + } + if (from.y < -step.y) { + int skip = from.y / -step.y; + from.y += skip * step.y; + i.y += skip; + } + if (to.x > clip.x + step.x) { + int skip = (to.x - clip.x) / step.x; + to.x -= skip * step.x; + } + if (to.y > clip.y + step.y) { + int skip = (to.y - clip.y) / step.y; + to.y -= skip * step.y; + } + + int width = to.x - from.x; + int height = to.y - from.y; + + for (Vector pos(from); pos.x <= to.x; pos.x += step.x) { + canv.SetColor((i.x++ % n.x) ? c1 : c2); + canv.Line(pos, Vector(pos.x, pos.y + height)); + } + for (Vector pos(from); pos.y <= to.y; pos.y += step.y) { + canv.SetColor((i.y++ % n.y) ? c1 : c2); + canv.Line(pos, Vector(pos.x + width, pos.y)); + } +} + +} + +void Canvas::Grid(Vector pos, Vector size, Vector step) { + GridImpl(*this, pos, size, step); +} + +void Canvas::Grid(Vector pos, Vector size, Vector step) { + GridImpl(*this, pos, size, step); +} + +void Canvas::Grid2( + Vector pos, Vector size, Vector step, + Vector n, Color c1, Color c2) { + Grid2Impl(*this, pos, size, step, n, c1, c2); +} + +void Canvas::Grid2( + Vector pos, Vector size, Vector step, + Vector n, Color c1, Color c2) { + Grid2Impl(*this, pos, size, step, n, c1, c2); +} + +} diff --git a/src/graphics/Canvas.h b/src/graphics/Canvas.h new file mode 100644 index 0000000..b464dd6 --- /dev/null +++ b/src/graphics/Canvas.h @@ -0,0 +1,60 @@ +#ifndef GWORM_CANVAS_H_ +#define GWORM_CANVAS_H_ + +#include "Color.h" +#include "Vector.h" + +#include + + +namespace gworm { + +class Canvas { + +public: + Canvas() : canv(nullptr) { } + Canvas(SDL_Window *win, int index, Uint32 flags); + ~Canvas(); + + Canvas(Canvas &&); + Canvas &operator =(Canvas &&); + + Canvas(const Canvas &) = delete; + Canvas &operator =(const Canvas &) = delete; + +public: + Vector Size() const; + + void Present(); + + void SetColor(Color); + + void Fill(); + void Outline(); + + void Line(Vector from, Vector to); + void FillRect(Vector pos, Vector size); + void OutlineRect(Vector pos, Vector size); + + void Dot(Vector pos); + void Cross(Vector pos, int extent); + void Arrow(Vector from, Vector to); + void Triangle(Vector, Vector, Vector); + void Quad(Vector, Vector, Vector, Vector); + + void Grid(Vector pos, Vector size, Vector step); + void Grid(Vector pos, Vector size, Vector step); + + void Grid2(Vector pos, Vector size, Vector step, + Vector n, Color, Color); + void Grid2(Vector pos, Vector size, Vector step, + Vector n, Color, Color); + +private: + SDL_Renderer *canv; + +}; + +} + +#endif diff --git a/src/graphics/Color.h b/src/graphics/Color.h new file mode 100644 index 0000000..f976e1a --- /dev/null +++ b/src/graphics/Color.h @@ -0,0 +1,22 @@ +#ifndef SPACE_COLOR_H_ +#define SPACE_COLOR_H_ + +#include + + +namespace gworm { + +struct Color { + + constexpr Color() + : Color(0, 0, 0) { } + constexpr Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 0xFF) + : r(r), g(g), b(b), a(a) { } + + Uint8 r, g, b, a; + +}; + +} + +#endif diff --git a/src/graphics/Moveable.h b/src/graphics/Moveable.h new file mode 100644 index 0000000..4afce58 --- /dev/null +++ b/src/graphics/Moveable.h @@ -0,0 +1,44 @@ +#ifndef SPACE_MOVEABLE_H_ +#define SPACE_MOVEABLE_H_ + +#include "Vector.h" + + +namespace gworm { + +template +class Moveable { + +public: + Moveable(Vector pos, Scalar speed) + : dir(0, 0), pos(pos), speed(speed) { } + +public: + const Vector &Pos() const { return pos; } + Vector Vel() const { return Vector(dir) * speed; } + void SetSpeed(float s) { speed = s; } + + void Update(Scalar delta) { + pos += Vel() * delta; + } + +public: + void MoveUp() { dir.y -= 1; } + void StopUp() { dir.y += 1; } + void MoveDown() { dir.y += 1; } + void StopDown() { dir.y -= 1; } + void MoveLeft() { dir.x -= 1; } + void StopLeft() { dir.x += 1; } + void MoveRight() { dir.x += 1; } + void StopRight() { dir.x -= 1; } + +private: + Vector dir; + Vector pos; + Scalar speed; + +}; + +} + +#endif diff --git a/src/graphics/Vector.h b/src/graphics/Vector.h new file mode 100644 index 0000000..d35cf33 --- /dev/null +++ b/src/graphics/Vector.h @@ -0,0 +1,210 @@ +#ifndef GWORM_VECTOR_H_ +#define GWORM_VECTOR_H_ + +#include +#include +#include +#include + + +namespace gworm { + +template +class Vector { + +public: + constexpr Vector() : x(0), y(0) { } + constexpr Vector(Scalar x, Scalar y) : x(x), y(y) { } + + template + constexpr Vector(Vector other) : x(other.x), y(other.y) { } + + static Vector FromPolar(Scalar rad, Scalar az) { + return Vector(rad * std::cos(az), rad * std::sin(az)); + } + + static constexpr Vector unit45 = Vector(-0.7071, 0.7071); + +public: + Vector &operator +=(Vector other) { + x += other.x; + y += other.y; + return *this; + } + Vector &operator -=(Vector other) { + x -= other.x; + y -= other.y; + return *this; + } + + SDL_Point ToPoint() const { + SDL_Point p; + p.x = x; + p.y = y; + return p; + } + +public: + Scalar x; + Scalar y; + +}; + +template +constexpr Vector Vector::unit45; + +/// specialization with same layout as SDL_Point +template<> +class Vector +: public SDL_Point { + +public: + constexpr Vector() : SDL_Point({0, 0}) { } + constexpr Vector(int x, int y) : SDL_Point({x, y}) { } + + template + constexpr Vector(Vector other) + : SDL_Point({int(other.x), int(other.y)}) { } + +public: + Vector &operator +=(Vector other) { + x += other.x; + y += other.y; + return *this; + } + Vector &operator -=(Vector other) { + x -= other.x; + y -= other.y; + return *this; + } + + SDL_Point ToPoint() const { + return *this; + } + +}; + + +template +constexpr Vector operator -(Vector v) { + return Vector(-v.x, -v.y); +} + + +template +constexpr Vector operator +(Vector lhs, Vector rhs) { + return Vector(lhs.x + rhs.x, lhs.y + rhs.y); +} + +template +constexpr Vector operator -(Vector lhs, Vector rhs) { + return Vector(lhs.x - rhs.x, lhs.y - rhs.y); +} + + +template +constexpr Vector operator *(Vector lhs, Scalar rhs) { + return Vector(lhs.x * rhs, lhs.y * rhs); +} + +template +constexpr Vector operator *(Scalar lhs, Vector rhs) { + return rhs * lhs; +} +template +constexpr Vector operator *(Vector lhs, Vector rhs) { + return Vector(lhs.x * rhs.x, lhs.y * rhs.y); +} + + +template +constexpr Vector operator /(Vector lhs, Scalar rhs) { + return Vector(lhs.x / rhs, lhs.y / rhs); +} + +template +constexpr Vector operator /(Scalar lhs, Vector rhs) { + return rhs / lhs; +} +template +constexpr Vector operator /(Vector lhs, Vector rhs) { + return Vector(lhs.x / rhs.x, lhs.y / rhs.y); +} + + +template +constexpr bool operator ==(Vector lhs, Vector rhs) { + return lhs.x == rhs.x && lhs.y == rhs.y; +} +template +constexpr bool operator !=(Vector lhs, Vector rhs) { + return lhs.x != rhs.x && lhs.y != rhs.y; +} + + +template +constexpr Scalar Cross2D(Vector lhs, Vector rhs) { + return (lhs.x * rhs.y) - (lhs.y * rhs.x); +} +template +constexpr Scalar Dot(Vector lhs, Vector rhs) { + return (lhs.x * rhs.x) + (lhs.y * rhs.y); +} +template +constexpr Scalar Length(Vector v) { + return std::sqrt(Dot(v, v)); +} +template +constexpr Vector Norm(Vector v) { + return v / Length(v); +} + +template +constexpr Vector Rotate90(Vector v) { + return Vector(-v.y, v.x); +} +template +constexpr Vector Rotate180(Vector v) { + return -v; +} +template +constexpr Vector Rotate270(Vector v) { + return Vector(v.y, -v.x); +} + + +template +inline std::ostream &operator <<(std::ostream &out, Vector v) { + return out << '<' << v.x << ',' << v.y << '>'; +} + +} + + +namespace std { + +template +constexpr gworm::Vector min( + gworm::Vector lhs, + gworm::Vector rhs +) { + return gworm::Vector( + min(lhs.x, rhs.x), + min(lhs.y, rhs.y) + ); +} + +template +constexpr gworm::Vector max( + gworm::Vector lhs, + gworm::Vector rhs +) { + return gworm::Vector( + max(lhs.x, rhs.x), + max(lhs.y, rhs.y) + ); +} + +} + +#endif diff --git a/src/graphics/Window.cpp b/src/graphics/Window.cpp new file mode 100644 index 0000000..e7571f8 --- /dev/null +++ b/src/graphics/Window.cpp @@ -0,0 +1,58 @@ +#include "Window.h" + +#include +#include +#include +#include + +using std::runtime_error; + + +namespace gworm { + +const Vector Window::POS_CENTER( + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); +const Vector Window::POS_UNDEF( + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED); + + +Window::Window( + const char *title, + Vector pos, + Vector size, + Uint32 flags) +: win(SDL_CreateWindow(title, pos.x, pos.y, size.x, size.y, flags)) { + if (!win) { + throw runtime_error(std::string("create window ") + title + + ": " + SDL_GetError()); + } +} + +Window::~Window() { + if (win) SDL_DestroyWindow(win); +} + +Window::Window(Window &&other) +: win(other.win) { + other.win = nullptr; +} + +Window &Window::operator =(Window &&other) { + std::swap(win, other.win); + return *this; +} + + +Vector Window::Size() const { + assert(win); + Vector size; + SDL_GetWindowSize(win, &size.x, &size.y); + return size; +} + + +Canvas Window::CreateCanvas(Uint32 flags) { + return Canvas(win, -1, flags); +} + +} diff --git a/src/graphics/Window.h b/src/graphics/Window.h new file mode 100644 index 0000000..08f9995 --- /dev/null +++ b/src/graphics/Window.h @@ -0,0 +1,41 @@ +#ifndef GWORM_WINDOW_H_ +#define GWORM_WINDOW_H + +#include "Canvas.h" +#include "Vector.h" + +#include + + +namespace gworm { + +class Window { + +public: + static const Vector POS_CENTER; + static const Vector POS_UNDEF; + +public: + Window() : win(nullptr) { } + Window(const char *title, Vector pos, Vector size, Uint32 flags); + ~Window(); + + Window(Window &&); + Window &operator =(Window &&); + + Window(const Window &) = delete; + Window &operator =(const Window &) = delete; + +public: + Vector Size() const; + + Canvas CreateCanvas(Uint32 flags); + +private: + SDL_Window *win; + +}; + +} + +#endif diff --git a/src/graphics/const.h b/src/graphics/const.h new file mode 100644 index 0000000..42db2af --- /dev/null +++ b/src/graphics/const.h @@ -0,0 +1,20 @@ +#ifndef GWORM_CONST_H_ +#define GWORM_CONST_H_ + +namespace gworm { + +// this is so very darn long so it can easily be changed to double +constexpr float PI = 3.141592653589793238462643383279502884; +constexpr float PI2 = 2 * PI; + +// gravitational constant in m^3 kg^-1 s^-2 (= N m^2 kg^-2) (for use with F = G ((m1 m2) / r^2) +constexpr float G = 6.67384e-11; + +template +constexpr int sigma(T v) { + return v > 0 ? 1 : (v < 0 ? -1 : 0); +} + +} + +#endif diff --git a/src/gworm.cpp b/src/gworm.cpp new file mode 100644 index 0000000..5f35fd9 --- /dev/null +++ b/src/gworm.cpp @@ -0,0 +1,54 @@ +#include "app/Application.h" +#include "app/SDL.h" +#include "graphics/Canvas.h" +#include "graphics/Window.h" +#include "world/World.h" + +using namespace gworm; + + +namespace { + +void make_planet(World &world, Vector center, int radius) { + const Vector begin(0, 0); + const Vector end(world.Size()); + const int sqrad = radius * radius; + + for (Vector pos(begin); pos.y < end.y; ++pos.y) { + for (pos.x = 0; pos.x < end.x; ++pos.x) { + Vector diff(center - pos); + int sqdist = Dot(diff, diff); + if (sqdist <= sqrad) { + world.SetMass(pos, 1e10); + world.SetColor(pos, Color(0x00, 0x00, 0xA0)); + } else { + world.SetMass(pos, 0); + world.SetColor(pos, Color(0x00, 0x00, 0x00, 0x00)); + } + } + } +} + +} + + +int main(int argc, const char *argv[]) { + SDL sdl(SDL_INIT_VIDEO); + Window win( + "gworm", + Window::POS_UNDEF, + Vector(800, 600), + SDL_WINDOW_RESIZABLE + ); + Canvas canv(win.CreateCanvas( + 0 + )); + + World world(Vector(500, 500)); + make_planet(world, Vector(250, 250), 220); + + Application app(canv, world); + app.Run(); + + return 0; +} diff --git a/src/world/World.cpp b/src/world/World.cpp new file mode 100644 index 0000000..219708e --- /dev/null +++ b/src/world/World.cpp @@ -0,0 +1,43 @@ +#include "World.h" + +#include "../graphics/const.h" + + +namespace gworm { + +World::World(Vector size) +: size(size) +, count(size.x * size.y) +, masses(count, 1000000000.0f) +, colors(count, Color(0x7F, 0x7F, 0x7F)) { + +} + + +void World::Update(float dt) { + +} + + +Vector World::ForceAt(Vector p1, float m1) const { + Vector force(0, 0); + + for (int i = 0; i < count; ++i) { + const Vector p2(i % size.y, i / size.y); + if (p1 == p2) continue; + + const Vector diff(p2 - p1); + const Vector dir(Norm(diff)); + + const float m2 = masses[i]; + const float r2 = Dot(diff, diff); + + const float mag = G * ((m1 * m2) / r2) * 2; // double because m2 is our reference frame + + force += dir * mag; + } + + return force; +} + +} diff --git a/src/world/World.h b/src/world/World.h new file mode 100644 index 0000000..4cb5a27 --- /dev/null +++ b/src/world/World.h @@ -0,0 +1,43 @@ +#ifndef GWORM_WORLD_H_ +#define GWORM_WORLD_H_ + +#include "../graphics/Color.h" +#include "../graphics/Vector.h" + +#include + + +namespace gworm { + +class World { + +public: + World(Vector size); + +public: + Vector Size() const { return size; } + +public: + void Update(float dt); + + int Index(Vector pos) const { return pos.x * size.y + pos.y; } + + float MassAt(Vector pos) const { return masses[Index(pos)]; } + void SetMass(Vector pos, float m) { masses[Index(pos)] = m; } + Color ColorAt(Vector pos) const { return colors[Index(pos)]; } + void SetColor(Vector pos, Color c) { colors[Index(pos)] = c; } + + Vector ForceAt(Vector, float m) const; + +private: + Vector size; + int count; + + std::vector masses; + std::vector colors; + +}; + +} + +#endif