]> git.localhorst.tv Git - space.git/blob - src/graphics/Canvas.cpp
6241ab30dcb00844154406e5a753706fd8b1f2df
[space.git] / src / graphics / Canvas.cpp
1 #include "Canvas.h"
2
3 #include <algorithm>
4 #include <cassert>
5 #include <stdexcept>
6 #include <string>
7
8 using std::runtime_error;
9
10
11 namespace space {
12
13 Canvas::Canvas(SDL_Window *win, int index, Uint32 flags)
14 : canv(SDL_CreateRenderer(win, index, flags)) {
15         if (!canv) {
16                 throw runtime_error(std::string("create canvas: ") + SDL_GetError());
17         }
18 }
19
20 Canvas::~Canvas() {
21         if (canv) SDL_DestroyRenderer(canv);
22 }
23
24 Canvas::Canvas(Canvas &&other)
25 : canv(other.canv) {
26         other.canv = nullptr;
27 }
28
29 Canvas &Canvas::operator =(Canvas &&other) {
30         std::swap(canv, other.canv);
31         return *this;
32 }
33
34
35 void Canvas::Present() {
36         SDL_RenderPresent(canv);
37 }
38
39
40 Vector<int> Canvas::Size() const {
41         assert(canv);
42         Vector<int> size;
43         SDL_GetRendererOutputSize(canv, &size.x, &size.y);
44         return size;
45 }
46
47
48 void Canvas::SetColor(Color c) {
49         SDL_SetRenderDrawColor(canv, c.r, c.g, c.b, c.a);
50 }
51
52
53 void Canvas::Fill() {
54         SDL_RenderClear(canv);
55 }
56
57 void Canvas::Outline() {
58         SDL_RenderDrawRect(canv, nullptr);
59 }
60
61
62 void Canvas::Line(Vector<int> from, Vector<int> to) {
63         SDL_RenderDrawLine(canv, from.x, from.y, to.x, to.y);
64 }
65
66 void Canvas::FillRect(Vector<int> pos, Vector<int> size) {
67         SDL_Rect destRect;
68         destRect.x = pos.x;
69         destRect.y = pos.y;
70         destRect.w = size.x;
71         destRect.h = size.y;
72         SDL_RenderFillRect(canv, &destRect);
73 }
74
75 void Canvas::OutlineRect(Vector<int> pos, Vector<int> size) {
76         SDL_Rect destRect;
77         destRect.x = pos.x;
78         destRect.y = pos.y;
79         destRect.w = size.x;
80         destRect.h = size.y;
81         SDL_RenderDrawRect(canv, &destRect);
82 }
83
84
85 void Canvas::Dot(Vector<int> pos) {
86         SDL_RenderDrawPoint(canv, pos.x, pos.y);
87 }
88
89 void Canvas::Cross(Vector<int> pos, int extent) {
90         Line(
91                 Vector<int>(pos.x - extent, pos.y),
92                 Vector<int>(pos.x + extent, pos.y));
93         Line(
94                 Vector<int>(pos.x, pos.y - extent),
95                 Vector<int>(pos.x, pos.y + extent));
96 }
97
98 void Canvas::Arrow(Vector<int> from, Vector<int> to) {
99         Line(from, to);
100         Vector<float> delta(to - from);
101         delta = delta / Length(delta);
102
103         Line(to, to + Vector<int>(Rotate90(delta) * 5.0f - (delta * 5.0f)));
104         Line(to, to + Vector<int>(Rotate270(delta) * 5.0f - (delta * 5.0f)));
105 }
106
107 void Canvas::Triangle(Vector<int> v1, Vector<int> v2, Vector<int> v3) {
108         SDL_Point points[4] = { v1, v2, v3, v1 };
109         SDL_RenderDrawLines(canv, points, 4);
110 }
111
112 void Canvas::Quad(Vector<int> v1, Vector<int> v2, Vector<int> v3, Vector<int> v4) {
113         SDL_Point points[5] = { v1, v2, v3, v4, v1 };
114         SDL_RenderDrawLines(canv, points, 5);
115 }
116
117
118 namespace {
119
120 template<class Scalar>
121 void GridImpl(
122                 Canvas &canv,
123                 Vector<int> pos,
124                 Vector<int> size,
125                 Vector<Scalar> step) {
126         Vector<int> from(pos);
127         Vector<int> to(pos + size);
128         Vector<int> clip(canv.Size());
129
130         if (from.x > clip.x || from.y > clip.y || to.x < 0 || to.y < 0) {
131                 return;
132         }
133         if (step.x <= 1 || step.y <= 1) {
134                 canv.FillRect(pos, size);
135                 return;
136         }
137
138         if (from.x < -step.x) {
139                 int skip = from.x / -step.x;
140                 from.x += skip * step.x;
141         }
142         if (from.y < -step.y) {
143                 int skip = from.y / -step.y;
144                 from.y += skip * step.y;
145         }
146         if (to.x > clip.x + step.x) {
147                 int skip = (to.x - clip.x) / step.x;
148                 to.x -= skip * step.x;
149         }
150         if (to.y > clip.y + step.y) {
151                 int skip = (to.y - clip.y) / step.y;
152                 to.y -= skip * step.y;
153         }
154
155         int width = to.x - from.x;
156         int height = to.y - from.y;
157
158         for (Vector<Scalar> pos(from); pos.x <= to.x; pos.x += step.x) {
159                 canv.Line(pos, Vector<int>(pos.x, pos.y + height));
160         }
161         for (Vector<Scalar> pos(from); pos.y <= to.y; pos.y += step.y) {
162                 canv.Line(pos, Vector<int>(pos.x + width, pos.y));
163         }
164 }
165
166 template<class Scalar>
167 void Grid2Impl(
168                 Canvas &canv,
169                 Vector<int> pos,
170                 Vector<int> size,
171                 Vector<Scalar> step,
172                 Vector<int> n,
173                 Color c1,
174                 Color c2) {
175         Vector<int> from(pos);
176         Vector<int> to(pos + size);
177         Vector<int> clip(canv.Size());
178
179         if (from.x > clip.x || from.y > clip.y || to.x < 0 || to.y < 0) {
180                 return;
181         }
182         if (step.x <= 1 || step.y <= 1) {
183                 canv.SetColor(c1);
184                 canv.FillRect(pos, size);
185                 canv.SetColor(c2);
186                 GridImpl(canv, pos, size, step);
187                 return;
188         }
189
190         Vector<int> i(0, 0);
191
192         if (from.x < -step.x) {
193                 int skip = from.x / -step.x;
194                 from.x += skip * step.x;
195                 i.x += skip;
196         }
197         if (from.y < -step.y) {
198                 int skip = from.y / -step.y;
199                 from.y += skip * step.y;
200                 i.y += skip;
201         }
202         if (to.x > clip.x + step.x) {
203                 int skip = (to.x - clip.x) / step.x;
204                 to.x -= skip * step.x;
205         }
206         if (to.y > clip.y + step.y) {
207                 int skip = (to.y - clip.y) / step.y;
208                 to.y -= skip * step.y;
209         }
210
211         int width = to.x - from.x;
212         int height = to.y - from.y;
213
214         for (Vector<Scalar> pos(from); pos.x <= to.x; pos.x += step.x) {
215                 canv.SetColor((i.x++ % n.x) ? c1 : c2);
216                 canv.Line(pos, Vector<int>(pos.x, pos.y + height));
217         }
218         for (Vector<Scalar> pos(from); pos.y <= to.y; pos.y += step.y) {
219                 canv.SetColor((i.y++ % n.y) ? c1 : c2);
220                 canv.Line(pos, Vector<int>(pos.x + width, pos.y));
221         }
222 }
223
224 }
225
226 void Canvas::Grid(Vector<int> pos, Vector<int> size, Vector<int> step) {
227         GridImpl(*this, pos, size, step);
228 }
229
230 void Canvas::Grid(Vector<int> pos, Vector<int> size, Vector<float> step) {
231         GridImpl(*this, pos, size, step);
232 }
233
234 void Canvas::Grid2(
235                 Vector<int> pos, Vector<int> size, Vector<int> step,
236                 Vector<int> n, Color c1, Color c2) {
237         Grid2Impl(*this, pos, size, step, n, c1, c2);
238 }
239
240 void Canvas::Grid2(
241                 Vector<int> pos, Vector<int> size, Vector<float> step,
242                 Vector<int> n, Color c1, Color c2) {
243         Grid2Impl(*this, pos, size, step, n, c1, c2);
244 }
245
246 }