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