--- /dev/null
+*.swp
+*.swp
+/build/*/gworm
+/build/*/src
--- /dev/null
+# 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
--- /dev/null
+include ../targets.mk
+include ../config.mk
+include ../gworm.mk
+
+CXXFLAGS += -O0 -g3
+
+-include local.mk
--- /dev/null
+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
--- /dev/null
+include ../targets.mk
+include ../config.mk
+include ../gworm.mk
+
+CPPFLAGS += -DNDEBUG
+CXXFLAGS += -O2
+
+-include local.mk
--- /dev/null
+all:
+
+clean:
+ @echo "clean"
+
+tests:
+
+test:
+
+.PHONY: all clean tests test
--- /dev/null
+#include "Application.h"
+
+#include "../graphics/Canvas.h"
+#include "../graphics/Color.h"
+#include "../world/World.h"
+
+#include <chrono>
+#include <iostream>
+using namespace std;
+using namespace chrono;
+
+
+namespace gworm {
+
+Application::Application(Canvas &c, World &w)
+: canvas(c)
+, world(w)
+, focus(Vector<float>(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<milliseconds>(background - enter).count() << endl;
+ RenderWorld();
+ auto world = chrono::system_clock::now();
+ cout << " world: " << duration_cast<milliseconds>(world - background).count() << endl;
+ RenderEntities();
+ auto entities = chrono::system_clock::now();
+ cout << " entities: " << duration_cast<milliseconds>(entities - world).count() << endl;
+ RenderUI();
+ auto ui = chrono::system_clock::now();
+ cout << " UI: " << duration_cast<milliseconds>(ui - entities).count() << endl;
+}
+
+void Application::RenderBackground() {
+ constexpr Color background(0x00, 0x00, 0x00);
+
+ canvas.SetColor(background);
+ canvas.Fill();
+}
+
+void Application::RenderWorld() {
+ Vector<int> begin(0, 0);
+ Vector<int> end(world.Size());
+ Vector<int> topLeft = cam.ToScreen(Vector<float>(begin));
+ Vector<int> bottomRight = cam.ToScreen(Vector<float>(end));
+ Vector<int> 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<int> 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<float> force = world.ForceAt(focus.Pos(), 1);
+ const Vector<int> screenFocus = cam.ToScreen(focus.Pos());
+
+ canvas.SetColor(forceColor);
+ canvas.Arrow(screenFocus, screenFocus + Vector<int>(force * 10.0f));
+ cout << "force on 1kg at " << focus.Pos() << ": " << force << endl;
+}
+
+}
--- /dev/null
+#ifndef GWORM_APPLICATION_H_
+#define GWORM_APPLICATION_H_
+
+#include "../graphics/Camera.h"
+#include "../graphics/Moveable.h"
+#include "../graphics/Vector.h"
+
+#include <SDL.h>
+
+
+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<float> focus;
+ Camera cam;
+
+ Uint32 last;
+ bool running;
+ bool paused;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "SDL.h"
+
+#include <stdexcept>
+#include <string>
+
+
+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();
+}
+
+}
--- /dev/null
+#ifndef GWORM_SDL_H_
+#define GWORM_SDL_H_
+
+#include <SDL.h>
+
+
+namespace gworm {
+
+class SDL {
+
+public:
+ explicit SDL(Uint32 flags);
+ ~SDL();
+
+ SDL(const SDL &) = delete;
+ SDL &operator =(const SDL &) = delete;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Camera.h"
+
+
+namespace gworm {
+
+Camera::Camera(Vector<int> s, const Vector<float> &t)
+: target(&t)
+, size(s)
+, offset(size / 2) {
+
+}
+
+
+void Camera::Resize(Vector<int> s) {
+ size = s;
+ offset = size / 2;
+}
+
+}
--- /dev/null
+#ifndef GWORM_CAMERA_H_
+#define GWORM_CAMERA_H_
+
+#include "Vector.h"
+
+
+namespace gworm {
+
+class Camera {
+
+public:
+ Camera(Vector<int> size, const Vector<float> &);
+
+public:
+ void SetTarget(const Vector<float> &t) { target = &t; }
+
+ Vector<int> ScreenSize() const { return size; }
+
+ void Resize(int w, int h) { Resize(Vector<int>(w, h)); }
+ void Resize(Vector<int>);
+ void Update(float deltaT) { } // unused
+
+ /// transform v from world coords to screen coords
+ Vector<int> ToScreen(Vector<float> v) const {
+ return Vector<int>(v - *target) + offset;
+ }
+
+ /// transform v from screen coords to world coords
+ Vector<float> FromScreen(Vector<int> v) const {
+ return Vector<float>(v - offset) + *target;
+ }
+
+private:
+ const Vector<float> *target;
+ Vector<int> size;
+ Vector<int> offset;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Canvas.h"
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+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<int> Canvas::Size() const {
+ assert(canv);
+ Vector<int> 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<int> from, Vector<int> to) {
+ SDL_RenderDrawLine(canv, from.x, from.y, to.x, to.y);
+}
+
+void Canvas::FillRect(Vector<int> pos, Vector<int> 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<int> pos, Vector<int> 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<int> pos) {
+ SDL_RenderDrawPoint(canv, pos.x, pos.y);
+}
+
+void Canvas::Cross(Vector<int> pos, int extent) {
+ Line(
+ Vector<int>(pos.x - extent, pos.y),
+ Vector<int>(pos.x + extent, pos.y));
+ Line(
+ Vector<int>(pos.x, pos.y - extent),
+ Vector<int>(pos.x, pos.y + extent));
+}
+
+void Canvas::Arrow(Vector<int> from, Vector<int> to) {
+ Line(from, to);
+ Vector<float> delta(to - from);
+ delta = delta / Length(delta);
+
+ Line(to, to + Vector<int>(Rotate90(delta) * 5.0f - (delta * 5.0f)));
+ Line(to, to + Vector<int>(Rotate270(delta) * 5.0f - (delta * 5.0f)));
+}
+
+void Canvas::Triangle(Vector<int> v1, Vector<int> v2, Vector<int> v3) {
+ SDL_Point points[4] = { v1, v2, v3, v1 };
+ SDL_RenderDrawLines(canv, points, 4);
+}
+
+void Canvas::Quad(Vector<int> v1, Vector<int> v2, Vector<int> v3, Vector<int> v4) {
+ SDL_Point points[5] = { v1, v2, v3, v4, v1 };
+ SDL_RenderDrawLines(canv, points, 5);
+}
+
+
+namespace {
+
+template<class Scalar>
+void GridImpl(
+ Canvas &canv,
+ Vector<int> pos,
+ Vector<int> size,
+ Vector<Scalar> step) {
+ Vector<int> from(pos);
+ Vector<int> to(pos + size);
+ Vector<int> 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<Scalar> pos(from); pos.x <= to.x; pos.x += step.x) {
+ canv.Line(pos, Vector<int>(pos.x, pos.y + height));
+ }
+ for (Vector<Scalar> pos(from); pos.y <= to.y; pos.y += step.y) {
+ canv.Line(pos, Vector<int>(pos.x + width, pos.y));
+ }
+}
+
+template<class Scalar>
+void Grid2Impl(
+ Canvas &canv,
+ Vector<int> pos,
+ Vector<int> size,
+ Vector<Scalar> step,
+ Vector<int> n,
+ Color c1,
+ Color c2) {
+ Vector<int> from(pos);
+ Vector<int> to(pos + size);
+ Vector<int> 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<Scalar>(n));
+ return;
+ }
+
+ Vector<int> 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<Scalar> pos(from); pos.x <= to.x; pos.x += step.x) {
+ canv.SetColor((i.x++ % n.x) ? c1 : c2);
+ canv.Line(pos, Vector<int>(pos.x, pos.y + height));
+ }
+ for (Vector<Scalar> pos(from); pos.y <= to.y; pos.y += step.y) {
+ canv.SetColor((i.y++ % n.y) ? c1 : c2);
+ canv.Line(pos, Vector<int>(pos.x + width, pos.y));
+ }
+}
+
+}
+
+void Canvas::Grid(Vector<int> pos, Vector<int> size, Vector<int> step) {
+ GridImpl(*this, pos, size, step);
+}
+
+void Canvas::Grid(Vector<int> pos, Vector<int> size, Vector<float> step) {
+ GridImpl(*this, pos, size, step);
+}
+
+void Canvas::Grid2(
+ Vector<int> pos, Vector<int> size, Vector<int> step,
+ Vector<int> n, Color c1, Color c2) {
+ Grid2Impl(*this, pos, size, step, n, c1, c2);
+}
+
+void Canvas::Grid2(
+ Vector<int> pos, Vector<int> size, Vector<float> step,
+ Vector<int> n, Color c1, Color c2) {
+ Grid2Impl(*this, pos, size, step, n, c1, c2);
+}
+
+}
--- /dev/null
+#ifndef GWORM_CANVAS_H_
+#define GWORM_CANVAS_H_
+
+#include "Color.h"
+#include "Vector.h"
+
+#include <SDL.h>
+
+
+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<int> Size() const;
+
+ void Present();
+
+ void SetColor(Color);
+
+ void Fill();
+ void Outline();
+
+ void Line(Vector<int> from, Vector<int> to);
+ void FillRect(Vector<int> pos, Vector<int> size);
+ void OutlineRect(Vector<int> pos, Vector<int> size);
+
+ void Dot(Vector<int> pos);
+ void Cross(Vector<int> pos, int extent);
+ void Arrow(Vector<int> from, Vector<int> to);
+ void Triangle(Vector<int>, Vector<int>, Vector<int>);
+ void Quad(Vector<int>, Vector<int>, Vector<int>, Vector<int>);
+
+ void Grid(Vector<int> pos, Vector<int> size, Vector<int> step);
+ void Grid(Vector<int> pos, Vector<int> size, Vector<float> step);
+
+ void Grid2(Vector<int> pos, Vector<int> size, Vector<int> step,
+ Vector<int> n, Color, Color);
+ void Grid2(Vector<int> pos, Vector<int> size, Vector<float> step,
+ Vector<int> n, Color, Color);
+
+private:
+ SDL_Renderer *canv;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef SPACE_COLOR_H_
+#define SPACE_COLOR_H_
+
+#include <SDL.h>
+
+
+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
--- /dev/null
+#ifndef SPACE_MOVEABLE_H_
+#define SPACE_MOVEABLE_H_
+
+#include "Vector.h"
+
+
+namespace gworm {
+
+template<class Scalar>
+class Moveable {
+
+public:
+ Moveable(Vector<Scalar> pos, Scalar speed)
+ : dir(0, 0), pos(pos), speed(speed) { }
+
+public:
+ const Vector<Scalar> &Pos() const { return pos; }
+ Vector<Scalar> Vel() const { return Vector<Scalar>(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<int> dir;
+ Vector<Scalar> pos;
+ Scalar speed;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef GWORM_VECTOR_H_
+#define GWORM_VECTOR_H_
+
+#include <algorithm>
+#include <cmath>
+#include <ostream>
+#include <SDL.h>
+
+
+namespace gworm {
+
+template<class Scalar>
+class Vector {
+
+public:
+ constexpr Vector() : x(0), y(0) { }
+ constexpr Vector(Scalar x, Scalar y) : x(x), y(y) { }
+
+ template<class Other>
+ constexpr Vector(Vector<Other> other) : x(other.x), y(other.y) { }
+
+ static Vector<Scalar> FromPolar(Scalar rad, Scalar az) {
+ return Vector<Scalar>(rad * std::cos(az), rad * std::sin(az));
+ }
+
+ static constexpr Vector<Scalar> unit45 = Vector<Scalar>(-0.7071, 0.7071);
+
+public:
+ Vector<Scalar> &operator +=(Vector<Scalar> other) {
+ x += other.x;
+ y += other.y;
+ return *this;
+ }
+ Vector<Scalar> &operator -=(Vector<Scalar> 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<class T>
+constexpr Vector<T> Vector<T>::unit45;
+
+/// specialization with same layout as SDL_Point
+template<>
+class Vector<int>
+: public SDL_Point {
+
+public:
+ constexpr Vector() : SDL_Point({0, 0}) { }
+ constexpr Vector(int x, int y) : SDL_Point({x, y}) { }
+
+ template<class Other>
+ constexpr Vector(Vector<Other> other)
+ : SDL_Point({int(other.x), int(other.y)}) { }
+
+public:
+ Vector<int> &operator +=(Vector<int> other) {
+ x += other.x;
+ y += other.y;
+ return *this;
+ }
+ Vector<int> &operator -=(Vector<int> other) {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+ }
+
+ SDL_Point ToPoint() const {
+ return *this;
+ }
+
+};
+
+
+template<class Scalar>
+constexpr Vector<Scalar> operator -(Vector<Scalar> v) {
+ return Vector<Scalar>(-v.x, -v.y);
+}
+
+
+template<class Scalar>
+constexpr Vector<Scalar> operator +(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return Vector<Scalar>(lhs.x + rhs.x, lhs.y + rhs.y);
+}
+
+template<class Scalar>
+constexpr Vector<Scalar> operator -(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return Vector<Scalar>(lhs.x - rhs.x, lhs.y - rhs.y);
+}
+
+
+template<class Scalar>
+constexpr Vector<Scalar> operator *(Vector<Scalar> lhs, Scalar rhs) {
+ return Vector<Scalar>(lhs.x * rhs, lhs.y * rhs);
+}
+
+template<class Scalar>
+constexpr Vector<Scalar> operator *(Scalar lhs, Vector<Scalar> rhs) {
+ return rhs * lhs;
+}
+template<class Scalar>
+constexpr Vector<Scalar> operator *(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return Vector<Scalar>(lhs.x * rhs.x, lhs.y * rhs.y);
+}
+
+
+template<class Scalar>
+constexpr Vector<Scalar> operator /(Vector<Scalar> lhs, Scalar rhs) {
+ return Vector<Scalar>(lhs.x / rhs, lhs.y / rhs);
+}
+
+template<class Scalar>
+constexpr Vector<Scalar> operator /(Scalar lhs, Vector<Scalar> rhs) {
+ return rhs / lhs;
+}
+template<class Scalar>
+constexpr Vector<Scalar> operator /(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return Vector<Scalar>(lhs.x / rhs.x, lhs.y / rhs.y);
+}
+
+
+template<class Scalar>
+constexpr bool operator ==(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+template<class Scalar>
+constexpr bool operator !=(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return lhs.x != rhs.x && lhs.y != rhs.y;
+}
+
+
+template<class Scalar>
+constexpr Scalar Cross2D(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return (lhs.x * rhs.y) - (lhs.y * rhs.x);
+}
+template<class Scalar>
+constexpr Scalar Dot(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return (lhs.x * rhs.x) + (lhs.y * rhs.y);
+}
+template<class Scalar>
+constexpr Scalar Length(Vector<Scalar> v) {
+ return std::sqrt(Dot(v, v));
+}
+template<class Scalar>
+constexpr Vector<Scalar> Norm(Vector<Scalar> v) {
+ return v / Length(v);
+}
+
+template<class Scalar>
+constexpr Vector<Scalar> Rotate90(Vector<Scalar> v) {
+ return Vector<Scalar>(-v.y, v.x);
+}
+template<class Scalar>
+constexpr Vector<Scalar> Rotate180(Vector<Scalar> v) {
+ return -v;
+}
+template<class Scalar>
+constexpr Vector<Scalar> Rotate270(Vector<Scalar> v) {
+ return Vector<Scalar>(v.y, -v.x);
+}
+
+
+template<class Scalar>
+inline std::ostream &operator <<(std::ostream &out, Vector<Scalar> v) {
+ return out << '<' << v.x << ',' << v.y << '>';
+}
+
+}
+
+
+namespace std {
+
+template<class Scalar>
+constexpr gworm::Vector<Scalar> min(
+ gworm::Vector<Scalar> lhs,
+ gworm::Vector<Scalar> rhs
+) {
+ return gworm::Vector<Scalar>(
+ min(lhs.x, rhs.x),
+ min(lhs.y, rhs.y)
+ );
+}
+
+template<class Scalar>
+constexpr gworm::Vector<Scalar> max(
+ gworm::Vector<Scalar> lhs,
+ gworm::Vector<Scalar> rhs
+) {
+ return gworm::Vector<Scalar>(
+ max(lhs.x, rhs.x),
+ max(lhs.y, rhs.y)
+ );
+}
+
+}
+
+#endif
--- /dev/null
+#include "Window.h"
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+using std::runtime_error;
+
+
+namespace gworm {
+
+const Vector<int> Window::POS_CENTER(
+ SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+const Vector<int> Window::POS_UNDEF(
+ SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED);
+
+
+Window::Window(
+ const char *title,
+ Vector<int> pos,
+ Vector<int> 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<int> Window::Size() const {
+ assert(win);
+ Vector<int> size;
+ SDL_GetWindowSize(win, &size.x, &size.y);
+ return size;
+}
+
+
+Canvas Window::CreateCanvas(Uint32 flags) {
+ return Canvas(win, -1, flags);
+}
+
+}
--- /dev/null
+#ifndef GWORM_WINDOW_H_
+#define GWORM_WINDOW_H
+
+#include "Canvas.h"
+#include "Vector.h"
+
+#include <SDL.h>
+
+
+namespace gworm {
+
+class Window {
+
+public:
+ static const Vector<int> POS_CENTER;
+ static const Vector<int> POS_UNDEF;
+
+public:
+ Window() : win(nullptr) { }
+ Window(const char *title, Vector<int> pos, Vector<int> size, Uint32 flags);
+ ~Window();
+
+ Window(Window &&);
+ Window &operator =(Window &&);
+
+ Window(const Window &) = delete;
+ Window &operator =(const Window &) = delete;
+
+public:
+ Vector<int> Size() const;
+
+ Canvas CreateCanvas(Uint32 flags);
+
+private:
+ SDL_Window *win;
+
+};
+
+}
+
+#endif
--- /dev/null
+#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<class T>
+constexpr int sigma(T v) {
+ return v > 0 ? 1 : (v < 0 ? -1 : 0);
+}
+
+}
+
+#endif
--- /dev/null
+#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<int> center, int radius) {
+ const Vector<int> begin(0, 0);
+ const Vector<int> end(world.Size());
+ const int sqrad = radius * radius;
+
+ for (Vector<int> pos(begin); pos.y < end.y; ++pos.y) {
+ for (pos.x = 0; pos.x < end.x; ++pos.x) {
+ Vector<int> 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<int>(800, 600),
+ SDL_WINDOW_RESIZABLE
+ );
+ Canvas canv(win.CreateCanvas(
+ 0
+ ));
+
+ World world(Vector<int>(500, 500));
+ make_planet(world, Vector<int>(250, 250), 220);
+
+ Application app(canv, world);
+ app.Run();
+
+ return 0;
+}
--- /dev/null
+#include "World.h"
+
+#include "../graphics/const.h"
+
+
+namespace gworm {
+
+World::World(Vector<int> size)
+: size(size)
+, count(size.x * size.y)
+, masses(count, 1000000000.0f)
+, colors(count, Color(0x7F, 0x7F, 0x7F)) {
+
+}
+
+
+void World::Update(float dt) {
+
+}
+
+
+Vector<float> World::ForceAt(Vector<float> p1, float m1) const {
+ Vector<float> force(0, 0);
+
+ for (int i = 0; i < count; ++i) {
+ const Vector<float> p2(i % size.y, i / size.y);
+ if (p1 == p2) continue;
+
+ const Vector<float> diff(p2 - p1);
+ const Vector<float> 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;
+}
+
+}
--- /dev/null
+#ifndef GWORM_WORLD_H_
+#define GWORM_WORLD_H_
+
+#include "../graphics/Color.h"
+#include "../graphics/Vector.h"
+
+#include <vector>
+
+
+namespace gworm {
+
+class World {
+
+public:
+ World(Vector<int> size);
+
+public:
+ Vector<int> Size() const { return size; }
+
+public:
+ void Update(float dt);
+
+ int Index(Vector<int> pos) const { return pos.x * size.y + pos.y; }
+
+ float MassAt(Vector<int> pos) const { return masses[Index(pos)]; }
+ void SetMass(Vector<int> pos, float m) { masses[Index(pos)] = m; }
+ Color ColorAt(Vector<int> pos) const { return colors[Index(pos)]; }
+ void SetColor(Vector<int> pos, Color c) { colors[Index(pos)] = c; }
+
+ Vector<float> ForceAt(Vector<float>, float m) const;
+
+private:
+ Vector<int> size;
+ int count;
+
+ std::vector<float> masses;
+ std::vector<Color> colors;
+
+};
+
+}
+
+#endif