1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17/* Basic classes for rectangles */
18
19#include "C4Rect.h"
20
21#include "StdAdaptors.h"
22#include "StdCompiler.h"
23
24void C4Rect::Default()
25{
26 x = y = Wdt = Hgt = 0;
27}
28
29void C4Rect::CompileFunc(StdCompiler *pComp)
30{
31 pComp->Value(rStruct: mkDefaultAdapt(rValue&: x, rDefault: 0)); pComp->Separator();
32 pComp->Value(rStruct: mkDefaultAdapt(rValue&: y, rDefault: 0)); pComp->Separator();
33 pComp->Value(rStruct: mkDefaultAdapt(rValue&: Wdt, rDefault: 0)); pComp->Separator();
34 pComp->Value(rStruct: mkDefaultAdapt(rValue&: Hgt, rDefault: 0));
35}
36
37C4Rect C4Rect::Scaled(float scale) const noexcept
38{
39 const auto scaled = [scale](int32_t val)
40 {
41 return static_cast<int32_t>(static_cast<float>(val) * scale);
42 };
43 return {scaled(x), scaled(y), scaled(Wdt), scaled(Hgt)};
44}
45
46void C4TargetRect::Default()
47{
48 C4Rect::Default();
49 tx = ty = 0;
50}
51
52void C4TargetRect::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iTX, int32_t iTY)
53{
54 C4Rect::Set(iX, iY, iWdt, iHgt);
55 tx = iTX; ty = iTY;
56}
57
58bool C4TargetRect::ClipBy(C4TargetRect &rClip)
59{
60 int32_t d;
61 // clip left
62 if ((d = x - rClip.x) < 0) { Wdt += d; x = rClip.x; }
63 else tx += d;
64 // clip top
65 if ((d = y - rClip.y) < 0) { Hgt += d; y = rClip.y; }
66 else ty += d;
67 // clip right
68 if ((d = (x + Wdt - rClip.x - rClip.Wdt)) > 0) Wdt -= d;
69 // clip bottom
70 if ((d = (y + Hgt - rClip.y - rClip.Hgt)) > 0) Hgt -= d;
71 // check validity
72 if (Wdt <= 0 || Hgt <= 0) return false;
73 // add target pos
74 tx += rClip.tx;
75 ty += rClip.ty;
76 // done
77 return true;
78}
79
80void C4TargetRect::CompileFunc(StdCompiler *pComp)
81{
82 C4Rect::CompileFunc(pComp); pComp->Separator();
83 pComp->Value(rStruct: mkDefaultAdapt(rValue&: tx, rDefault: 0)); pComp->Separator();
84 pComp->Value(rStruct: mkDefaultAdapt(rValue&: ty, rDefault: 0));
85}
86
87void C4Rect::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
88{
89 x = iX; y = iY; Wdt = iWdt; Hgt = iHgt;
90}
91
92bool C4Rect::Overlap(C4Rect &rTarget)
93{
94 if (x + Wdt <= rTarget.x) return false;
95 if (x >= rTarget.x + rTarget.Wdt) return false;
96 if (y + Hgt <= rTarget.y) return false;
97 if (y >= rTarget.y + rTarget.Hgt) return false;
98 return true;
99}
100
101void C4Rect::Intersect(const C4Rect &r2)
102{
103 // Narrow bounds
104 if (r2.x > x)
105 if (r2.x + r2.Wdt < x + Wdt)
106 {
107 x = r2.x; Wdt = r2.Wdt;
108 }
109 else
110 {
111 Wdt -= (r2.x - x); x = r2.x;
112 }
113 else if (r2.x + r2.Wdt < x + Wdt)
114 Wdt = r2.x + r2.Wdt - x;
115 if (r2.y > y)
116 if (r2.y + r2.Hgt < y + Hgt)
117 {
118 y = r2.y; Hgt = r2.Hgt;
119 }
120 else
121 {
122 Hgt -= (r2.y - y); y = r2.y;
123 }
124 else if (r2.y + r2.Hgt < y + Hgt)
125 Hgt = r2.y + r2.Hgt - y;
126 // Degenerated? Will happen when the two rects don't overlap
127 if (Wdt < 0) Wdt = 0;
128 if (Hgt < 0) Hgt = 0;
129}
130
131bool C4Rect::IntersectsLine(int32_t iX, int32_t iY, int32_t iX2, int32_t iY2)
132{
133 // Easy cases first
134 if (Contains(iX, iY)) return true;
135 if (Contains(iX: iX2, iY: iY2)) return true;
136 if (iX < x && iX2 < x) return false;
137 if (iY < y && iY2 < y) return false;
138 if (iX >= x + Wdt && iX2 >= x + Wdt) return false;
139 if (iY >= y + Hgt && iY2 >= y + Hgt) return false;
140 // check some special cases
141 if (iX == iX2 || iY == iY2) return true;
142 // Check intersection left/right
143 int32_t iXI, iYI;
144 iXI = (iX < x ? x : x + Wdt);
145 iYI = iY + (iY2 - iY) * (iXI - iX) / (iX2 - iX);
146 if (iYI >= y && iYI < y + Hgt) return true;
147 // Check intersection up/down
148 iYI = (iY < y ? y : y + Hgt);
149 iXI = iX + (iX2 - iX) * (iYI - iY) / (iY2 - iY);
150 return iXI >= x && iXI < x + Wdt;
151}
152
153void C4Rect::Add(const C4Rect &r2)
154{
155 // Null? Don't do anything
156 if (!r2.Wdt || !r2.Hgt) return;
157 if (!Wdt || !Hgt)
158 {
159 *this = r2;
160 return;
161 }
162 // Expand bounds
163 if (r2.x < x)
164 if (r2.x + r2.Wdt > x + Wdt)
165 {
166 x = r2.x; Wdt = r2.Wdt;
167 }
168 else
169 {
170 Wdt += (x - r2.x); x = r2.x;
171 }
172 else if (r2.x + r2.Wdt > x + Wdt)
173 Wdt = r2.x + r2.Wdt - x;
174 if (r2.y < y)
175 if (r2.y + r2.Hgt > y + Hgt)
176 {
177 y = r2.y; Hgt = r2.Hgt;
178 }
179 else
180 {
181 Hgt += (y - r2.y); y = r2.y;
182 }
183 else if (r2.y + r2.Hgt > y + Hgt)
184 Hgt = r2.y + r2.Hgt - y;
185}
186
187// C4RectList
188
189void C4RectList::ClipByRect(const C4Rect &rClip)
190{
191 // split up all rectangles
192 for (size_t i = 0; i < GetCount(); ++i)
193 {
194 C4Rect *pTarget = &Get(idx: i);
195 // any overlap?
196 if (rClip.x + rClip.Wdt <= pTarget->x) continue;
197 if (rClip.y + rClip.Hgt <= pTarget->y) continue;
198 if (rClip.x >= pTarget->x + pTarget->Wdt) continue;
199 if (rClip.y >= pTarget->y + pTarget->Hgt) continue;
200 // okay; split up rectangle
201 // first split will just reduce the target rectangle size
202 // if more splits are done, additional rectangles need to be added
203 size_t iSplitCount = 0, iOver; C4Rect rcThis(*pTarget);
204 // clipped by right side
205 if ((iOver = rcThis.x + rcThis.Wdt - rClip.x - rClip.Wdt) > 0)
206 {
207 pTarget->x += pTarget->Wdt - iOver; pTarget->Wdt = iOver; rcThis.Wdt -= iOver;
208 ++iSplitCount;
209 }
210 // clipped by obttom side
211 if ((iOver = rcThis.y + rcThis.Hgt - rClip.y - rClip.Hgt) > 0)
212 {
213 if (iSplitCount) { AddRect(rNewRect: rcThis); pTarget = &Get(idx: GetCount() - 1); }
214 pTarget->y += pTarget->Hgt - iOver; pTarget->Hgt = iOver; rcThis.Hgt -= iOver;
215 ++iSplitCount;
216 }
217 // clipped by left side
218 if ((iOver = rClip.x - rcThis.x) > 0)
219 {
220 if (iSplitCount) { AddRect(rNewRect: rcThis); pTarget = &Get(idx: GetCount() - 1); }
221 pTarget->Wdt = iOver; rcThis.Wdt -= iOver; rcThis.x = rClip.x;
222 ++iSplitCount;
223 }
224 // clipped by top side
225 if ((iOver = rClip.y - rcThis.y) > 0)
226 {
227 if (iSplitCount) { AddRect(rNewRect: rcThis); pTarget = &Get(idx: GetCount() - 1); }
228 else ++iSplitCount;
229 pTarget->Hgt = iOver;
230 }
231 // nothing split? This means this rectnagle is completely contained
232 if (!iSplitCount)
233 {
234 // make it vanish
235 RemoveIndexedRect(idx: i); --i;
236 }
237 }
238 // concat rectangles if possible
239 bool fDone = false;
240 while (!fDone)
241 {
242 fDone = true;
243 for (size_t i = 0, cnt = GetCount(); i < cnt && fDone; ++i)
244 {
245 C4Rect &rc1 = Get(idx: i);
246 for (size_t j = i + 1; j < cnt; ++j)
247 {
248 C4Rect &rc2 = Get(idx: j);
249 if (rc1.y == rc2.y && rc1.Hgt == rc2.Hgt)
250 {
251 if (rc1.x + rc1.Wdt == rc2.x)
252 {
253 rc1.Wdt += rc2.Wdt; RemoveIndexedRect(idx: j); fDone = false; break;
254 }
255 else if (rc2.x + rc2.Wdt == rc1.x)
256 {
257 rc2.Wdt += rc1.Wdt; RemoveIndexedRect(idx: i); fDone = false; break;
258 }
259 }
260 else if (rc1.x == rc2.x && rc1.Wdt == rc2.Wdt)
261 {
262 if (rc1.y + rc1.Hgt == rc2.y)
263 {
264 rc1.Hgt += rc2.Hgt; RemoveIndexedRect(idx: j); fDone = false; break;
265 }
266 else if (rc2.y + rc2.Hgt == rc1.y)
267 {
268 rc2.Hgt += rc1.Hgt; RemoveIndexedRect(idx: i); fDone = false; break;
269 }
270 }
271 }
272 }
273 }
274}
275