Home | History | Annotate | Line # | Download | only in dist
      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  */
     18 RArea
     19 RAreaNew(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  */
     34 RArea *
     35 RAreaNewStatic(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  */
     48 RArea
     49 RAreaInvalid(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  */
     62 bool
     63 RAreaIsValid(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  */
     72 int
     73 RAreaX2(const RArea *self)
     74 {
     75 	return self->x + self->width - 1;
     76 }
     77 
     78 
     79 /**
     80  * Return the bottom edge of an RArea.
     81  */
     82 int
     83 RAreaY2(const RArea *self)
     84 {
     85 	return self->y + self->height - 1;
     86 }
     87 
     88 
     89 /**
     90  * Return the area of an RArea.
     91  */
     92 int
     93 RAreaArea(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  */
    102 RArea
    103 RAreaIntersect(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  */
    126 bool
    127 RAreaIsIntersect(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  */
    158 bool
    159 RAreaContainsXY(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  */
    178 RAreaList *
    179 RAreaHorizontalUnion(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  */
    303 RAreaList *
    304 RAreaVerticalUnion(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  */
    410 void
    411 RAreaPrint(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