Beispiel #1
0
def find_intersect_line_of_planes(plane_0, plane_1, use_double_rounding=False,
                                  round_adjust=0, normalize=False):
    # returns an arbitrary point where the provided planes intersect.
    # if they do not intersect, None will be returned instead.
    plane_0 = Plane(plane_0)
    plane_1 = Plane(plane_1)
    plane_0.normalize()
    plane_1.normalize()
    plane_0_dir = matrices.Ray(plane_0[:3])
    plane_1_dir = matrices.Ray(plane_1[:3])
    line_dir = matrices.Ray.cross(plane_0_dir, plane_1_dir)
    determinant = line_dir.mag_sq
    if determinant <= float_info.epsilon:
        # planes are parallel. ignore
        return

    line_point = (
        (matrices.Ray.cross(line_dir, plane_0_dir) * plane_1[3]) +
        (matrices.Ray.cross(plane_1_dir, line_dir) * plane_0[3])) / determinant

    max_comp = 2
    if abs(line_dir[0]) >= abs(line_dir[1]):
        if abs(line_dir[0]) >= abs(line_dir[2]):
            max_comp = 0
    elif abs(line_dir[1]) >= abs(line_dir[2]):
        max_comp = 1

    line_point -= line_dir * (line_point[max_comp] / line_dir[max_comp])
    if normalize:
        line_dir.normalize()

    return line_point, line_dir
Beispiel #2
0
def find_intersect_point_of_lines(line_0, line_1, use_double_rounding=False,
                                  round_adjust=0, ignore_check=False):
    # returns an arbitrary point where the provided lines intersect.
    # if they do not intersect, None will be returned instead.
    v0, d0 = matrices.Point(line_0[0]), matrices.Ray(line_0[1])
    v1, d1 = matrices.Point(line_1[0]), matrices.Ray(line_1[1])
    assert len(v0) == len(v1)
    assert len(d0) == len(d1)
    if d0.is_zero or d1.is_zero:
       # one or both vectors are the zero-vector
        return None

    try:
        reduced, result = matrices.Matrix(
            [(a, -b) for a, b in zip(d0, d1)]).row_reduce(
            matrices.Matrix([v0[i] - v1[i] for i in range(len(v0))]))
    except matrices.CannotRowReduce:
        return None

    intersect_a = v0 - d0 * result[0][0]
    intersect_b = v1 - d1 * result[1][0]
    if ignore_check or matrices.are_vectors_equal(
            intersect_a, intersect_b, use_double_rounding, round_adjust):
        # point lies on both lines
        return intersect_a + (intersect_b - intersect_a) / 2

    return None
Beispiel #3
0
def point_distance_to_line(point, line, use_double_rounding=False,
                           round_adjust=0):
    mantissa_len = (53 if use_double_rounding else 23)
    line_point = matrices.Point(line[0])
    line_dir   = matrices.Ray(line[1]).normalized
    dist = matrices.Ray.cross(line_dir, line_point - point).mag

    # return True if point is on forward side of plane, otherwise False
    # take into account rounding errors for 32bit floats
    delta_max = 2**(
        int(ceil(log(abs(dist) + float_info.epsilon, 2))) -
        mantissa_len) + abs(round_adjust)
    if abs(dist) < delta_max:
        # point lies on plane
        return 0.0
    return dist
Beispiel #4
0
def get_is_valid_edge_loop(edge_loop, plane, verts):
    vert_ct = len(edge_loop)
    if vert_ct < 3:
        return False

    # if the edge loop would construct triangles facing the
    # wrong direction, we need to reverse the edge loop
    for i in range(vert_ct):
        tri_plane = matrices.Ray(matrices.vertex_cross_product(
            verts[edge_loop[i]],
            verts[edge_loop[(i + 1) % vert_ct]],
            verts[edge_loop[(i + 2) % vert_ct]]))

        if matrices.dot_product(tri_plane, plane[: 3]) < 0:
            # facing opposite directions
            return False

    return True
