def make_cases_obj():
    """Writes obj files demonstrating the main cases of marching cubes"""
    import marching_cubes_gen as gen
    mesh = Mesh()
    highlights = Mesh()
    offset = V3(0, 0, 0)
    for bits, faces in sorted(gen.BASE_CASES.items()):
        verts = set(gen.bits_to_verts(bits))

        # Run marching cubes for a just this case
        def f(x, y, z):
            vert = gen.VERTICES.index((x, y, z))
            return 1 if vert in verts else -1
        case_mesh = marching_cubes_3d_single_cell(f, 0, 0, 0)
        case_mesh = case_mesh.translate(offset)
        mesh.extend(case_mesh)

        # Output the solid verts
        highlight = Mesh([V3(*gen.VERTICES[v]) for v in verts], [])
        highlight = highlight.translate(offset)
        highlights.extend(highlight)

        offset.x += 1.5
        if offset.x > 6:
            offset.x = 0
            offset.y += 1.5

    with open("cases.obj", "w") as f:
        make_obj(f, mesh)
    with open("case_highlights.obj", "w") as f:
        make_obj(f, highlights)
 def edge_to_boundary_vertex(edge):
     """Returns the vertex in the middle of the specified edge"""
     # Find the two vertices specified by this edge, and interpolate between
     # them according to adapt, as in the 2d case
     v0, v1 = EDGES[edge]
     f0 = f_eval[v0]
     f1 = f_eval[v1]
     t0 = 1 - adapt(f0, f1)
     t1 = 1 - t0
     vert_pos0 = VERTICES[v0]
     vert_pos1 = VERTICES[v1]
     return V3(x + vert_pos0[0] * t0 + vert_pos1[0] * t1,
               y + vert_pos0[1] * t0 + vert_pos1[1] * t1,
               z + vert_pos0[2] * t0 + vert_pos1[2] * t1)
Example #3
0
    def norm(x, y, z):

        num_samples = 1000
        sample_points = []

        for i in range(0, num_samples):
            sample_point = V3(0, 0, 0)

            u = random.randint(0, 1000) / 1000.0
            v = random.randint(0, 1000) / 1000.0

            pi = 4 * math.atan(1)

            theta = 2.0 * pi * u
            phi = math.acos(2.0 * v - 1.0)

            # generate pseudorandom location on 2-sphere of radius d
            sample_point.x = d * math.cos(theta) * math.sin(phi)
            sample_point.y = d * math.sin(theta) * math.sin(phi)
            sample_point.z = d * math.cos(phi)

            sample_points.append(sample_point)

        normal = V3(0, 0, 0)

        for sample_counter in range(0, len(sample_points)):
            if f(sample_points[sample_counter].x,
                 sample_points[sample_counter].y,
                 sample_points[sample_counter].z) < 0:
                normal.x += -sample_points[sample_counter].x
                normal.y += -sample_points[sample_counter].y
                normal.z += -sample_points[sample_counter].z

        if 0 == normal.x * normal.x + normal.y * normal.y + normal.z * normal.z:
            normal.x = 1

        return normal.normalize()
Example #4
0
def dual_contour_3d_find_best_vertex(f, f_normal, x, y, z):
    if not ADAPTIVE:
        return V3(x + 0.5, y + 0.5, z + 0.5)

    # Evaluate f at each corner
    v = np.empty((2, 2, 2))
    for dx in (0, 1):
        for dy in (0, 1):
            for dz in (0, 1):
                v[dx, dy, dz] = f(x + dx, y + dy, z + dz)

    # For each edge, identify where there is a sign change.
    # There are 4 edges along each of the three axes
    changes = []
    for dx in (0, 1):
        for dy in (0, 1):
            if (v[dx, dy, 0] > 0) != (v[dx, dy, 1] > 0):
                changes.append(
                    (x + dx, y + dy, z + adapt(v[dx, dy, 0], v[dx, dy, 1])))

    for dx in (0, 1):
        for dz in (0, 1):
            if (v[dx, 0, dz] > 0) != (v[dx, 1, dz] > 0):
                changes.append(
                    (x + dx, y + adapt(v[dx, 0, dz], v[dx, 1, dz]), z + dz))

    for dy in (0, 1):
        for dz in (0, 1):
            if (v[0, dy, dz] > 0) != (v[1, dy, dz] > 0):
                changes.append(
                    (x + adapt(v[0, dy, dz], v[1, dy, dz]), y + dy, z + dz))

    if len(changes) <= 1:
        return None

    # For each sign change location v[i], we find the normal n[i].
    # The error term we are trying to minimize is sum( dot(x-v[i], n[i]) ^ 2)

    # In other words, minimize || A * x - b || ^2 where A and b are a matrix and vector
    # derived from v and n

    normals = []
    for v in changes:
        n = f_normal(v[0], v[1], v[2])
        normals.append([n.x, n.y, n.z])

    return solve_qef_3d(x, y, z, changes, normals)
