]> git.localhorst.tv Git - orbi.git/blob - src/graphics/Canvas.cpp
rotatable arm for character entities
[orbi.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 orbi {
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 Texture Canvas::CreateStaticTexture(Vector<int> size) {
49         return Texture(canv, Color::Format, SDL_TEXTUREACCESS_STATIC, size);
50 }
51
52 Texture Canvas::LoadTexture(const char *file) {
53         return Texture(canv, file);
54 }
55
56
57 void Canvas::Copy(Texture &tex, Vector<int> to) {
58         tex.Copy(canv, to);
59 }
60
61 void Canvas::Copy(Texture &tex, Rect<int> clip, Vector<int> to) {
62         tex.Copy(canv, clip, to);
63 }
64
65
66 void Canvas::SetColor(Color c) {
67         SDL_SetRenderDrawColor(canv, c.r, c.g, c.b, c.a);
68 }
69
70
71 void Canvas::Fill() {
72         SDL_RenderClear(canv);
73 }
74
75 void Canvas::Outline() {
76         SDL_RenderDrawRect(canv, nullptr);
77 }
78
79
80 void Canvas::Line(Vector<int> from, Vector<int> to) {
81         SDL_RenderDrawLine(canv, from.x, from.y, to.x, to.y);
82 }
83
84 void Canvas::FillRect(Rect<int> rect) {
85         SDL_RenderFillRect(canv, &rect);
86 }
87
88 void Canvas::OutlineRect(Rect<int> rect) {
89         SDL_RenderDrawRect(canv, &rect);
90 }
91
92 void Canvas::OutlineRectRot(Rect<float> rect, Vector<float> origin, float rot) {
93         const Vector<float> topLeft(rect.x, rect.y);
94         const Vector<float> topRight(rect.x + rect.w - 1, rect.y);
95         const Vector<float> botLeft(rect.x, rect.y + rect.h - 1);
96         const Vector<float> botRight(rect.x + rect.w - 1, rect.y + rect.h - 1);
97
98         Quad(
99                 Rotate(topLeft - origin, rot) + origin,
100                 Rotate(topRight - origin, rot) + origin,
101                 Rotate(botRight - origin, rot) + origin,
102                 Rotate(botLeft - origin, rot) + origin
103         );
104 }
105
106
107 void Canvas::Dot(Vector<int> pos) {
108         SDL_RenderDrawPoint(canv, pos.x, pos.y);
109 }
110
111 void Canvas::Cross(Vector<int> pos, int extent) {
112         Line(
113                 Vector<int>(pos.x - extent, pos.y),
114                 Vector<int>(pos.x + extent, pos.y));
115         Line(
116                 Vector<int>(pos.x, pos.y - extent),
117                 Vector<int>(pos.x, pos.y + extent));
118 }
119
120 void Canvas::Arrow(Vector<int> from, Vector<int> to) {
121         Line(from, to);
122         Vector<float> delta(to - from);
123         delta = delta / Length(delta);
124
125         Line(to, to + Vector<int>(Rotate90(delta) * 5.0f - (delta * 5.0f)));
126         Line(to, to + Vector<int>(Rotate270(delta) * 5.0f - (delta * 5.0f)));
127 }
128
129 void Canvas::Triangle(Vector<int> v1, Vector<int> v2, Vector<int> v3) {
130         SDL_Point points[4] = { v1, v2, v3, v1 };
131         SDL_RenderDrawLines(canv, points, 4);
132 }
133
134 void Canvas::Quad(Vector<int> v1, Vector<int> v2, Vector<int> v3, Vector<int> v4) {
135         SDL_Point points[5] = { v1, v2, v3, v4, v1 };
136         SDL_RenderDrawLines(canv, points, 5);
137 }
138
139
140 namespace {
141
142 template<class Scalar>
143 void GridImpl(
144                 Canvas &canv,
145                 Vector<int> pos,
146                 Vector<int> size,
147                 Vector<Scalar> step) {
148         Vector<int> from(pos);
149         Vector<int> to(pos + size);
150         Vector<int> clip(canv.Size());
151
152         if (from.x > clip.x || from.y > clip.y || to.x < 0 || to.y < 0) {
153                 return;
154         }
155         if (step.x <= 1 || step.y <= 1) {
156                 canv.FillRect(pos, size);
157                 return;
158         }
159
160         if (from.x < -step.x) {
161                 int skip = from.x / -step.x;
162                 from.x += skip * step.x;
163         }
164         if (from.y < -step.y) {
165                 int skip = from.y / -step.y;
166                 from.y += skip * step.y;
167         }
168         if (to.x > clip.x + step.x) {
169                 int skip = (to.x - clip.x) / step.x;
170                 to.x -= skip * step.x;
171         }
172         if (to.y > clip.y + step.y) {
173                 int skip = (to.y - clip.y) / step.y;
174                 to.y -= skip * step.y;
175         }
176
177         int width = to.x - from.x;
178         int height = to.y - from.y;
179
180         for (Vector<Scalar> pos(from); pos.x <= to.x; pos.x += step.x) {
181                 canv.Line(pos, Vector<int>(pos.x, pos.y + height));
182         }
183         for (Vector<Scalar> pos(from); pos.y <= to.y; pos.y += step.y) {
184                 canv.Line(pos, Vector<int>(pos.x + width, pos.y));
185         }
186 }
187
188 template<class Scalar>
189 void Grid2Impl(
190                 Canvas &canv,
191                 Vector<int> pos,
192                 Vector<int> size,
193                 Vector<Scalar> step,
194                 Vector<int> n,
195                 Color c1,
196                 Color c2) {
197         Vector<int> from(pos);
198         Vector<int> to(pos + size);
199         Vector<int> clip(canv.Size());
200
201         if (from.x > clip.x || from.y > clip.y || to.x < 0 || to.y < 0) {
202                 return;
203         }
204         if (step.x <= 1 || step.y <= 1) {
205                 canv.SetColor(c1);
206                 canv.FillRect(pos, size);
207                 canv.SetColor(c2);
208                 GridImpl(canv, pos, size, step * Vector<Scalar>(n));
209                 return;
210         }
211
212         Vector<int> i(0, 0);
213
214         if (from.x < -step.x) {
215                 int skip = from.x / -step.x;
216                 from.x += skip * step.x;
217                 i.x += skip;
218         }
219         if (from.y < -step.y) {
220                 int skip = from.y / -step.y;
221                 from.y += skip * step.y;
222                 i.y += skip;
223         }
224         if (to.x > clip.x + step.x) {
225                 int skip = (to.x - clip.x) / step.x;
226                 to.x -= skip * step.x;
227         }
228         if (to.y > clip.y + step.y) {
229                 int skip = (to.y - clip.y) / step.y;
230                 to.y -= skip * step.y;
231         }
232
233         int width = to.x - from.x;
234         int height = to.y - from.y;
235
236         for (Vector<Scalar> pos(from); pos.x <= to.x; pos.x += step.x) {
237                 canv.SetColor((i.x++ % n.x) ? c1 : c2);
238                 canv.Line(pos, Vector<int>(pos.x, pos.y + height));
239         }
240         for (Vector<Scalar> pos(from); pos.y <= to.y; pos.y += step.y) {
241                 canv.SetColor((i.y++ % n.y) ? c1 : c2);
242                 canv.Line(pos, Vector<int>(pos.x + width, pos.y));
243         }
244 }
245
246 }
247
248 void Canvas::Grid(Vector<int> pos, Vector<int> size, Vector<int> step) {
249         GridImpl(*this, pos, size, step);
250 }
251
252 void Canvas::Grid(Vector<int> pos, Vector<int> size, Vector<float> step) {
253         GridImpl(*this, pos, size, step);
254 }
255
256 void Canvas::Grid2(
257                 Vector<int> pos, Vector<int> size, Vector<int> step,
258                 Vector<int> n, Color c1, Color c2) {
259         Grid2Impl(*this, pos, size, step, n, c1, c2);
260 }
261
262 void Canvas::Grid2(
263                 Vector<int> pos, Vector<int> size, Vector<float> step,
264                 Vector<int> n, Color c1, Color c2) {
265         Grid2Impl(*this, pos, size, step, n, c1, c2);
266 }
267
268 }