1 /*
2 C-Dogs SDL
3 A port of the legendary (and fun) action/arcade cdogs.
4 Copyright (c) 2013-2014, 2017, 2020 Cong Xu
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include "algorithms.h"
29
30 #include <math.h>
31
32 #include "c_array.h"
33 #include "c_hashmap/hashmap.h"
34 #include "log.h"
35
36 typedef struct
37 {
38 // Whether to use the IsBlocked func to check visibility,
39 // and also whether to early-terminate if the line is blocked
40 bool CheckBlockedAndEarlyTerminate;
41 bool (*IsBlocked)(void *, struct vec2i);
42 void (*OnPoint)(void *, struct vec2i);
43 void *data;
44 } AlgoLineData;
BresenhamLine(struct vec2i from,struct vec2i to,AlgoLineData * data)45 static bool BresenhamLine(
46 struct vec2i from, struct vec2i to, AlgoLineData *data)
47 {
48 struct vec2i d = svec2i(abs(to.x - from.x), abs(to.y - from.y));
49 struct vec2i s = svec2i(from.x < to.x ? 1 : -1, from.y < to.y ? 1 : -1);
50 int err = d.x - d.y;
51 struct vec2i v = from;
52 for (;;)
53 {
54 int e2 = 2 * err;
55 if (svec2i_is_equal(v, to))
56 {
57 break;
58 }
59 if (data->CheckBlockedAndEarlyTerminate)
60 {
61 if (data->IsBlocked(data->data, v))
62 {
63 return false;
64 }
65 }
66 else
67 {
68 data->OnPoint(data->data, v);
69 }
70 if (e2 > -d.y)
71 {
72 err -= d.y;
73 v.x += s.x;
74 }
75 if (svec2i_is_equal(v, to))
76 {
77 break;
78 }
79 if (e2 < d.x)
80 {
81 err += d.x;
82 v.y += s.y;
83 }
84 }
85 if (data->CheckBlockedAndEarlyTerminate)
86 {
87 return !data->IsBlocked(data->data, v);
88 }
89 else
90 {
91 data->OnPoint(data->data, v);
92 }
93 return true;
94 }
95
96 // From "Raytracing on a grid" by James McNeill
97 // http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
98 // "The code snippets are free to use as you see fit and no attribution is
99 // necessary"
JMRaytrace(int x0,int y0,int x1,int y1,AlgoLineData * data)100 static bool JMRaytrace(int x0, int y0, int x1, int y1, AlgoLineData *data)
101 {
102 int dx = abs(x1 - x0);
103 int dy = abs(y1 - y0);
104 int x = x0;
105 int y = y0;
106 int n = 1 + dx + dy;
107 int x_inc = (x1 > x0) ? 1 : -1;
108 int y_inc = (y1 > y0) ? 1 : -1;
109 int error = dx - dy;
110 dx *= 2;
111 dy *= 2;
112
113 for (; n > 0; --n)
114 {
115 const struct vec2i v = svec2i(x, y);
116 if (data->CheckBlockedAndEarlyTerminate)
117 {
118 if (data->IsBlocked(data->data, v))
119 {
120 return false;
121 }
122 }
123 else
124 {
125 data->OnPoint(data->data, v);
126 }
127
128 if (error > 0)
129 {
130 x += x_inc;
131 error -= dy;
132 }
133 else
134 {
135 y += y_inc;
136 error += dx;
137 }
138 }
139 return true;
140 }
141
HasClearLineBresenham(struct vec2i from,struct vec2i to,HasClearLineData * data)142 bool HasClearLineBresenham(
143 struct vec2i from, struct vec2i to, HasClearLineData *data)
144 {
145 AlgoLineData bData;
146 bData.CheckBlockedAndEarlyTerminate = true;
147 bData.IsBlocked = data->IsBlocked;
148 bData.data = data->data;
149 return BresenhamLine(from, to, &bData);
150 }
HasClearLineJMRaytrace(const struct vec2i from,const struct vec2i to,HasClearLineData * data)151 bool HasClearLineJMRaytrace(
152 const struct vec2i from, const struct vec2i to, HasClearLineData *data)
153 {
154 AlgoLineData bData;
155 bData.CheckBlockedAndEarlyTerminate = true;
156 bData.IsBlocked = data->IsBlocked;
157 bData.data = data->data;
158 return JMRaytrace(from.x, from.y, to.x, to.y, &bData);
159 }
160
BresenhamLineDraw(struct vec2i from,struct vec2i to,AlgoLineDrawData * data)161 void BresenhamLineDraw(
162 struct vec2i from, struct vec2i to, AlgoLineDrawData *data)
163 {
164 AlgoLineData bData;
165 bData.CheckBlockedAndEarlyTerminate = false;
166 bData.OnPoint = data->Draw;
167 bData.data = data->data;
168 BresenhamLine(from, to, &bData);
169 }
JMRaytraceLineDraw(const struct vec2i from,const struct vec2i to,AlgoLineDrawData * data)170 void JMRaytraceLineDraw(
171 const struct vec2i from, const struct vec2i to, AlgoLineDrawData *data)
172 {
173 AlgoLineData bData;
174 bData.CheckBlockedAndEarlyTerminate = false;
175 bData.OnPoint = data->Draw;
176 bData.data = data->data;
177 JMRaytrace(from.x, from.y, to.x, to.y, &bData);
178 }
179
180 static bool TryAddFloodFillNeighbor(
181 CArray *q, map_t seen, const FloodFillData *data, const struct vec2i v);
CFloodFill(const struct vec2i v,FloodFillData * data)182 bool CFloodFill(const struct vec2i v, FloodFillData *data)
183 {
184 bool result = false;
185 // Use BFS queue
186 CArray q;
187 CArrayInit(&q, sizeof v);
188 map_t seen = hashmap_new();
189 if (TryAddFloodFillNeighbor(&q, seen, data, v))
190 {
191 result = true;
192 }
193 while (q.size > 0)
194 {
195 const struct vec2i qv = *(struct vec2i *)CArrayGet(&q, 0);
196 data->Fill(data->data, qv);
197 TryAddFloodFillNeighbor(&q, seen, data, svec2i(qv.x - 1, qv.y));
198 TryAddFloodFillNeighbor(&q, seen, data, svec2i(qv.x + 1, qv.y));
199 TryAddFloodFillNeighbor(&q, seen, data, svec2i(qv.x, qv.y - 1));
200 TryAddFloodFillNeighbor(&q, seen, data, svec2i(qv.x, qv.y + 1));
201 CArrayDelete(&q, 0);
202 }
203 CArrayTerminate(&q);
204 hashmap_free(seen);
205 return result;
206 }
TryAddFloodFillNeighbor(CArray * q,map_t seen,const FloodFillData * data,const struct vec2i v)207 static bool TryAddFloodFillNeighbor(
208 CArray *q, map_t seen, const FloodFillData *data, const struct vec2i v)
209 {
210 char buf[256];
211 sprintf(buf, "%d,%d", v.x, v.y);
212 if (data->IsSame(data->data, v) &&
213 hashmap_get(seen, buf, NULL) == MAP_MISSING)
214 {
215 CArrayPushBack(q, &v);
216 if (hashmap_put(seen, buf, NULL) != MAP_OK)
217 {
218 LOG(LM_MAIN, LL_ERROR, "failed to add key to seen set (%s)", buf);
219 }
220 return true;
221 }
222 return false;
223 }
224