Example #5
0
def solve_qef_3d(x, y, z, positions, normals):
    # The error term we are trying to minimize is sum( dot(x-v[i], n[i]) ^ 2)
    # This should be minimized over the unit square with top left point (x, y)

    # In other words, minimize || A * x - b || ^2 where A and b are a matrix and vector
    # derived from v and n
    # The heavy lifting is done by the QEF class, but this function includes some important
    # tricks to cope with edge cases

    # This is demonstration code and isn't optimized, there are many good C++ implementations
    # out there if you need speed.

    if settings.BIAS:
        # Add extra normals that add extra error the further we go
        # from the cell, this encourages the final result to be
        # inside the cell
        # These normals are shorter than the input normals
        # as that makes the bias weaker,  we want them to only
        # really be important when the input is ambiguous

        # Take a simple average of positions as the point we will
        # pull towards.
        mass_point = numpy.mean(positions, axis=0)

        normals.append([settings.BIAS_STRENGTH, 0, 0])
        positions.append(mass_point)
        normals.append([0, settings.BIAS_STRENGTH, 0])
        positions.append(mass_point)
        normals.append([0, 0, settings.BIAS_STRENGTH])
        positions.append(mass_point)

    qef = QEF.make_3d(positions, normals)

    residual, v = qef.solve()

    if settings.BOUNDARY:

        def inside(r):
            return x <= r[1][0] <= x + 1 and y <= r[1][1] <= y + 1 and z <= r[
                1][2] <= z + 1

        # It's entirely possible that the best solution to the qef is not actually
        # inside the cell.
        if not inside((residual, v)):
            # If so, we constrain the the qef to the 6
            # planes bordering the cell, and find the best point of those
            r1 = qef.fix_axis(0, x + 0).solve()
            r2 = qef.fix_axis(0, x + 1).solve()
            r3 = qef.fix_axis(1, y + 0).solve()
            r4 = qef.fix_axis(1, y + 1).solve()
            r5 = qef.fix_axis(2, z + 0).solve()
            r6 = qef.fix_axis(2, z + 1).solve()

            rs = list(filter(inside, [r1, r2, r3, r4, r5, r6]))

            if len(rs) == 0:
                # It's still possible that those planes (which are infinite)
                # cause solutions outside the box.
                # So now try the 12 lines bordering the cell
                r1 = qef.fix_axis(1, y + 0).fix_axis(0, x + 0).solve()
                r2 = qef.fix_axis(1, y + 1).fix_axis(0, x + 0).solve()
                r3 = qef.fix_axis(1, y + 0).fix_axis(0, x + 1).solve()
                r4 = qef.fix_axis(1, y + 1).fix_axis(0, x + 1).solve()
                r5 = qef.fix_axis(2, z + 0).fix_axis(0, x + 0).solve()
                r6 = qef.fix_axis(2, z + 1).fix_axis(0, x + 0).solve()
                r7 = qef.fix_axis(2, z + 0).fix_axis(0, x + 1).solve()
                r8 = qef.fix_axis(2, z + 1).fix_axis(0, x + 1).solve()
                r9 = qef.fix_axis(2, z + 0).fix_axis(1, y + 0).solve()
                r10 = qef.fix_axis(2, z + 1).fix_axis(1, y + 0).solve()
                r11 = qef.fix_axis(2, z + 0).fix_axis(1, y + 1).solve()
                r12 = qef.fix_axis(2, z + 1).fix_axis(1, y + 1).solve()

                rs = list(
                    filter(
                        inside,
                        [r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12]))

            if len(rs) == 0:
                # So finally, we evaluate which corner
                # of the cell looks best
                r1 = qef.eval_with_pos((x + 0, y + 0, z + 0))
                r2 = qef.eval_with_pos((x + 0, y + 0, z + 1))
                r3 = qef.eval_with_pos((x + 0, y + 1, z + 0))
                r4 = qef.eval_with_pos((x + 0, y + 1, z + 1))
                r5 = qef.eval_with_pos((x + 1, y + 0, z + 0))
                r6 = qef.eval_with_pos((x + 1, y + 0, z + 1))
                r7 = qef.eval_with_pos((x + 1, y + 1, z + 0))
                r8 = qef.eval_with_pos((x + 1, y + 1, z + 1))

                rs = list(filter(inside, [r1, r2, r3, r4, r5, r6, r7, r8]))

            # Pick the best of the available options
            residual, v = min(rs)

    if settings.CLIP:
        # Crudely force v to be inside the cell
        v[0] = numpy.clip(v[0], x, x + 1)
        v[1] = numpy.clip(v[1], y, y + 1)
        v[2] = numpy.clip(v[2], z, z + 1)

    return V3(v[0], v[1], v[2])
