1/*
2 * Copyright notice...
3 */
4
5#include "ctwm.h"
6
7#include <stdlib.h>
8#include <stdio.h>
9
10#include "r_area.h"
11#include "r_area_list.h"
12#include "util.h"
13
14
15/**
16 * Construct an RArea from given components.
17 */
18RArea
19RAreaNew(int x, int y, int width, int height)
20{
21	RArea area = { x, y, width, height };
22	return area;
23}
24
25
26/**
27 * Return a pointer to a static newly constructed RArea.
28 *
29 * This is a thin wrapper around RAreaNew() that returns a static
30 * pointer.  This is used in places that need to take RArea pointers, but
31 * we don't want to futz with making local intermediate vars.  Currently
32 * exclusively used inline in RAreaListNew() calls.
33 */
34RArea *
35RAreaNewStatic(int x, int y, int width, int height)
36{
37	static RArea area;
38	area = RAreaNew(x, y, width, height);
39	return &area;
40}
41
42
43/**
44 * Return a facially-invalid RArea.
45 *
46 * This is used in places that need a sentinel value.
47 */
48RArea
49RAreaInvalid(void)
50{
51	RArea area = { -1, -1, -1, -1 };
52	return area;
53}
54
55
56/**
57 * Is an RArea facially valid?
58 *
59 * Mostly used to check against sentinel values in places that may or may
60 * not have a real value to work with.
61 */
62bool
63RAreaIsValid(const RArea *self)
64{
65	return self->width >= 0 && self->height >= 0;
66}
67
68
69/**
70 * Return the right edge of an RArea.
71 */
72int
73RAreaX2(const RArea *self)
74{
75	return self->x + self->width - 1;
76}
77
78
79/**
80 * Return the bottom edge of an RArea.
81 */
82int
83RAreaY2(const RArea *self)
84{
85	return self->y + self->height - 1;
86}
87
88
89/**
90 * Return the area of an RArea.
91 */
92int
93RAreaArea(const RArea *self)
94{
95	return self->width * self->height;
96}
97
98
99/**
100 * Return an RArea describing the intersection of two RArea's.
101 */
102RArea
103RAreaIntersect(const RArea *self, const RArea *other)
104{
105	// Do they even intersect?
106	if(RAreaIsIntersect(self, other)) {
107		int x1, x2, y1, y2;
108
109		x1 = max(other->x, self->x);
110		x2 = min(RAreaX2(other), RAreaX2(self));
111
112		y1 = max(other->y, self->y);
113		y2 = min(RAreaY2(other), RAreaY2(self));
114
115		return RAreaNew(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
116	}
117
118	// Nope, so nothing
119	return RAreaInvalid();
120}
121
122
123/**
124 * Do two areas intersect?
125 */
126bool
127RAreaIsIntersect(const RArea *self, const RArea *other)
128{
129	// [other][self]
130	if(RAreaX2(other) < self->x) {
131		return false;
132	}
133
134	// [self][other]
135	if(other->x > RAreaX2(self)) {
136		return false;
137	}
138
139	// [other]
140	// [self]
141	if(RAreaY2(other) < self->y) {
142		return false;
143	}
144
145	// [self]
146	// [other]
147	if(other->y > RAreaY2(self)) {
148		return false;
149	}
150
151	return true;
152}
153
154
155/**
156 * Is a given coordinate inside a RArea?
157 */
158bool
159RAreaContainsXY(const RArea *self, int x, int y)
160{
161	return x >= self->x && x <= RAreaX2(self)
162	       && y >= self->y && y <= RAreaY2(self);
163}
164
165
166/**
167 * Create a list of maximal horizontal stripes of two RArea's.
168 *
169 * This yields a set of RArea's that completely cover (without overlap)
170 * the pair of input RArea's (or NULL if the inputs are disjoint).  That
171 * could be just a single RArea if e.g. they're the same height and
172 * touch/overlap horizontally, or they're the same width and
173 * touch/overlap vertically.  Otherwise it will wind up being multiple
174 * rows (2 or 3).
175 *
176 * Only used in startup to populate the RLayout.horiz list.
177 */
178RAreaList *
179RAreaHorizontalUnion(const RArea *self, const RArea *other)
180{
181	// If there's horizontal space between them, they can't possibly
182	// combine.
183	// [other]  [self]    or     [self]  [other]
184	if(RAreaX2(other) < self->x - 1) {
185		return NULL;
186	}
187	if(other->x > RAreaX2(self) + 1) {
188		return NULL;
189	}
190
191	// No vertical overlap (though maybe they touch?)
192	// [other] or [self]
193	// [self]     [other]
194	if(RAreaY2(other) < self->y || other->y > RAreaY2(self)) {
195		// Special case: if they're the same width, and start at the same
196		// X coordinate, _and_ are touching each other vertically, we can
197		// combine them into a single block.
198		if(self->width == other->width && self->x == other->x) {
199			// [other]
200			// [self ]
201			if(RAreaY2(other) + 1 == self->y) {
202				return RAreaListNew(
203				               1,
204				               RAreaNewStatic(self->x, other->y,
205				                              self->width, self->height + other->height),
206				               NULL);
207			}
208
209			// [self ]
210			// [other]
211			if(RAreaY2(self) + 1 == other->y) {
212				return RAreaListNew(
213				               1,
214				               RAreaNewStatic(self->x, self->y,
215				                              self->width, self->height + other->height),
216				               NULL);
217			}
218
219			// Nope, there must be vertical space between us
220		}
221
222		// Can't combine
223		return NULL;
224	}
225
226	// No horizontal space between the two (may be touching, or
227	// overlapping), and there's some vertical overlap.
228	// So there must be some horizontal stripes we can create.
229	{
230		// left- and right-most x coords, which define the maximum width
231		// a union could be.
232		const int min_x = min(self->x, other->x);
233		const int max_x = max(RAreaX2(self), RAreaX2(other));
234		const int max_width = max_x - min_x + 1;
235
236		RAreaList *res = RAreaListNew(3, NULL);
237
238		// Figure which starts higher.
239		const RArea *top, *bot;
240		if(self->y < other->y) {
241			top = self;
242			bot = other;
243		}
244		else {
245			top = other;
246			bot = self;
247		}
248		// bot now starts even with or below top
249
250		//      [   ]    [   ]
251		// [bot][top] or [top][bot]
252		//      [   ]         [   ]
253
254		// Room in top before bot starts?  That's one stripe.
255		if(bot->y != top->y) {
256			RAreaListAdd(res, RAreaNewStatic(top->x, top->y,
257			                                 top->width, bot->y - top->y));
258		}
259
260		// Next there's a stripe across both of them.
261		RAreaListAdd(res,
262		             RAreaNewStatic(min_x, bot->y,
263		                            max_width,
264		                            min(RAreaY2(top), RAreaY2(bot)) - max(top->y, bot->y) + 1));
265
266		// If their bottoms aren't coincident, there's another stripe
267		// below them of whichever one is taller.
268		if(RAreaY2(top) != RAreaY2(bot)) {
269			if(RAreaY2(bot) < RAreaY2(top)) {
270				//      [   ]    [   ]
271				// [bot][top] or [top][bot]
272				//      [   ]    [   ]
273				RAreaListAdd(res,
274				             RAreaNewStatic(top->x, RAreaY2(bot) + 1,
275				                            top->width, RAreaY2(top) - RAreaY2(bot)));
276			}
277			else {
278				//      [   ]    [   ]
279				// [bot][top] or [top][bot]
280				// [  ]               [   ]
281				RAreaListAdd(res,
282				             RAreaNewStatic(bot->x, RAreaY2(top) + 1,
283				                            bot->width, RAreaY2(bot) - RAreaY2(top)));
284			}
285		}
286
287		// And there they are.
288		return res;
289	}
290}
291
292
293/**
294 * Create a list of maximal vertical stripes of two RArea's.
295 *
296 * This yields a set of RArea's that completely cover (without overlap)
297 * the pair of input RArea's (or NULL if the inputs are disjoint).  This
298 * is the equivalent of RAreaHorizontalUnion(), except with vertical
299 * stripes.
300 *
301 * Only used in startup to populate the RLayout.vert list.
302 */
303RAreaList *
304RAreaVerticalUnion(const RArea *self, const RArea *other)
305{
306	// Vertical space between them; can't possibly combine.
307	if(RAreaY2(other) < self->y - 1) {
308		return NULL;
309	}
310	if(other->y > RAreaY2(self) + 1) {
311		return NULL;
312	}
313
314	// No horizontal overlap (though perhaps touching)
315	// [other][self] or [self][other]
316	if(RAreaX2(other) < self->x || other->x > RAreaX2(self)) {
317		// Special case: if they're the same height, and start at the same
318		// Y coordinate, _and_ are touching each other horizontally, we can
319		// combine them into a single block.
320		if(self->height == other->height && self->y == other->y) {
321			// [other][self]
322			if(RAreaX2(other) + 1 == self->x) {
323				return RAreaListNew(
324				               1,
325				               RAreaNewStatic(other->x, self->y,
326				                              self->width + other->width, self->height),
327				               NULL);
328			}
329
330			// [self][other]
331			if(RAreaX2(self) + 1 == other->x) {
332				return RAreaListNew(
333				               1,
334				               RAreaNewStatic(self->x, self->y,
335				                              self->width + other->width, self->height),
336				               NULL);
337			}
338
339			// Nope, not touching; horizontal space means no combining.
340		}
341		return NULL;
342	}
343
344	// No vertical space (touching or overlap), and some horizontal
345	// overlap.  So there are vertical stripes we can make.
346	{
347		// top- and bottom-most y coords, giving a maximum height
348		const int min_y = min(self->y, other->y);
349		const int max_y = max(RAreaY2(self), RAreaY2(other));
350		const int max_height = max_y - min_y + 1;
351
352		RAreaList *res = RAreaListNew(3, NULL);
353
354		// Which starts left-most
355		const RArea *left, *right;
356		if(self->x < other->x) {
357			left = self;
358			right = other;
359		}
360		else {
361			left = other;
362			right = self;
363		}
364
365		// [--left--] or  [right]  or    [right] or [left]
366		//  [right]     [--left--]    [left]          [right]
367
368		// Room to the left before right starts?  That's one stripe.
369		if(right->x != left->x) {
370			RAreaListAdd(res,
371			             RAreaNewStatic(left->x, left->y,
372			                            right->x - left->x, left->height));
373		}
374
375		// There's a stripe of their overlap.
376		RAreaListAdd(res,
377		             RAreaNewStatic(right->x, min_y,
378		                            min(RAreaX2(left), RAreaX2(right)) - max(left->x, right->x) + 1,
379		                            max_height));
380
381		// If they don't end at the same x coord, there's a third stripe
382		// of a piece to the right of one or the other.
383		if(RAreaX2(left) != RAreaX2(right)) {
384			if(RAreaX2(right) < RAreaX2(left)) {
385				// [--left--] or  [right]
386				//  [right]     [--left--]
387				RAreaListAdd(res,
388				             RAreaNewStatic(RAreaX2(right) + 1, left->y,
389				                            RAreaX2(left) - RAreaX2(right), left->height));
390			}
391			else {
392				//     [right] or [left]
393				//  [left]          [right]
394				RAreaListAdd(res,
395				             RAreaNewStatic(RAreaX2(left) + 1, right->y,
396				                            RAreaX2(right) - RAreaX2(left), right->height));
397			}
398		}
399
400		return res;
401	}
402}
403
404
405/**
406 * Pretty-print an RArea.
407 *
408 * Used for dev/debug.
409 */
410void
411RAreaPrint(const RArea *self)
412{
413	fprintf(stderr, "[x=%d y=%d w=%d h=%d]", self->x, self->y, self->width,
414	        self->height);
415}
416