int igraph_vector_order2(igraph_vector_t *v) { igraph_indheap_t heap; igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v)); IGRAPH_FINALLY(igraph_indheap_destroy, &heap); igraph_vector_clear(v); while (!igraph_indheap_empty(&heap)) { IGRAPH_CHECK(igraph_vector_push_back(v, igraph_indheap_max_index(&heap)-1)); igraph_indheap_delete_max(&heap); } igraph_indheap_destroy(&heap); IGRAPH_FINALLY_CLEAN(1); return 0; }
/** * \ingroup nongraph * \function igraph_convex_hull * \brief Determines the convex hull of a given set of points in the 2D plane * * </para><para> * The convex hull is determined by the Graham scan algorithm. * See the following reference for details: * * </para><para> * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford * Stein. Introduction to Algorithms, Second Edition. MIT Press and * McGraw-Hill, 2001. ISBN 0262032937. Pages 949-955 of section 33.3: * Finding the convex hull. * * \param data vector containing the coordinates. The length of the * vector must be even, since it contains X-Y coordinate pairs. * \param resverts the vector containing the result, e.g. the vector of * vertex indices used as the corners of the convex hull. Supply * \c NULL here if you are only interested in the coordinates of * the convex hull corners. * \param rescoords the matrix containing the coordinates of the selected * corner vertices. Supply \c NULL here if you are only interested in * the vertex indices. * \return Error code: * \c IGRAPH_ENOMEM: not enough memory * * Time complexity: O(n log(n)) where n is the number of vertices */ int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, igraph_matrix_t *rescoords) { igraph_integer_t no_of_nodes; long int i, pivot_idx=0, last_idx, before_last_idx, next_idx, j; igraph_real_t* angles; igraph_vector_t stack; igraph_indheap_t order; igraph_real_t px, py, cp; no_of_nodes=igraph_matrix_nrow(data); if (igraph_matrix_ncol(data) != 2) { IGRAPH_ERROR("matrix must have 2 columns", IGRAPH_EINVAL); } if (no_of_nodes == 0) { if (resverts != 0) { IGRAPH_CHECK(igraph_vector_resize(resverts, 0)); } if (rescoords != 0) { IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2)); } /**************************** this is an exit here *********/ return 0; } angles=igraph_Calloc(no_of_nodes, igraph_real_t); if (!angles) IGRAPH_ERROR("not enough memory for angle array", IGRAPH_ENOMEM); IGRAPH_FINALLY(free, angles); IGRAPH_VECTOR_INIT_FINALLY(&stack, 0); /* Search for the pivot vertex */ for (i=1; i<no_of_nodes; i++) { if (MATRIX(*data, i, 1)<MATRIX(*data, pivot_idx, 1)) pivot_idx=i; else if (MATRIX(*data, i, 1) == MATRIX(*data, pivot_idx, 1) && MATRIX(*data, i, 0) < MATRIX(*data, pivot_idx, 0)) pivot_idx=i; } px=MATRIX(*data, pivot_idx, 0); py=MATRIX(*data, pivot_idx, 1); /* Create angle array */ for (i=0; i<no_of_nodes; i++) { if (i == pivot_idx) { /* We can't calculate the angle of the pivot point with itself, * so we use 10 here. This way, after sorting the angle vector, * the pivot point will always be the first one, since the range * of atan2 is -3.14..3.14 */ angles[i] = 10; } else { angles[i] = atan2(MATRIX(*data, i, 1)-py, MATRIX(*data, i, 0)-px); } } IGRAPH_CHECK(igraph_indheap_init_array(&order, angles, no_of_nodes)); IGRAPH_FINALLY(igraph_indheap_destroy, &order); igraph_Free(angles); IGRAPH_FINALLY_CLEAN(1); if (no_of_nodes == 1) { IGRAPH_CHECK(igraph_vector_push_back(&stack, 0)); igraph_indheap_delete_max(&order); } else { /* Do the trick */ IGRAPH_CHECK(igraph_vector_push_back(&stack, igraph_indheap_max_index(&order)-1)); igraph_indheap_delete_max(&order); IGRAPH_CHECK(igraph_vector_push_back(&stack, igraph_indheap_max_index(&order)-1)); igraph_indheap_delete_max(&order); j=2; while (!igraph_indheap_empty(&order)) { /* Determine whether we are at a left or right turn */ last_idx=VECTOR(stack)[j-1]; before_last_idx=VECTOR(stack)[j-2]; next_idx=(long)igraph_indheap_max_index(&order)-1; igraph_indheap_delete_max(&order); cp=(MATRIX(*data, last_idx, 0)-MATRIX(*data, before_last_idx, 0))* (MATRIX(*data, next_idx, 1)-MATRIX(*data, before_last_idx, 1))- (MATRIX(*data, next_idx, 0)-MATRIX(*data, before_last_idx, 0))* (MATRIX(*data, last_idx, 1)-MATRIX(*data, before_last_idx, 1)); /* printf("B L N cp: %d, %d, %d, %f [", before_last_idx, last_idx, next_idx, (float)cp); for (k=0; k<j; k++) printf("%ld ", (long)VECTOR(stack)[k]); printf("]\n"); */ if (cp == 0) { /* The last three points are collinear. Replace the last one in * the stack to the newest one */ VECTOR(stack)[j-1]=next_idx; } else if (cp < 0) { /* We are turning into the right direction */ IGRAPH_CHECK(igraph_vector_push_back(&stack, next_idx)); j++; } else { /* No, skip back until we're okay */ while (cp >= 0 && j > 2) { igraph_vector_pop_back(&stack); j--; last_idx=VECTOR(stack)[j-1]; before_last_idx=VECTOR(stack)[j-2]; cp=(MATRIX(*data, last_idx, 0)-MATRIX(*data, before_last_idx, 0))* (MATRIX(*data, next_idx, 1)-MATRIX(*data, before_last_idx, 1))- (MATRIX(*data, next_idx, 0)-MATRIX(*data, before_last_idx, 0))* (MATRIX(*data, last_idx, 1)-MATRIX(*data, before_last_idx, 1)); } IGRAPH_CHECK(igraph_vector_push_back(&stack, next_idx)); j++; } } } /* Create result vector */ if (resverts != 0) { igraph_vector_clear(resverts); IGRAPH_CHECK(igraph_vector_append(resverts, &stack)); } if (rescoords != 0) { igraph_matrix_select_rows(data, rescoords, &stack); } /* Free everything */ igraph_vector_destroy(&stack); igraph_indheap_destroy(&order); IGRAPH_FINALLY_CLEAN(2); return 0; }