Example #1
0
    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
Example #2
0
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
Example #3
0
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
Example #5
0
    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
Example #6
0
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
Example #7
0
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