Beispiel #5
0
def planes_to_verts_and_edge_loops(planes, center, plane_dir=True, max_plane_ct=32,
                                   use_double_rounding=False, round_adjust=0):
    assert len(center) == 3
    # make a set out of the planes to remove duplicates
    planes = list(set(tuple(Plane(p).normalized) for p in planes))
    indices_by_planes = {p: set() for p in planes}

    if len(planes) > max_plane_ct:
        raise ValueError(
            "Provided more planes(%s) than the max plane count(%s)" %
            (len(planes), max_plane_ct))

    is_point_inside_form = (is_point_on_front_side_of_planes if plane_dir
                            else is_point_on_back_side_of_planes)

    # find the intersect points of all planes within the polyhedron
    intersects = []
    intersect_weights = []
    for i in range(len(planes)):
        p0 = planes[i]
        p0_indices = indices_by_planes[p0]
        for j in range(len(planes)):
            if j == i: continue
            p1 = planes[j]
            p1_indices = indices_by_planes[p1]
            for k in range(len(planes)):
                if k == i or k == j: continue
                p2 = planes[k]
                p2_indices = indices_by_planes[p2]

                intersect = find_intersect_point_of_planes(p0, p1, p2)
                if intersect is None or not is_point_inside_form(
                        intersect, planes, True, round_adjust=round_adjust):
                    continue

                weight = abs(matrices.Ray.cross(p0[: -1], p1[: -1]) *
                             matrices.Ray(p2[: -1]))
                better_vert_index = None
                similar_verts = set()
                for w in range(len(intersects)):
                    if matrices.are_vectors_equal(
                            intersects[w], intersect,
                            round_adjust=round_adjust / weight):
                        if intersect_weights[w] > weight:
                            better_vert_index = w
                        else:
                            similar_verts.add(w)

                if better_vert_index is None:
                    # adding a new vert
                    vert_index = len(intersects)
                    intersects.append(intersect)
                    intersect_weights.append(weight)
                else:
                    # using a better intersect than this one
                    vert_index = better_vert_index
                    intersect = intersects[better_vert_index]
                    weight = intersect_weights[better_vert_index]

                # overwrite existing verts with a more accurate one
                for v_i in similar_verts:
                    intersects[v_i] = intersect
                    intersect_weights[v_i] = weight

                p0_indices.add(vert_index)
                p1_indices.add(vert_index)
                p2_indices.add(vert_index)

    vert_map = {}
    vert_rebase_map = [None] * len(intersects)
    pruned_intersects = []
    pruned_intersect_weights = []
    for i in range(len(intersects)):
        vert = tuple(intersects[i])
        if vert not in vert_map:
            vert_map[vert] = len(vert_map)
            pruned_intersects.append(intersects[i])
            pruned_intersect_weights.append(intersect_weights[i])
        vert_rebase_map[i] = vert_map[vert]

    for indices in indices_by_planes.values():
        new_indices = set()
        for i in indices:
            new_indices.add(vert_rebase_map[i])
        indices.clear()
        indices.update(new_indices)

    intersects = pruned_intersects
    intersect_weights = pruned_intersect_weights
    #for i in range(len(intersects)):
    #    print(intersect_weights[i], intersects[i])

    # Get intersection lines by crossing each plane with each other plane.
    lines_by_planes = {plane: set() for plane in planes}
    for p0_i in range(len(planes) - 1):
        for p1_i in range(p0_i + 1, len(planes)):
            p0 = planes[p0_i]
            p1 = planes[p1_i]

            line = find_intersect_line_of_planes(p0, p1)
            if line is None:
                continue

            lines_by_planes[p0].add((tuple(line[0]), tuple(line[1])))
            lines_by_planes[p1].add((tuple(line[0]), tuple(-line[1])))


    edges_by_planes = {plane: set() for plane in planes}
    used_vert_indices = set()
    for plane, lines in lines_by_planes.items():
        used_vert_counts = {i: [] for i in indices_by_planes[plane]}

        # count how many lines each vert lies on
        for line in lines:
            line_weight = matrices.Ray(line[1]).mag
            vert_indices_on_line = set()
            for v_i in used_vert_counts:
                if is_point_on_line(intersects[v_i], line,
                                    round_adjust=(round_adjust/(
                                        intersect_weights[v_i] * line_weight))):
                    vert_indices_on_line.add(v_i)

            # filter out verts only intersecting one line
            if len(vert_indices_on_line) >= 2:
                for v_i in vert_indices_on_line:
                    used_vert_counts[v_i].append(line)

        verts_on_lines = {line: [] for line in lines}
        for v_i, used_lines in used_vert_counts.items():
            if len(used_lines) < 2: continue
            for line in used_lines:
                verts_on_lines[line].append(v_i)

        for line in lines:
            if len(verts_on_lines[line]) < 2:
                continue

            line_point, line_dir = line
            sorted_verts_on_line = {}
            axis = 0
            # figure out which axis has the most precision
            for i in range(1, len(line_dir)):
                if abs(line_dir[i]) > abs(line_dir[axis]):
                    axis = i

            # sort the vertices by how far along the line they are
            for v_i in verts_on_lines[line]:
                axis_val = intersects[v_i][axis]
                dist = (axis_val - line_point[axis]) / line_dir[axis]
                sorted_verts_on_line[dist] = v_i

            verts_on_line = [sorted_verts_on_line[dist]
                             for dist in sorted(sorted_verts_on_line)]

            # if the edge chain will go opposite the line, reverse it
            if matrices.Ray(intersects[verts_on_line[0]] -
                            intersects[verts_on_line[-1]]) * matrices.Ray(line[1]) < 0:
                verts_on_line = verts_on_line[::-1]

            # generate edges that ascendingly follow this line
            for i in range(len(verts_on_line) - 1):
                v0_i = verts_on_line[i]
                v1_i = verts_on_line[i + 1]
                # planes intersect and are not parallel
                edges_by_planes[plane].add((v0_i, v1_i))
                used_vert_indices.update((v0_i, v1_i))


    # prune the unused verts
    verts = [None] * len(used_vert_indices)
    vert_map = {}
    i = 0
    for vert_index in used_vert_indices:
        vert_map[vert_index] = i
        verts[i] = intersects[vert_index]
        i += 1

    for plane, edges in edges_by_planes.items():
        new_edges = set()
        for edge in edges:
            new_edges.add((vert_map[edge[0]], vert_map[edge[1]]))
        edges.clear()
        edges.update(new_edges)

    edge_loops = []
    # loop over each edge and put together an edge loop list
    # by visiting the edges shared by each vert index
    for plane, edges in edges_by_planes.items():
        if not edges:
            continue

        try:
            edge_loop = edges_to_edge_loop(edges, plane, verts)
        except Exception:
            from traceback import format_exc
            print(format_exc())
            continue

        if not edge_loop:
            continue

        edge_loops.append(edge_loop)

    #print("PLANES:", )
    #i = 0
    #for plane in edges_by_planes:
    #    print(i, plane)
    #    for e in edges_by_planes[plane]:
    #        print(e)
    #    print()
    #    i += 1

    #i = 0
    #print("VERTS:", len(verts))
    #for vert in verts:
    #    print(i, vert)
    #    i += 1

    #print("TRIS:", sum(len(edge_loop) - 2 for edge_loop in edge_loops))
    #for edge_loop in edge_loops:
    #    print(edge_loop)

    return verts, edge_loops