Example #6
0
 def norm(x, y, z):
     return V3(
         (f(x + d, y, z) - f(x - d, y, z)) / 2 / d,
         (f(x, y + d, z) - f(x, y - d, z)) / 2 / d,
         (f(x, y, z + d) - f(x, y, z - d)) / 2 / d,
     ).normalize()
Example #7
0
def circle_normal(x, y, z):
    l = math.sqrt(x * x + y * y + z * z)
    return V3(-x / l, -y / l, -z / l)
Example #8
0
def get_normal_from_mc( src_string,
                        src_z_w,
                        src_C_x, src_C_y, src_C_z, src_C_w,
			src_max_iterations,
			src_threshold,
                        src_x_grid_min, src_x_grid_max,
                        src_y_grid_min, src_y_grid_max,
                        src_z_grid_min, src_z_grid_max,
                        src_x_res, src_y_res, src_z_res,
                        src_make_border,
                        src_write_triangles):


    float3 = ct.c_float * 3
    int1 = ct.c_uint
    float1 = ct.c_float

    z_w = float1(src_z_w)
    
    C_x = float1(src_C_x)
    C_y = float1(src_C_y)
    C_z = float1(src_C_z)
    C_w = float1(src_C_w)

    max_iterations = int1(src_max_iterations)
    threshold  = float1(src_threshold)
    
    x_grid_min = float1(src_x_grid_min)
    x_grid_max = float1(src_x_grid_max)
    y_grid_min = float1(src_y_grid_min)
    y_grid_max = float1(src_y_grid_max)
    z_grid_min = float1(src_z_grid_min)
    z_grid_max = float1(src_z_grid_max)

    x_res = int1(src_x_res)
    y_res = int1(src_y_res)
    z_res = int1(src_z_res)

    output_arr = float3()

    make_border = int1(src_make_border)
    write_triangles = int1(src_write_triangles)

    lib = ct.CDLL("mc_dll.dll")

    lib.get_normal(ct.c_char_p(src_string.encode('utf-8')),
                    z_w,
                    C_x, C_y, C_z, C_w,
                    max_iterations,
                    threshold,
                    x_grid_min, x_grid_max,
                    y_grid_min, y_grid_max,
                    z_grid_min, z_grid_max,
                    x_res, y_res, z_res,
                    output_arr,
                    make_border,
                    write_triangles)

    ret = V3(output_arr[0], output_arr[1], output_arr[2]) 

    return ret