static void test_overlap_funcs () { MetaRectangle temp1, temp2; int i; for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&temp1); get_random_rect (&temp2); g_assert (meta_rectangle_overlap (&temp1, &temp2) == (meta_rectangle_horiz_overlap (&temp1, &temp2) && meta_rectangle_vert_overlap (&temp1, &temp2))); } temp1 = meta_rect ( 0, 0, 10, 10); temp2 = meta_rect (20, 0, 10, 5); g_assert (!meta_rectangle_overlap (&temp1, &temp2)); g_assert (!meta_rectangle_horiz_overlap (&temp1, &temp2)); g_assert ( meta_rectangle_vert_overlap (&temp1, &temp2)); printf ("%s passed.\n", G_STRFUNC); }
//some math utilities static gboolean rect_is_overlapping_any(MetaRectangle rect, MetaRectangle* rects, gint n, MetaRectangle border) { if (!meta_rectangle_contains_rect(&border, &rect)) return TRUE; for (int i = 0; i < n; i++) { if (meta_rectangle_equal(&rects[i], &rect)) continue; if (meta_rectangle_overlap(&rects[i], &rect)) return TRUE; } return FALSE; }
static gboolean rect_overlaps_region (const GList *spanning_rects, const MetaRectangle *rect) { /* FIXME: Should I move this to boxes.[ch]? */ const GList *temp; gboolean overlaps; temp = spanning_rects; overlaps = FALSE; while (!overlaps && temp != NULL) { overlaps = overlaps || meta_rectangle_overlap (temp->data, rect); temp = temp->next; } return overlaps; }
static void natural_placement (MosesOverview* self, MetaRectangle area) { g_debug("%s: geom: %d,%d,%d,%d", __func__, area.x, area.y, area.width, area.height); MosesOverviewPrivate* priv = self->priv; GPtrArray* clones = priv->clones; MetaRectangle bounds = {area.x, area.y, area.width, area.height}; int direction = 0; int* directions = g_malloc(sizeof(int)*clones->len); MetaRectangle* rects = g_malloc(sizeof(MetaRectangle)*clones->len); for (int i = 0; i < clones->len; i++) { // save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y] MetaRectangle rect; ClutterActor* clone = g_ptr_array_index(clones, i); MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone))); MetaWindow* win = meta_window_actor_get_meta_window(actor); meta_window_get_frame_rect(win, &rect); rect = rect_adjusted(rect, -GAPS, -GAPS, GAPS, GAPS); rects[i] = rect; g_debug("%s: frame: %d,%d,%d,%d", __func__, rect.x, rect.y, rect.width, rect.height); meta_rectangle_union(&bounds, &rect, &bounds); // This is used when the window is on the edge of the screen to try to use as much screen real estate as possible. directions[i] = direction; direction++; if (direction == 4) direction = 0; } int loop_counter = 0; gboolean overlap = FALSE; do { overlap = FALSE; for (int i = 0; i < clones->len; i++) { for (int j = 0; j < clones->len; j++) { if (i == j) continue; MetaRectangle rect = rects[i]; MetaRectangle comp = rects[j]; if (!meta_rectangle_overlap(&rect, &comp)) continue; loop_counter ++; overlap = TRUE; // Determine pushing direction GdkPoint i_center = rect_center (rect); GdkPoint j_center = rect_center (comp); GdkPoint diff = {j_center.x - i_center.x, j_center.y - i_center.y}; // Prevent dividing by zero and non-movement if (diff.x == 0 && diff.y == 0) diff.x = 1; // Approximate a vector of between 10px and 20px in magnitude in the same direction float length = sqrtf (diff.x * diff.x + diff.y * diff.y); diff.x = (int)floorf (diff.x * ACCURACY / length); diff.y = (int)floorf (diff.y * ACCURACY / length); // Move both windows apart rect.x += -diff.x; rect.y += -diff.y; comp.x += diff.x; comp.y += diff.y; // Try to keep the bounding rect the same aspect as the screen so that more // screen real estate is utilised. We do this by splitting the screen into nine // equal sections, if the window center is in any of the corner sections pull the // window towards the outer corner. If it is in any of the other edge sections // alternate between each corner on that edge. We don't want to determine it // randomly as it will not produce consistant locations when using the filter. // Only move one window so we don't cause large amounts of unnecessary zooming // in some situations. We need to do this even when expanding later just in case // all windows are the same size. // (We are using an old bounding rect for this, hopefully it doesn't matter) int x_section = (int)roundf ((rect.x - bounds.x) / (bounds.width / 3.0f)); int y_section = (int)roundf ((comp.y - bounds.y) / (bounds.height / 3.0f)); i_center = rect_center (rect); diff.x = 0; diff.y = 0; if (x_section != 1 || y_section != 1) { // Remove this if you want the center to pull as well if (x_section == 1) x_section = (directions[i] / 2 == 1 ? 2 : 0); if (y_section == 1) y_section = (directions[i] % 2 == 1 ? 2 : 0); } if (x_section == 0 && y_section == 0) { diff.x = bounds.x - i_center.x; diff.y = bounds.y - i_center.y; } if (x_section == 2 && y_section == 0) { diff.x = bounds.x + bounds.width - i_center.x; diff.y = bounds.y - i_center.y; } if (x_section == 2 && y_section == 2) { diff.x = bounds.x + bounds.width - i_center.x; diff.y = bounds.y + bounds.height - i_center.y; } if (x_section == 0 && y_section == 2) { diff.x = bounds.x - i_center.x; diff.y = bounds.y + bounds.height - i_center.y; } if (diff.x != 0 || diff.y != 0) { length = sqrtf (diff.x * diff.x + diff.y * diff.y); diff.x *= (int)floorf (ACCURACY / length / 2.0f); diff.y *= (int)floorf (ACCURACY / length / 2.0f); rect.x += diff.x; rect.y += diff.y; } // Update bounding rect meta_rectangle_union(&bounds, &rect, &bounds); meta_rectangle_union(&bounds, &comp, &bounds); //we took copies from the rects from our list so we need to reassign them rects[i] = rect; rects[j] = comp; } } } while (overlap && loop_counter < MAX_TRANSLATIONS); // Work out scaling by getting the most top-left and most bottom-right window coords. float scale = fminf (fminf (area.width / (float)bounds.width, area.height / (float)bounds.height), 1.0f); // Make bounding rect fill the screen size for later steps bounds.x = (int)floorf (bounds.x - (area.width - bounds.width * scale) / 2); bounds.y = (int)floorf (bounds.y - (area.height - bounds.height * scale) / 2); bounds.width = (int)floorf (area.width / scale); bounds.height = (int)floorf (area.height / scale); // Move all windows back onto the screen and set their scale int index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; rects[index] = (MetaRectangle){ (int)floorf ((rect.x - bounds.x) * scale + area.x), (int)floorf ((rect.y - bounds.y) * scale + area.y), (int)floorf (rect.width * scale), (int)floorf (rect.height * scale) }; } // fill gaps by enlarging windows gboolean moved = FALSE; MetaRectangle border = area; do { moved = FALSE; index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; int width_diff = ACCURACY; int height_diff = (int)floorf ((((rect.width + width_diff) - rect.height) / (float)rect.width) * rect.height); int x_diff = width_diff / 2; int y_diff = height_diff / 2; //top right MetaRectangle old = rect; rect = (MetaRectangle){ rect.x + x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff }; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //bottom right old = rect; rect = (MetaRectangle){rect.x + x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //bottom left old = rect; rect = (MetaRectangle){rect.x - x_diff, rect.y + y_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; //top left old = rect; rect = (MetaRectangle){rect.x - x_diff, rect.y - y_diff - height_diff, rect.width + width_diff, rect.height + width_diff}; if (rect_is_overlapping_any (rect, rects, clones->len, border)) rect = old; else moved = TRUE; rects[index] = rect; } } while (moved); index = 0; for (; index < clones->len; index++) { MetaRectangle rect = rects[index]; ClutterActor* clone = g_ptr_array_index(clones, index); MetaWindowActor* actor = META_WINDOW_ACTOR(clutter_clone_get_source(CLUTTER_CLONE(clone))); MetaWindow* window = meta_window_actor_get_meta_window(actor); MetaRectangle window_rect; meta_window_get_frame_rect(window, &window_rect); rect = rect_adjusted(rect, GAPS, GAPS, -GAPS, -GAPS); scale = rect.width / (float)window_rect.width; if (scale > 2.0 || (scale > 1.0 && (window_rect.width > 300 || window_rect.height > 300))) { scale = (window_rect.width > 300 || window_rect.height > 300) ? 1.0f : 2.0f; rect = (MetaRectangle){rect_center (rect).x - (int)floorf (window_rect.width * scale) / 2, rect_center (rect).y - (int)floorf (window_rect.height * scale) / 2, (int)floorf (window_rect.width * scale), (int)floorf (window_rect.height * scale)}; } place_window(self, clone, rect); } g_free(directions); g_free(rects); }
static void test_merge_regions () { /* logarithmically distributed random number of struts (range?) * logarithmically distributed random size of struts (up to screen size???) * uniformly distributed location of center of struts (within screen) * merge all regions that are possible * print stats on problem setup * number of (non-completely-occluded?) struts * percentage of screen covered * length of resulting non-minimal spanning set * length of resulting minimal spanning set * print stats on merged regions: * number boxes merged * number of those merges that were of the form A contains B * number of those merges that were of the form A partially contains B * number of those merges that were of the form A is adjacent to B */ GList* region; GList* compare; int num_contains, num_merged, num_part_contains, num_adjacent; num_contains = num_merged = num_part_contains = num_adjacent = 0; compare = region = get_screen_region (2); g_assert (region); printf ("Merging stats:\n"); printf (" Length of initial list: %d\n", g_list_length (region)); #ifdef PRINT_DEBUG char rect1[RECT_LENGTH], rect2[RECT_LENGTH]; char region_list[(RECT_LENGTH + 2) * g_list_length (region)]; meta_rectangle_region_to_string (region, ", ", region_list); printf (" Initial rectangles: %s\n", region_list); #endif while (compare && compare->next) { MetaRectangle *a = compare->data; GList *other = compare->next; g_assert (a->width > 0 && a->height > 0); while (other) { MetaRectangle *b = other->data; GList *delete_me = NULL; g_assert (b->width > 0 && b->height > 0); #ifdef PRINT_DEBUG printf (" -- Comparing %s to %s --\n", meta_rectangle_to_string (a, rect1), meta_rectangle_to_string (b, rect2)); #endif /* If a contains b, just remove b */ if (meta_rectangle_contains_rect (a, b)) { delete_me = other; num_contains++; num_merged++; } /* If b contains a, just remove a */ else if (meta_rectangle_contains_rect (a, b)) { delete_me = compare; num_contains++; num_merged++; } /* If a and b might be mergeable horizontally */ else if (a->y == b->y && a->height == b->height) { /* If a and b overlap */ if (meta_rectangle_overlap (a, b)) { int new_x = MIN (a->x, b->x); a->width = MAX (a->x + a->width, b->x + b->width) - new_x; a->x = new_x; delete_me = other; num_part_contains++; num_merged++; } /* If a and b are adjacent */ else if (a->x + a->width == b->x || a->x == b->x + b->width) { int new_x = MIN (a->x, b->x); a->width = MAX (a->x + a->width, b->x + b->width) - new_x; a->x = new_x; delete_me = other; num_adjacent++; num_merged++; } } /* If a and b might be mergeable vertically */ else if (a->x == b->x && a->width == b->width) { /* If a and b overlap */ if (meta_rectangle_overlap (a, b)) { int new_y = MIN (a->y, b->y); a->height = MAX (a->y + a->height, b->y + b->height) - new_y; a->y = new_y; delete_me = other; num_part_contains++; num_merged++; } /* If a and b are adjacent */ else if (a->y + a->height == b->y || a->y == b->y + b->height) { int new_y = MIN (a->y, b->y); a->height = MAX (a->y + a->height, b->y + b->height) - new_y; a->y = new_y; delete_me = other; num_adjacent++; num_merged++; } } other = other->next; /* Delete any rectangle in the list that is no longer wanted */ if (delete_me != NULL) { #ifdef PRINT_DEBUG MetaRectangle *bla = delete_me->data; printf (" Deleting rect %s\n", meta_rectangle_to_string (bla, rect1)); #endif /* Deleting the rect we're compare others to is a little tricker */ if (compare == delete_me) { compare = compare->next; other = compare->next; a = compare->data; } /* Okay, we can free it now */ g_free (delete_me->data); region = g_list_delete_link (region, delete_me); } #ifdef PRINT_DEBUG char region_list[(RECT_LENGTH + 2) * g_list_length (region)]; meta_rectangle_region_to_string (region, ", ", region_list); printf (" After comparison, new list is: %s\n", region_list); #endif } compare = compare->next; } printf (" Num rectangles contained in others : %d\n", num_contains); printf (" Num rectangles partially contained in others: %d\n", num_part_contains); printf (" Num rectangles adjacent to others : %d\n", num_adjacent); printf (" Num rectangles merged with others : %d\n", num_merged); #ifdef PRINT_DEBUG char region_list2[(RECT_LENGTH + 2) * g_list_length (region)]; meta_rectangle_region_to_string (region, ", ", region_list2); printf (" Final rectangles: %s\n", region_list2); #endif meta_rectangle_free_spanning_set (region); region = NULL; printf ("%s passed.\n", G_STRFUNC); }