def _rotate_and_chop(self, verts, normal, axis=[0, 0, 1]): r""" Method to rotate a set of vertices (or coords) to align with an axis points must be coplanar and normal must be given Chops axis coord to give vertices back in 2D Used to prepare verts for printing or calculating convex hull in order to arrange them in hull order for calculations and printing """ xaxis = [1, 0, 0] yaxis = [0, 1, 0] zaxis = [0, 0, 1] angle = tr.angle_between_vectors(normal, axis) if (angle == 0.0) or (angle == np.pi): "We are already aligned" facet = verts else: M = tr.rotation_matrix(tr.angle_between_vectors(normal, axis), tr.vector_product(normal, axis)) facet = np.dot(verts, M[:3, :3].T) x = facet[:, 0] y = facet[:, 1] z = facet[:, 2] " Work out span of points and set axes scales to cover this and be equal in both dimensions " if axis == xaxis: output = np.column_stack((y, z)) elif axis == yaxis: output = np.column_stack((x, z)) elif axis == zaxis: output = np.column_stack((x, y)) else: output = facet return output
def centre_of_mass(geometry, vertices='throat.offset_vertices', **kwargs): r""" Calculate the centre of mass of the throat from the voronoi vertices. """ Nt = geometry.num_throats() outer_verts = geometry['throat.vertices'] offset_verts = geometry[vertices] normal = geometry['throat.normal'] z_axis = [0, 0, 1] value = _sp.ndarray([Nt, 3]) for i in range(Nt): if len(offset_verts[i]) > 2: verts = offset_verts[i] elif len(outer_verts[i]) > 2: verts = outer_verts[i] else: verts = [] if len(verts) > 0: # For boundaries some facets will already be aligned with the axis - # if this is the case a rotation is unnecessary and could also cause # problems angle = tr.angle_between_vectors(normal[i], z_axis) if angle == 0.0 or angle == _sp.pi: "We are already aligned" rotate_input = False facet = verts else: rotate_input = True M = tr.rotation_matrix(tr.angle_between_vectors(normal[i], z_axis), tr.vector_product(normal[i], z_axis)) facet = _sp.dot(verts, M[:3, :3].T) # Now we have a rotated facet aligned with the z axis - make 2D facet_2D = _sp.column_stack((facet[:, 0], facet[:, 1])) z = _sp.unique(_sp.around(facet[:, 2], 10)) if len(z) == 1: # We need the vertices arranged in order so perform a convex hull hull = ConvexHull(facet_2D) ordered_facet_2D = facet_2D[hull.vertices] # Call the routine to calculate an area wighted centroid from the # 2D polygon COM_2D = vo.PolyWeightedCentroid2D(ordered_facet_2D) COM_3D = _sp.hstack((COM_2D, z)) # If we performed a rotation we need to rotate back if (rotate_input): MI = tr.inverse_matrix(M) # Unrotate the offset coordinates using the inverse of the # original rotation matrix value[i] = _sp.dot(COM_3D, MI[:3, :3].T) else: value[i] = COM_3D else: print('Rotation Failed: ' + str(_sp.unique(facet[:, 2]))) return value
def centre_of_mass(geometry, **kwargs): r""" Calculate the centre of mass of the throat from the voronoi vertices. """ Nt = geometry.num_throats() outer_verts = geometry['throat.vertices'] offset_verts = geometry['throat.offset_vertices'] normal = geometry['throat.normal'] z_axis = [0, 0, 1] value = _sp.ndarray([Nt, 3]) for i in range(Nt): if len(offset_verts[i]) > 2: verts = offset_verts[i] elif len(outer_verts[i]) > 2: verts = outer_verts[i] else: verts = [] if len(verts) > 0: " For boundaries some facets will already be aligned with the axis - if this is the case a rotation is unnecessary and could also cause problems " angle = tr.angle_between_vectors(normal[i], z_axis) if (angle == 0.0) or (angle == _sp.pi): "We are already aligned" rotate_input = False facet = verts else: rotate_input = True M = tr.rotation_matrix( tr.angle_between_vectors(normal[i], z_axis), tr.vector_product(normal[i], z_axis)) facet = _sp.dot(verts, M[:3, :3].T) "Now we have a rotated facet aligned with the z axis - make 2D" facet_2D = _sp.column_stack((facet[:, 0], facet[:, 1])) z = _sp.unique(_sp.around(facet[:, 2], 10)) if len(z) == 1: "We need the vertices arranged in order so perform a convex hull" hull = ConvexHull(facet_2D) ordered_facet_2D = facet_2D[hull.vertices] "Call the routine to calculate an area wighted centroid from the 2D polygon" COM_2D = vo.PolyWeightedCentroid2D(ordered_facet_2D) COM_3D = _sp.hstack((COM_2D, z)) "If we performed a rotation we need to rotate back" if (rotate_input): MI = tr.inverse_matrix(M) " Unrotate the offset coordinates using the inverse of the original rotation matrix" value[i] = _sp.dot(COM_3D, MI[:3, :3].T) else: value[i] = COM_3D else: print("Rotation Failed: " + str(_sp.unique(facet[:, 2]))) return value
def distance_transform(network, geometry, offset, **kwargs): r""" Use the Voronoi vertices and perform image analysis to obtain throat properties """ import math import numpy as np from skimage.morphology import convex_hull_image from skimage.measure import regionprops from scipy import ndimage Nt = geometry.num_throats() area = sp.zeros(Nt) perimeter = sp.zeros(Nt) centroid = sp.zeros([Nt, 3]) incentre = sp.zeros([Nt, 3]) inradius = sp.zeros(Nt) equiv_diameter = sp.zeros(Nt) eroded_verts = sp.ndarray(Nt, dtype=object) res = 200 vertices = geometry['throat.vertices'] normals = geometry['throat.normal'] z_axis = [0, 0, 1] for i in range(Nt): logger.info("Processing throat " + str(i+1)+" of "+str(Nt)) # For boundaries some facets will already be aligned with the axis - if this # is the case a rotation is unnecessary and could also cause problems angle = tr.angle_between_vectors(normals[i], z_axis) if angle == 0.0 or angle == np.pi: # We are already aligned rotate_facet = False facet = vertices[i] else: rotate_facet = True M = tr.rotation_matrix(tr.angle_between_vectors(normals[i], z_axis), tr.vector_product(normals[i], z_axis)) facet = np.dot(vertices[i], M[:3, :3].T) x = facet[:, 0] y = facet[:, 1] z = facet[:, 2] # Get points in 2d for image analysis pts = np.column_stack((x, y)) # Translate points so min sits at the origin translation = [pts[:, 0].min(), pts[:, 1].min()] pts -= translation order = np.int(math.ceil(-np.log10(np.max(pts)))) # Normalise and scale the points so that largest span equals the resolution # to save on memory and create clear image" max_factor = np.max([pts[:, 0].max(), pts[:, 1].max()]) f = res/max_factor # Scale the offset and define a circular structuring element with radius r = f*offset # Only proceed if r is less than half the span of the image" if r <= res/2: pts *= f minp1 = pts[:, 0].min() minp2 = pts[:, 1].min() maxp1 = pts[:, 0].max() maxp2 = pts[:, 1].max() img = np.zeros([np.int(math.ceil(maxp1-minp1)+1), np.int(math.ceil(maxp2-minp2)+1)]) int_pts = np.around(pts, 0).astype(int) for pt in int_pts: img[pt[0]][pt[1]] = 1 # Pad with zeros all the way around the edges img_pad = np.zeros([np.shape(img)[0] + 2, np.shape(img)[1] + 2]) img_pad[1:np.shape(img)[0]+1, 1:np.shape(img)[1]+1] = img # All points should lie on this plane but could be some rounding errors # so use the order parameter z_plane = sp.unique(np.around(z, order+2)) if len(z_plane) > 1: print('Rotation for image analysis failed') temp_arr = np.ones(1) temp_arr.fill(np.mean(z_plane)) z_plane = temp_arr "Fill in the convex hull polygon" convhullimg = convex_hull_image(img_pad) # Perform a Distance Transform and black out points less than r to create # binary erosion. This is faster than performing an erosion and dt can # also be used later to find incircle" eroded = ndimage.distance_transform_edt(convhullimg) eroded[eroded <= r] = 0 eroded[eroded > r] = 1 # If we are left with less than 3 non-zero points then the throat is # fully occluded if np.sum(eroded) >= 3: # Do some image analysis to extract the key properties regions = regionprops(eroded[1:np.shape(img)[0]+1, 1:np.shape(img)[1]+1].astype(int)) # Change this to cope with genuine multi-region throats if len(regions) == 1: for props in regions: x0, y0 = props.centroid equiv_diameter[i] = props.equivalent_diameter area[i] = props.area perimeter[i] = props.perimeter coords = props.coords # Undo the translation, scaling and truncation on the centroid centroid2d = [x0, y0]/f centroid2d += (translation) centroid3d = np.concatenate((centroid2d, z_plane)) # Distance transform the eroded facet to find the incentre and # inradius dt = ndimage.distance_transform_edt(eroded) inx0, iny0 = \ np.asarray(np.unravel_index(dt.argmax(), dt.shape)) \ .astype(float) incentre2d = [inx0, iny0] # Undo the translation, scaling and truncation on the incentre incentre2d /= f incentre2d += (translation) incentre3d = np.concatenate((incentre2d, z_plane)) # The offset vertices will be those in the coords that are # closest to the originals" offset_verts = [] for pt in int_pts: vert = np.argmin(np.sum(np.square(coords-pt), axis=1)) if vert not in offset_verts: offset_verts.append(vert) # If we are left with less than 3 different vertices then the # throat is fully occluded as we can't make a shape with # non-zero area if len(offset_verts) >= 3: offset_coords = coords[offset_verts].astype(float) # Undo the translation, scaling and truncation on the # offset_verts offset_coords /= f offset_coords_3d = \ np.vstack((offset_coords[:, 0]+translation[0], offset_coords[:, 1]+translation[1], np.ones(len(offset_verts))*z_plane)).T # Get matrix to un-rotate the co-ordinates back to the # original orientation if we rotated in the first place if rotate_facet: MI = tr.inverse_matrix(M) # Unrotate the offset coordinates incentre[i] = np.dot(incentre3d, MI[:3, :3].T) centroid[i] = np.dot(centroid3d, MI[:3, :3].T) eroded_verts[i] = np.dot(offset_coords_3d, MI[:3, :3].T) else: incentre[i] = incentre3d centroid[i] = centroid3d eroded_verts[i] = offset_coords_3d inradius[i] = dt.max() # Undo scaling on other parameters area[i] /= f*f perimeter[i] /= f equiv_diameter[i] /= f inradius[i] /= f else: area[i] = 0 perimeter[i] = 0 equiv_diameter[i] = 0 if kwargs['set_dependent'] is True: geometry['throat.area'] = area geometry['throat.perimeter'] = perimeter geometry['throat.centroid'] = centroid geometry['throat.diameter'] = equiv_diameter geometry['throat.indiameter'] = inradius*2 geometry['throat.incentre'] = incentre return eroded_verts
def _get_throat_geom(self, verts, normal, fibre_rad): r""" For one set of vertices defining a throat return the key properties This is the main loop for calling other sub-routines. General Method: For each connection or throat defined by the shared vertices Rotate the vertices to align with the xy-plane and get rid of z-coordinate Compute the convex hull of the 2D points giving a set of simplices which define neighbouring vertices in a clockwise fashion For each triplet calculate the offset position given the fibre radius Check for overlapping vertices and ones that lie outside the original hull - recalculate position to offset from or ignore if all overlapping Calculate Area and Perimeter if successfully generated offset vertices to replicate eroded throat Translate back into 3D Any Errors encountered result in the throat area being zero and no vertices being passed back These Errors are not coding mistakes but failures to obtain an eroded facet with non-zero area: Error 1: Less than 3 vertices in the convex hull - Should never happen (unless 2 points are incredibly close together) Error 2: The largest span of points is less than twice the fibre radius (i.e. throat will definitley be occluded) Error 3: All the offset vertices overlap with at least one other vertex - Throat fully occluded Error 4: Not enough offset vertices to continue - Throat fully occluded Error 5: An offset vertex is outside the original set of points - Throat fully occluded """ z_axis = [0, 0, 1] throat_area = 0.0 throat_perimeter = 0.0 output_offset = [] Error = 0 " For boundaries some facets will already be aligned with the axis - if this is the case a rotation is unnecessary and could also cause problems " angle = tr.angle_between_vectors(normal, z_axis) if (angle == 0.0) or (angle == np.pi): "We are already aligned" rotate_input = False facet = verts else: rotate_input = True M = tr.rotation_matrix(tr.angle_between_vectors(normal, z_axis), tr.vector_product(normal, z_axis)) facet = np.dot(verts, M[:3, :3].T) x = facet[:, 0] y = facet[:, 1] z = facet[:, 2] " Work out span of points and set axes scales to cover this and be equal in both dimensions " x_range = x.max() - x.min() y_range = y.max() - y.min() if (x_range > y_range): my_range = x_range else: my_range = y_range if (np.around(z.std(), 3) != 0.000): print("Rotation failed") facet_coords_2D = np.column_stack((x, y)) hull = ConvexHull(facet_coords_2D, qhull_options='QJ') verts_2D = facet_coords_2D[hull.vertices] offset = self._outer_offset(verts_2D, fibre_rad) " At this point we may have overlapping areas for which we need to offset from a new point " overlap_array, sweep_radius, line_points = self._set_overlap( verts_2D, offset) #first_array = overlap_array temp_vert_list = [] #new_vert_list=[] if (len(verts_2D) < 3): "Error: Fused Too Many Verts" Error = 1 elif (my_range < fibre_rad * 2): "Error: Facet Too small to Erode" Error = 2 else: if overlap_array.any() == False: " If no overlaps don't worry" "no overlaps" elif self._all_overlap(overlap_array) == True: " If all overlaps then throat is fully occluded" "Error: Throat fully occluded" Error = 3 else: " If one or two sets of overlaps exist and at least one vertex is not overlapped then we need to do a bit more work " " Do some linalg to find a new point to offset from saving un-overlapped verts and newly created verts in a temporary list " count = 0 temp_verts = verts_2D while True: temp_vert_list = [] for i in range(np.shape(line_points)[0]): if np.sum(overlap_array[i]) == 0.0: temp_vert_list.append(temp_verts[i]) else: my_lines = [] for j in range(np.shape(line_points)[0]): if overlap_array[i][j] == 1 and overlap_array[ j][i] == 1: list_a = line_points[i][j] list_b = line_points[j][i] my_lines = self._symmetric_difference( list_a, list_b) my_lines = np.asarray(my_lines) if len(my_lines) == 2: try: quad_points = temp_verts[my_lines] my_new_point = self._new_point(quad_points) temp_vert_list.append(my_new_point) except IndexError: print("IndexError: " + str(my_lines)) except TypeError: print("TypeError: " + str(my_lines)) #new_vert_list.append(my_new_point) temp_verts = np.asarray(misc.unique_list(temp_vert_list)) #new_vert_list=np.asarray(self._unique_list(new_vert_list)) #if len(verts_2D) >=3: offset = self._outer_offset(temp_verts, fibre_rad) overlap_array, sweep_radius, line_points = self._set_overlap( temp_verts, offset) #else: #Error = 4 if overlap_array.any() == False: break elif self._all_overlap(overlap_array) == True: Error = 3 break elif len(temp_verts) < 3: Error = 4 break else: count += 1 temp_verts = np.asarray( self._fuse_verts(verts=temp_verts, percentage=0.05 * count)) offset = self._outer_offset(temp_verts, fibre_rad) overlap_array, sweep_radius, line_points = self._set_overlap( temp_verts, offset) " Continue Looping until one of the above conditions is true or counter reaches 10" if count >= 10: break if len(offset) >= 3 and Error == 0: " Now also check whether any of the offset points lie outside the original convex hull " original_area = np.around(self._PolyArea2D(verts_2D), 10) all_points = np.concatenate((verts_2D, offset), axis=0) try: total_hull = ConvexHull( all_points, qhull_options='Pp') #ignores very small angles total_area = np.around( self._PolyArea2D(all_points[total_hull.vertices]), 10) except sp.spatial.qhull.QhullError: print(all_points) total_area = 999 Error = 5 #total_area=0 offset_hull = ConvexHull(offset) offset_verts_2D = offset[offset_hull.vertices] if (total_area > original_area): # Throat is fully occluded " Don't do anything " if Error != 5: Error = 6 #print("First Array") #print(first_array) #print("Second Array") #print(overlap_array) else: throat_area = self._PolyArea2D(offset_verts_2D) throat_perimeter = self._PolyPerimeter2D(offset_verts_2D) " Make 3D again in rotated plane " offset_verts_3D = np.column_stack( (offset_verts_2D, z[0:len(offset_verts_2D)])) " Get matrix to un-rotate the co-ordinates back to the original orientation if we rotated in the first place" if (rotate_input): M1 = tr.inverse_matrix(M) " Unrotate the offset coordinates " output_offset = np.dot(offset_verts_3D, M1[:3, :3].T) else: output_offset = offset_verts_3D return throat_area, throat_perimeter, output_offset, Error
def distance_transform(network, geometry, offset, **kwargs): r""" Use the Voronoi vertices and perform image analysis to obtain throat properties """ import math import numpy as np from skimage.morphology import convex_hull_image from skimage.measure import regionprops from scipy import ndimage Nt = geometry.num_throats() area = sp.zeros(Nt) perimeter = sp.zeros(Nt) centroid = sp.zeros([Nt, 3]) incentre = sp.zeros([Nt, 3]) inradius = sp.zeros(Nt) equiv_diameter = sp.zeros(Nt) eroded_verts = sp.ndarray(Nt, dtype=object) res = 200 vertices = geometry["throat.vertices"] normals = geometry["throat.normal"] z_axis = [0, 0, 1] for i in range(Nt): " For boundaries some facets will already be aligned with the axis - if this is the case a rotation is unnecessary and could also cause problems " angle = tr.angle_between_vectors(normals[i], z_axis) if (angle == 0.0) or (angle == np.pi): "We are already aligned" rotate_facet = False facet = vertices[i] else: rotate_facet = True M = tr.rotation_matrix( tr.angle_between_vectors(normals[i], z_axis), tr.vector_product(normals[i], z_axis)) facet = np.dot(vertices[i], M[:3, :3].T) x = facet[:, 0] y = facet[:, 1] z = facet[:, 2] "Get points in 2d for image analysis" pts = np.column_stack((x, y)) "translate points so min sits at the origin" translation = [pts[:, 0].min(), pts[:, 1].min()] pts -= translation order = np.int(math.ceil(-np.log10(np.max(pts)))) "Normalise and scale the points so that largest span equals the resolution to save on memory and create clear image" max_factor = np.max([pts[:, 0].max(), pts[:, 1].max()]) f = res / max_factor "Scale the offset and define a circular structuring element with radius" r = f * offset "Only proceed if r is less than half the span of the image" if r <= res / 2: pts *= f minp1 = pts[:, 0].min() minp2 = pts[:, 1].min() maxp1 = pts[:, 0].max() maxp2 = pts[:, 1].max() img = np.zeros([ np.int(math.ceil(maxp1 - minp1) + 1), np.int(math.ceil(maxp2 - minp2) + 1) ]) int_pts = np.around(pts, 0).astype(int) for pt in int_pts: img[pt[0]][pt[1]] = 1 "Pad with zeros all the way around the edges" img_pad = np.zeros([np.shape(img)[0] + 2, np.shape(img)[1] + 2]) img_pad[1:np.shape(img)[0] + 1, 1:np.shape(img)[1] + 1] = img "All points should lie on this plane but could be some rounding errors so use the order parameter" z_plane = sp.unique(np.around(z, order + 2)) if len(z_plane) > 1: print("rotation for image analysis failed") "Fill in the convex hull polygon" convhullimg = convex_hull_image(img_pad) "Perform a Distance Transform and black out points less than r to create binary erosion" "This is faster than performing an erosion and dt can also be used later to find incircle" eroded = ndimage.distance_transform_edt(convhullimg) #eroded = dt.copy() eroded[eroded <= r] = 0 eroded[eroded > r] = 1 "If we are left with less than 3 non-zero points then the throat is fully occluded" if np.sum(eroded) >= 3: "Do some image analysis to extract the key properties" regions = regionprops( eroded[1:np.shape(img)[0] + 1, 1:np.shape(img)[1] + 1].astype(int)) if len( regions ) == 1: # Change this to cope with genuine multi-region throats for props in regions: x0, y0 = props.centroid equiv_diameter[i] = props.equivalent_diameter area[i] = props.area perimeter[i] = props.perimeter coords = props.coords "Undo the translation, scaling and truncation on the centroif" centroid2d = [x0, y0] / f centroid2d += (translation) centroid3d = np.concatenate((centroid2d, z_plane)) "Distance transform the eroded facet to find the incentre and inradius" #dt[dt>r] -= r dt = ndimage.distance_transform_edt(eroded) inx0, iny0 = np.asarray( np.unravel_index(dt.argmax(), dt.shape)).astype(float) incentre2d = [inx0, iny0] "Undo the translation, scaling and truncation on the incentre" incentre2d /= f incentre2d += (translation) incentre3d = np.concatenate((incentre2d, z_plane)) "The offset vertices will be those in the coords that are closest to the originals" offset_verts = [] for pt in int_pts: vert = np.argmin(np.sum(np.square(coords - pt), axis=1)) if vert not in offset_verts: offset_verts.append(vert) "If we are left with less than 3 different vertices then the throat is fully occluded as we can't make a shape with non-zero area" if len(offset_verts) >= 3: offset_coords = coords[offset_verts].astype(float) "Undo the translation, scaling and truncation on the offset_verts" offset_coords /= f offset_coords_3d = np.vstack( (offset_coords[:, 0] + translation[0], offset_coords[:, 1] + translation[1], np.ones(len(offset_verts)) * z_plane)).T " Get matrix to un-rotate the co-ordinates back to the original orientation if we rotated in the first place" if (rotate_facet): MI = tr.inverse_matrix(M) " Unrotate the offset coordinates " incentre[i] = np.dot(incentre3d, MI[:3, :3].T) centroid[i] = np.dot(centroid3d, MI[:3, :3].T) eroded_verts[i] = np.dot(offset_coords_3d, MI[:3, :3].T) else: incentre[i] = incentre3d centroid[i] = centroid3d eroded_verts[i] = offset_coords_3d inradius[i] = dt.max() "Undo scaling on other parameters" area[i] /= f * f perimeter[i] /= f equiv_diameter[i] /= f inradius[i] /= f else: area[i] = 0 perimeter[i] = 0 equiv_diameter[i] = 0 if kwargs["set_dependent"] == True: geometry["throat.area"] = area geometry["throat.perimeter"] = perimeter geometry["throat.centroid"] = centroid geometry["throat.diameter"] = equiv_diameter geometry["throat.indiameter"] = inradius * 2 geometry["throat.incentre"] = incentre return eroded_verts
def get_throat_geom(verts,normal,fibre_rad): r""" For one set of vertices defining a throat return the key properties This is the main loop for calling other sub-routines. General Method: For each connection or throat defined by the shared vertices Rotate the vertices to align with the xy-plane and get rid of z-coordinate Compute the convex hull of the 2D points giving a set of simplices which define neighbouring vertices in a clockwise fashion For each triplet calculate the offset position given the fibre radius Check for overlapping vertices and ones that lie outside the original hull - recalculate position to offset from or ignore if all overlapping Calculate Area and Perimeter if successfully generated offset vertices to replicate eroded throat Translate back into 3D Any Errors encountered result in the throat area being zero and no vertices being passed back These Errors are not coding mistakes but failures to obtain an eroded facet with non-zero area: Error 1: Less than 3 vertices in the convex hull - Should never happen (unless 2 points are incredibly close together) Error 2: The largest span of points is less than twice the fibre radius (i.e. throat will definitley be occluded) Error 3: All the offset vertices overlap with at least one other vertex - Throat fully occluded Error 4: Not enough offset vertices to continue - Throat fully occluded Error 5: An offset vertex is outside the original set of points - Throat fully occluded """ z_axis = [0,0,1] throat_area = 0.0 throat_perimeter = 0.0 throat_COM = np.zeros([1,3]) output_offset = [] Error = 0 " For boundaries some facets will already be aligned with the axis - if this is the case a rotation is unnecessary and could also cause problems " angle = tr.angle_between_vectors(normal,z_axis) if (angle==0.0)or(angle==np.pi): "We are already aligned" rotate_input = False facet = verts else: rotate_input = True M = tr.rotation_matrix(tr.angle_between_vectors(normal,z_axis),tr.vector_product(normal,z_axis)) facet = np.dot(verts,M[:3,:3].T) x = facet[:,0] y = facet[:,1] z = facet[:,2] " Work out span of points and set axes scales to cover this and be equal in both dimensions " x_range = x.max() - x.min() y_range = y.max() - y.min() if (x_range > y_range): my_range = x_range else: my_range = y_range if (np.around(z.std(),3)!=0.000): print("Rotation failed") facet_coords_2D = np.column_stack((x,y)) hull = ConvexHull(facet_coords_2D,qhull_options='QJ Pp') verts_2D = facet_coords_2D[hull.vertices] offset = outer_offset(verts_2D,fibre_rad) " At this point we may have overlapping areas for which we need to offset from a new point " overlap_array,sweep_radius,line_points = set_overlap(verts_2D,offset) #first_array = overlap_array temp_vert_list=[] #new_vert_list=[] if (len(verts_2D) <3): "Error: Fused Too Many Verts" Error = 1 elif(my_range < fibre_rad*2): "Error: Facet Too small to Erode" Error = 2 else: if overlap_array.any()==False: " If no overlaps don't worry" "no overlaps" elif all_overlap(overlap_array)==True: " If all overlaps then throat is fully occluded" "Error: Throat fully occluded" Error = 3 else: " If one or two sets of overlaps exist and at least one vertex is not overlapped then we need to do a bit more work " " Do some linalg to find a new point to offset from saving un-overlapped verts and newly created verts in a temporary list " count = 0 temp_verts = verts_2D while True: temp_vert_list=[] for i in range(np.shape(line_points)[0]): if np.sum(overlap_array[i])==0.0: temp_vert_list.append(temp_verts[i]) else: my_lines=[] for j in range(np.shape(line_points)[0]): if overlap_array[i][j] ==1 and overlap_array[j][i]==1: list_a = line_points[i][j] list_b = line_points[j][i] my_lines = symmetric_difference(list_a,list_b) my_lines=np.asarray(my_lines) if len(my_lines)==2: try: quad_points=temp_verts[my_lines] my_new_point = new_point(quad_points) temp_vert_list.append(my_new_point) except IndexError: print("IndexError: "+str(my_lines)) except TypeError: print("TypeError: "+str(my_lines)) #new_vert_list.append(my_new_point) temp_verts=np.asarray(misc.unique_list(temp_vert_list)) #new_vert_list=np.asarray(self._unique_list(new_vert_list)) #if len(verts_2D) >=3: offset = outer_offset(temp_verts,fibre_rad) overlap_array,sweep_radius,line_points = set_overlap(temp_verts,offset) #else: #Error = 4 if overlap_array.any()==False: break elif all_overlap(overlap_array)==True: Error = 3 break elif len(temp_verts) <3: Error = 4 break else: count+=1 temp_verts = np.asarray(fuse_verts(verts=temp_verts,percentage=0.05*count)) offset = outer_offset(temp_verts,fibre_rad) overlap_array,sweep_radius,line_points = set_overlap(temp_verts,offset) " Continue Looping until one of the above conditions is true or counter reaches 10" if count >= 10: break if len(offset) >= 3 and Error == 0: " Now also check whether any of the offset points lie outside the original convex hull " original_area = np.around(PolyArea2D(verts_2D),10) all_points = np.concatenate((verts_2D,offset),axis=0) try: total_hull = ConvexHull(all_points,qhull_options='QJ Pp') #ignores very small angles total_area = np.around(PolyArea2D(all_points[total_hull.vertices]),10) except np.spatial.qhull.QhullError: print(all_points) total_area =999 Error = 5 #total_area=0 offset_hull = ConvexHull(offset,qhull_options='QJ Pp') offset_verts_2D = offset[offset_hull.vertices] if (total_area>original_area): # Throat is fully occluded " Don't do anything " if Error != 5: Error = 6 #print("First Array") #print(first_array) #print("Second Array") #print(overlap_array) else: throat_area = PolyArea2D(offset_verts_2D) throat_perimeter = PolyPerimeter2D(offset_verts_2D) throat_COM_2D = PolyWeightedCentroid2D(offset_verts_2D) throat_COM = np.hstack((throat_COM_2D,z[0])) " Make 3D again in rotated plane " offset_verts_3D = np.column_stack((offset_verts_2D,z[0:len(offset_verts_2D)])) " Get matrix to un-rotate the co-ordinates back to the original orientation if we rotated in the first place" if (rotate_input): M1 = tr.inverse_matrix(M) " Unrotate the offset coordinates " output_offset = np.dot(offset_verts_3D,M1[:3,:3].T) throat_COM = np.dot(throat_COM,M1[:3,:3].T) else: output_offset = offset_verts_3D return throat_area, throat_perimeter, output_offset, throat_COM, Error