def contour_area(con): """Calculate the area, convex hull area, and convexity deficiency of a polygon contour. Parameters ---------- con : arraylike A 2D array of vertices with shape (N,2) that follows the scikit image conventions (con[:,0] are j indices) Returns ------- region_area : float hull_area : float convexity_deficiency : float """ # reshape the data to x, y order con_points = con[:, ::-1] # calculate area of polygon region_area = polygon_area(con_points) # find convex hull hull = qhull.ConvexHull(con_points) #hull_points = np.array([con_points[pt] for pt in hull.vertices]) hull_area = hull.volume cd = (hull_area - region_area) / region_area return region_area, hull_area, cd
def _update(self): """Update the :math:`A_{ineq}` matrix and the :math:`b_u` vector""" # if time to update if self._cnt % self._ticks == 0: # compute convex hull try: self._hull = qhull.ConvexHull(self._points) except qhull.QhullError: print( "Not enough different points to compute the convex hull, using the old one." ) if self._hull is not None: # convex hull equations A = self._hull.equations[:, : -2] # shape: (F,2) - we only care about x and y (and not z)) b = 1 * self._hull.equations[:, -1] - self.safety_margin # get jacobian (note that we only care about x and y (and not z)) jacobian = self.model.get_com_jacobian( full=False)[:2] # shape: (2,N) # get current com position x_com = self.model.get_com_position()[:2] # shape: (2,) # constraint matrix and vector self._A_ineq = A.dot(jacobian) # (F,N) self._b_upper_bound = b - self._margin - A.dot(x_com) # (F,) # reset counter self._cnt = 0 # update counter self._cnt += 1
def check(name): points = DATASETS[name] tri = qhull.Delaunay(points) hull = qhull.ConvexHull(points) assert_hulls_equal(points, tri.convex_hull, hull.simplices)
def test_incremental_volume_area_random_input(self): """Test that incremental mode gives the same volume/area as non-incremental mode and incremental mode with restart""" nr_points = 20 dim = 3 points = np.random.random((nr_points, dim)) inc_hull = qhull.ConvexHull(points[:dim+1, :], incremental=True) inc_restart_hull = qhull.ConvexHull(points[:dim+1, :], incremental=True) for i in range(dim+1, nr_points): hull = qhull.ConvexHull(points[:i+1, :]) inc_hull.add_points(points[i:i+1, :]) inc_restart_hull.add_points(points[i:i+1, :], restart=True) assert_allclose(hull.volume, inc_hull.volume, rtol=1e-7) assert_allclose(hull.volume, inc_restart_hull.volume, rtol=1e-7) assert_allclose(hull.area, inc_hull.area, rtol=1e-7) assert_allclose(hull.area, inc_restart_hull.area, rtol=1e-7)
def test_convex(points, bpoints, radius=0.0): is_convex = True # compute the convex hull of the region try: hull = qhull.ConvexHull(points) qhull_success = True except: # qhull can fail if the points are all on the same line # if we have five points on the same line, # these are note the drones we're looking for qhull_success = False is_convex = False # turn into a matplotib polygon if qhull_success: vertices = [[points[v, 0], points[v, 1]] for v in hull.vertices] # add dummy endpoint (ignored but used to close polygon) vertices.append(vertices[-1]) codes = np.ones(len(vertices)) * mplPath.Path.LINETO codes[0] = mplPath.Path.MOVETO codes[-1] = mplPath.Path.CLOSEPOLY bbPath = mplPath.Path(vertices, codes) for bpt in bpoints: # the hull should not contain any of the boundary points if bbPath.contains_point(bpt, radius=radius): is_convex = False # need to get rid of the most recently added point, # which is the one responsible for breaking convexity #print 'test_convex:', is_convex return is_convex
def test_volume_area(self): # Basic check that we get back the correct volume and area for a cube points = np.array([(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]) tri = qhull.ConvexHull(points) assert_allclose(tri.volume, 1., rtol=1e-14) assert_allclose(tri.area, 6., rtol=1e-14)
def test_incremental(self, name): # Test incremental construction of the convex hull chunks, _ = INCREMENTAL_DATASETS[name] points = np.concatenate(chunks, axis=0) obj = qhull.ConvexHull(chunks[0], incremental=True) for chunk in chunks[1:]: obj.add_points(chunk) obj2 = qhull.ConvexHull(points) obj3 = qhull.ConvexHull(chunks[0], incremental=True) if len(chunks) > 1: obj3.add_points(np.concatenate(chunks[1:], axis=0), restart=True) # Check that the incremental mode agrees with upfront mode assert_hulls_equal(points, obj.simplices, obj2.simplices) assert_hulls_equal(points, obj.simplices, obj3.simplices)
def test_volume_area(self): #Basic check that we get back the correct volume and area for a cube points = np.array([(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), (0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)]) hull = qhull.ConvexHull(points) assert_allclose(hull.volume, 1., rtol=1e-14, err_msg="Volume of cube is incorrect") assert_allclose(hull.area, 6., rtol=1e-14, err_msg="Area of cube is incorrect")
def test_good2d(self, incremental): # Make sure the QGn option gives the correct value of "good". points = np.array([[0.2, 0.2], [0.2, 0.4], [0.4, 0.4], [0.4, 0.2], [0.3, 0.6]]) hull = qhull.ConvexHull(points=points, incremental=incremental, qhull_options='QG4') expected = np.array([False, True, False, False], dtype=bool) actual = hull.good assert_equal(actual, expected)
def test_good2d_inside(self, incremental): # Make sure the QGn option gives the correct value of "good". # When point n is inside the convex hull of the rest, good is # all False. points = np.array([[0.2, 0.2], [0.2, 0.4], [0.4, 0.4], [0.4, 0.2], [0.3, 0.3]]) hull = qhull.ConvexHull(points=points, incremental=incremental, qhull_options='QG4') expected = np.array([False, False, False, False], dtype=bool) actual = hull.good assert_equal(actual, expected)
def test_vertices_2d(self): # The vertices should be in counterclockwise order in 2-D np.random.seed(1234) points = np.random.rand(30, 2) hull = qhull.ConvexHull(points) assert_equal(np.unique(hull.simplices), np.sort(hull.vertices)) # Check counterclockwiseness x, y = hull.points[hull.vertices].T angle = np.arctan2(y - y.mean(), x - x.mean()) assert_(np.all(np.diff(np.unwrap(angle)) > 0))
def check(name): points = DATASETS[name] tri = qhull.Delaunay(points) hull = qhull.ConvexHull(points) assert_hulls_equal(points, tri.convex_hull, hull.simplices) # Check that the hull extremes are as expected if points.shape[1] == 2: assert_equal(np.unique(hull.simplices), np.sort(hull.vertices)) else: assert_equal(np.unique(hull.simplices), hull.vertices)
def test_good2d_no_option(self, incremental): # handle case where good attribue doesn't exist # because Qgn or Qg-n wasn't specified points = np.array([[0.2, 0.2], [0.2, 0.4], [0.4, 0.4], [0.4, 0.2], [0.3, 0.6]]) hull = qhull.ConvexHull(points=points, incremental=incremental) actual = hull.good assert actual is None # preserve None after incremental addition if incremental: hull.add_points(np.zeros((1, 2))) actual = hull.good assert actual is None
def check(name, chunksize): points = DATASETS[name] ndim = points.shape[1] if name == 'pathological-1': # include enough points so that we get different x-coordinates nmin = 12 else: nmin = ndim +2 obj = qhull.ConvexHull(points[:nmin], incremental=True) for j in xrange(nmin, len(points), chunksize): obj.add_points(points[j:j+chunksize]) obj2 = qhull.ConvexHull(points) obj3 = qhull.ConvexHull(points[:nmin], incremental=True) obj3.add_points(points[nmin:], restart=True) # Check that the incremental mode agrees with upfront mode assert_hulls_equal(points, obj.simplices, obj2.simplices) assert_hulls_equal(points, obj.simplices, obj3.simplices)
def test_good3d(self, incremental): # Make sure the QGn option gives the correct value of "good" # for a 3d figure points = np.array([[0.0, 0.0, 0.0], [0.90029516, -0.39187448, 0.18948093], [0.48676420, -0.72627633, 0.48536925], [0.57651530, -0.81179274, -0.09285832], [0.67846893, -0.71119562, 0.18406710]]) hull = qhull.ConvexHull(points=points, incremental=incremental, qhull_options='QG0') expected = np.array([True, False, False, False], dtype=bool) assert_equal(hull.good, expected)
def test_good2d_incremental_changes(self, new_gen, expected, visibility): # use the usual square convex hull # generators from test_good2d points = np.array([[0.2, 0.2], [0.2, 0.4], [0.4, 0.4], [0.4, 0.2], [0.3, 0.6]]) hull = qhull.ConvexHull(points=points, incremental=True, qhull_options=visibility) hull.add_points(new_gen) actual = hull.good if '-' in visibility: expected = np.invert(expected) assert_equal(actual, expected)
def test_hull_consistency_tri(self, name): # Check that a convex hull returned by qhull in ndim # and the hull constructed from ndim delaunay agree points = DATASETS[name] tri = qhull.Delaunay(points) hull = qhull.ConvexHull(points) assert_hulls_equal(points, tri.convex_hull, hull.simplices) # Check that the hull extremes are as expected if points.shape[1] == 2: assert_equal(np.unique(hull.simplices), np.sort(hull.vertices)) else: assert_equal(np.unique(hull.simplices), hull.vertices)
def test_random_volume_area(self): #Test that the results for a random 10-point convex are #coherent with the output of qconvex Qt s FA points = np.array([(0.362568364506, 0.472712355305, 0.347003084477), (0.733731893414, 0.634480295684, 0.950513180209), (0.511239955611, 0.876839441267, 0.418047827863), (0.0765906233393, 0.527373281342, 0.6509863541), (0.146694972056, 0.596725793348, 0.894860986685), (0.513808585741, 0.069576205858, 0.530890338876), (0.512343805118, 0.663537132612, 0.037689295973), (0.47282965018, 0.462176697655, 0.14061843691), (0.240584597123, 0.778660020591, 0.722913476339), (0.951271745935, 0.967000673944, 0.890661319684)]) hull = qhull.ConvexHull(points) assert_allclose(hull.volume, 0.14562013, rtol=1e-07, err_msg="Volume of random polyhedron is incorrect") assert_allclose(hull.area, 1.6670425, rtol=1e-07, err_msg="Area of random polyhedron is incorrect")
def _construct_hull(properties): """ Returns points on convex hull. """ hull = qhull.ConvexHull(properties.coords) return hull.points[hull.vertices].astype(int).tolist()
ha = hexgrid.HexArray(shape=(10, 10)) pos = np.array([ha.pos(n) for n in range(ha.N)]) x, y = pos.T nmid = ha.N / 2 + ha.Nx / 2 r = -np.sqrt((x - x[nmid])**2 + (y - y[nmid])**2) mask = r <= 3 plt.scatter(*pos.T, c=mask, cmap='Greys') hr = hexgrid.HexArrayRegion(ha) bpos = np.array([ha.pos(n) for n in hr.interior_boundary()]) ebpos = np.array([ha.pos(n) for n in hr.exterior_boundary()]) plt.scatter(*bpos.T, c='m') plt.scatter(*ebpos.T, c='c') hull = qhull.ConvexHull(bpos) hv = np.hstack([hull.vertices, hull.vertices[0]]) plt.plot(*hull.points[hv].T, color='k') def pnpoly(vertx, verty, testx, testy): nvert = len(vertx) i = 0 j = nvert - 1 c = False while (i < nvert): #for (i = 0, j = nvert-1; i < nvert; j = i++) { if (((verty[i] > testy) != (verty[j] > testy)) and (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i])): c = not c