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