def phi_distance(polar_drive: Iterable[float], polar_driven: Iterable[float], k: int = 1,
                 distance_function=standard_deviation_distance) -> Tuple[float, List[float], List[float], float, float]:
    """
    Get the distance between two phi functions
    :param polar_drive: polar coordinates of the drive contour
    :param polar_driven: polar coordinates of the driven contour
    :param k: the k parameter
    :param distance_function: the distance function used to calculate the distance
    :param torque_weight: weight of torque term
    :return: the distance between phi functions, two d_phi functions and center distances
    """
    y_drive, dist_drive, phi_drive = compute_dual_gear(polar_drive)
    y_driven, dist_driven, phi_driven = compute_dual_gear(polar_driven)
    y_driven, dist_driven, phi_driven = compute_dual_gear(y_driven)
    d_phi_drive = list(differentiate_function(pre_process(phi_drive)))
    d_phi_driven = list(differentiate_function(pre_process(phi_driven)))
    # d_phi_driven = [1.0 / phi for phi in differentiate_function(pre_process(phi_driven))]

    offset = align(d_phi_drive, d_phi_driven, distance_function=distance_function, k=k)
    if k != 1:
        assert len(d_phi_driven) % k == 0
        extended_d_phi_driven = list(
            extend_part(d_phi_driven, offset, offset + int(len(d_phi_driven) / k), len(d_phi_drive)))
        offset = 0
    else:
        extended_d_phi_driven = d_phi_driven
    return distance_function(d_phi_drive, extended_d_phi_driven[offset:] + extended_d_phi_driven[:offset]), \
           d_phi_drive, d_phi_driven, dist_drive, dist_driven
def gradual_average(drive_model: Model, driven_model: Model, drive_center: Tuple[float, float],
                    driven_center: Tuple[float, float], count_of_averages: int):
    """
    Gradually average two contours
    :param drive_model: The drive model
    :param driven_model: The driven model
    :param drive_center: center of drive
    :param driven_center: center of driven
    :param count_of_averages: count of average values
    :return: None
    """
    debugger, opt_config, plotter = init((drive_model, driven_model), 'optimization_config.yaml')
    drive_contour, driven_contour = get_inputs(debugger, drive_model, driven_model, plotter)

    distance, d_drive, d_driven, dist_drive, dist_driven = \
        contour_distance(drive_contour, drive_center, driven_contour, driven_center, 1024)
    for average in np.linspace(0, 1, count_of_averages, True):
        center_dist = dist_drive * 0.5 + dist_driven * 0.5
        reconstructed_drive = rebuild_polar(center_dist, align_and_average(d_drive, d_driven, average))
        reconstructed_driven, center_dist, phi = compute_dual_gear(list(reconstructed_drive))
        reconstructed_drive_contour = toCartesianCoordAsNp(reconstructed_drive, 0, 0)
        reconstructed_driven_contour = toCartesianCoordAsNp(reconstructed_driven, center_dist, 0)
        reconstructed_driven_contour = np.array(rotate(reconstructed_driven_contour, phi[0], (center_dist, 0)))
        average_str = '%1.8f' % average
        plotter.draw_contours(debugger.file_path(average_str + '.png'), [
            ('math_drive', reconstructed_drive_contour),
            ('math_driven', reconstructed_driven_contour)
        ], [(0, 0), (center_dist, 0)])
        save_contour(debugger.file_path(average_str + '_drive.dat'), reconstructed_drive_contour)
        save_contour(debugger.file_path(average_str + '_driven.dat'), reconstructed_driven_contour)
示例#3
0
def main_stage_one(drive_model: Model, driven_model: Model, do_math_cut=True, math_animation=False,
                   reply_cut_anim=False, save_cut_anim=True, opt_config='optimization_config.yaml', k=1):
    # initialize logging system, configuration files, etc.
    opt_config = os.path.join(os.path.dirname(__file__), opt_config)
    debugger, opt_config, plotter = init((drive_model, driven_model), opt_config)
    logger.info(f'Optimizing {drive_model.name} with {driven_model.name}')
    plt.close('all')
    character_str = f'{drive_model.name}, {driven_model.name}'
    print('starting ' + character_str)

    # get input polygons
    cart_input_drive, cart_input_driven = get_inputs(debugger, drive_model, driven_model, plotter, uniform=False)
    counts = cart_input_drive.shape[0], cart_input_driven.shape[0]
    start_time = perf_counter_ns()
    cart_input_drive, cart_input_driven = get_inputs(debugger, drive_model, driven_model, plotter, uniform=True)
    print('pre-processing done for ' + character_str)
    pre_processing = perf_counter_ns()

    # optimization
    center, center_distance, cart_drive, score = optimize_center(cart_input_drive, cart_input_driven, debugger,
                                                                 opt_config, plotter, k=k)
    print('optimization done for ' + character_str)
    logger.info(f'score = {score}')

    drive_model.center_point = (0, 0)
    cart_drive = shape_factory.uniform_and_smooth(cart_drive, drive_model)

    start_time = perf_counter_ns()
    *_, center_distance, phi = compute_dual_gear(toExteriorPolarCoord(Point(0, 0), cart_drive, 1024), k)
    # add teeth
    cart_drive = add_teeth((0, 0), center_distance, debugger, cart_drive, drive_model, plotter)

    # rotate and cut
    cart_driven_gear = rotate_and_carve(cart_drive, (0, 0), center_distance, debugger, drive_model, phi, None, k=k,
                                        replay_anim=False, save_anim=False)
    rotate_and_cut = perf_counter_ns()
    logger.info('rotate_and_carve done in' + str(rotate_and_cut - start_time))
    logger.info('count of follower:' + str(cart_driven_gear.shape[0]))

    # save 2D contour
    fabrication.generate_2d_obj(debugger, 'drive_2d_(0,0).obj', cart_drive)
    fabrication.generate_2d_obj(debugger, f'driven_2d_({center_distance, 0}).obj', cart_driven_gear)

    # generate 3D mesh with axle hole
    fabrication.generate_3D_with_axles(8, debugger.file_path('drive_2d_(0,0).obj'),
                                       debugger.file_path(f'driven_2d_({center_distance, 0}).obj'),
                                       (0, 0), (center_distance, 0), debugger, 6)
示例#4
0
def math_cut(drive_model: Model,
             cart_drive: np.ndarray,
             reporter: Reporter,
             plotter: Optional[Plotter],
             animation=False,
             center_point: Optional[Tuple[float, float]] = None):
    center = center_point or drive_model.center_point
    polar_math_drive = toExteriorPolarCoord(Point(center[0], center[1]),
                                            cart_drive, drive_model.sample_num)
    polar_math_driven, center_distance, phi = compute_dual_gear(
        polar_math_drive, k=drive_model.k)

    if animation:
        plot_sampled_function((polar_math_drive, polar_math_driven), (phi, ),
                              reporter.get_math_debug_dir_name(),
                              100,
                              0.001, [(0, 0), (center_distance, 0)], (8, 8),
                              ((-0.5, 1.5), (-1.1, 1.1)),
                              plotter=plotter)

    # save figures
    plotter.draw_contours(
        reporter.file_path('math_drive.png'),
        [('math_drive', toCartesianCoordAsNp(polar_math_drive, 0, 0))], None)
    plotter.draw_contours(
        reporter.file_path('math_driven.png'),
        [('math_driven', toCartesianCoordAsNp(polar_math_driven, 0, 0))], None)
    plotter.draw_contours(
        reporter.file_path('math_results.png'),
        [('math_drive', toCartesianCoordAsNp(polar_math_drive, 0, 0)),
         ('math_driven',
          np.array(
              rotate(
                  list(
                      toCartesianCoordAsNp(polar_math_driven, center_distance,
                                           0)), phi[0],
                  (center_distance, 0))))], [(0, 0), (center_distance, 0)])

    logging.info('math rotate complete')
    logging.info(f'Center Distance = {center_distance}')

    return center_distance, phi, polar_math_drive, polar_math_driven
def sample_result(drive_contour: np.ndarray, drive_polygon: Polygon,
                  sample_window: Tuple[float, float, float, float], k: int) \
        -> Union[Tuple[float, float, float, np.ndarray], None]:
    """
    sample the center of the sample window and get the driven gear
    :param drive_contour: the drive gear contour
    :param drive_polygon: the driving polygon
    :param sample_window: the window in which to take sample (minx, maxx, miny, maxy)
    :param k: the drive/driven ratio
    :return: center_x, center_y, center_distance, the driven gear | None if not possible
    """
    min_x, max_x, min_y, max_y = sample_window
    center_x, center_y = (min_x + max_x) / 2, (min_y + max_y) / 2
    if not drive_polygon.contains(Point(center_x, center_y)):
        return None
    polar_coordinates = toExteriorPolarCoord(Point(center_x,
                                                   center_y), drive_contour,
                                             drive_contour.shape[0])
    driven, center_distance, phi = compute_dual_gear(polar_coordinates, k)
    driven_contour = toCartesianCoordAsNp(driven, 0, 0)
    return center_x, center_y, center_distance, driven_contour
def optimize_center(cart_input_drive,
                    cart_input_driven,
                    debugger,
                    opt_config,
                    plotter,
                    k=1):
    debug_suite = ReportingSuite(debugger, plotter,
                                 plt.figure(figsize=(16, 9)))
    results = sampling_optimization(
        cart_input_drive,
        cart_input_driven,
        opt_config['sampling_count'],
        opt_config['keep_count'],
        opt_config['resampling_accuracy'],
        opt_config['max_sample_depth'],
        debug_suite,
        opt_config['torque_weight'],
        k=k,
        mismatch_penalty=opt_config['mismatch_penalty'])
    results.sort(key=lambda total_score, *_: total_score)
    best_result = results[0]
    logging.info(f'Best result with score {best_result[0]}')
    score, polar_drive = best_result
    polar_driven, center_distance, phi = compute_dual_gear(polar_drive, k)
    drive_contour = toCartesianCoordAsNp(polar_drive, 0, 0)
    driven_contour = toCartesianCoordAsNp(polar_driven, center_distance, 0)
    driven_contour = np.array(
        rotate(driven_contour, phi[0], (center_distance, 0)))
    plotter.draw_contours(debugger.file_path('optimize_result.png'),
                          [('carve_drive', drive_contour),
                           ('carve_driven', driven_contour)],
                          [(0, 0), (center_distance, 0)])
    save_contour(debugger.file_path('optimized_drive.dat'), drive_contour)
    save_contour(debugger.file_path('optimized_driven.dat'), driven_contour)
    return (0, 0), center_distance, toCartesianCoordAsNp(polar_drive, 0,
                                                         0), score
def sampling_optimization(drive_contour: np.ndarray, driven_contour: np.ndarray, sampling_count: Tuple[int, int],
                          keep_count: int, sampling_accuracy: int, iteration_count: int,
                          debugging_suite: ReportingSuite, torque_weight: float = 0.0, k: int = 1,
                          mismatch_penalty=0.5) -> List[Tuple[float, Polar_T]]:
    logger.info(f'Initiating Sampling Optimization with torque_weight = {torque_weight},'
                f' mismatch_penalty = {mismatch_penalty}')
    logger.info(f'k={k}')
    drive_polygon = Polygon(drive_contour)
    driven_polygon = Polygon(driven_contour)
    min_x, min_y, max_x, max_y = drive_polygon.bounds
    window_pairs = (min_x, max_x, min_y, max_y)
    min_x, min_y, max_x, max_y = driven_polygon.bounds
    window_pairs = [(window_pairs, (min_x, max_x, min_y, max_y))]
    x_sample, y_sample = sampling_count

    # start iteration
    results = []  # dummy
    for iteration in range(iteration_count):
        path = debugging_suite.debugger.file_path('iteration_' + str(iteration))
        os.makedirs(path, exist_ok=True)
        # if k == 1:
        window_pairs = list(itertools.chain.from_iterable([
            itertools.product(split_window(drive_window, x_sample, y_sample),
                              split_window(driven_window, x_sample, y_sample))
            for drive_window, driven_window in window_pairs
        ]))
        # else:
        #     # do not allow the driven gear with k to move center
        #     window_pairs = list(itertools.chain.from_iterable([
        #         itertools.product(split_window(drive_window, x_sample, y_sample),
        #                           [driven_window])
        #         for drive_window, driven_window in window_pairs
        #     ]))
        results = sample_in_windows(drive_contour, driven_contour, window_pairs, keep_count,
                                    debugging_suite.sub_suite(os.path.join(path, 'result_')),
                                    sampling_accuracy=sampling_accuracy, torque_weight=torque_weight, k=k,
                                    mismatch_penalty=mismatch_penalty)
        window_pairs = [(drive_window, driven_window) for _, drive_window, driven_window, *__ in results]
        if debugging_suite.plotter is not None:
            for index, final_result in enumerate(results):
                score, *_, reconstructed_drive, max_phi, m_penalty = final_result
                driven, center_distance, phi = compute_dual_gear(reconstructed_drive, k)
                final_drive = toCartesianCoordAsNp(reconstructed_drive, 0, 0)
                final_driven = np.array(
                    psf_rotate(toCartesianCoordAsNp(driven, center_distance, 0), phi[0], (center_distance, 0)))
                debugging_suite.plotter.draw_contours(
                    os.path.join(path, f'final_result_{index}_{"%.6f" % (score,)}.png'),
                    [('carve_drive', final_drive),
                     ('carve_driven', final_driven)],
                    [(0, 0), (center_distance, 0)])
                save_contour(os.path.join(path, f'final_result_{index}_drive.dat'), final_drive)
                save_contour(os.path.join(path, f'final_result_{index}_driven.dat'), final_driven)
                d_drive = differentiate_function(pre_process(phi))
                save_information(os.path.join(path, f'final_result_{index}.txt'), (0, 0), (center_distance, 0), score,
                                 max_dphi_drive=max(d_drive),
                                 actual_distance=score - torque_weight * max_phi,
                                 mismatch_penalty=m_penalty)

    results = results[:keep_count]
    results.sort(key=lambda dist, *_: dist)
    results = [(score, reconstructed_drive)
               for score, drive_window, driven_window, reconstructed_drive, max_phi, m_penalty in results]
    return results
def sample_in_windows(drive_contour: np.ndarray, driven_contour: np.ndarray,
                      window_pairs: List[Tuple[Window_T, Window_T]], keep_count: int,
                      debugging_suite: ReportingSuite, k: int = 1, center_determine_function=center_of_window,
                      sampling_accuracy=1024, torque_weight=0.0, mismatch_penalty: float = 0.5) \
        -> List[Tuple[float, Window_T, Window_T, Polar_T, float, float]]:
    """
    find the best sample windows
    :param drive_contour: the drive contour
    :param driven_contour: the driven contour
    :param window_pairs: pair of windows
    :param keep_count: count of the windows to be kept
    :param debugging_suite: the debugging suite
    :param k: the k of the opposing gear
    :param center_determine_function: function to determine from window to center
    :param sampling_accuracy: number of samples when converting to polar contour
    :param torque_weight: weight of torque term
    :param mismatch_penalty: penalty for the extended d_driven not matching start and end (only for k>1)
    :return: list of (score, drive_window, driven_window, reconstructed_drive, max_phi, mismatch_penalty)
    """
    results = []
    path_prefix = debugging_suite.path_prefix  # store in a directory
    if debugging_suite.figure is not None:
        debugging_suite.figure.clear()  # clear the figure
        plt.figure(debugging_suite.figure.number)
        subplots = debugging_suite.figure.subplots(2, 2)
        update_polygon_subplots(drive_contour, driven_contour, subplots[0])
    else:
        subplots = None
    for index, (drive_window, driven_window) in enumerate(window_pairs):
        center_drive = center_determine_function(drive_window)
        center_driven = center_determine_function(driven_window)
        if not (Polygon(drive_contour).contains(Point(*center_drive)) and Polygon(driven_contour).contains(
                Point(*center_driven))):
            # not good windows
            continue
        # polar, dist, phi = compute_dual_gear(
        #     toExteriorPolarCoord(Point(*center_driven), driven_contour, sampling_accuracy), 1)
        # polar, dist, phi = compute_dual_gear(polar, 1)
        # d_phi = differentiate_function(pre_process(phi))
        # fig, splts = plt.subplots(2, 2, figsize=(9, 9))
        # splts[0][1].plot(*driven_contour.transpose())
        # splts[0][1].axis('equal')
        # splts[1][0].plot(np.linspace(0, 2 * math.pi, len(phi), endpoint=False), phi)
        # splts[1][1].plot(np.linspace(0, 2 * math.pi, len(d_phi), endpoint=False), d_phi, color='red')
        # plt.show() #checkpoint passed
        distance, d_drive, d_driven, dist_drive, dist_driven = \
            contour_distance(drive_contour, center_drive, driven_contour, center_driven, sampling_accuracy, k)
        # splts[1][1].plot(np.linspace(0, 2 * math.pi, len(d_driven), endpoint=False), d_driven, color='blue')
        # plt.show()
        reconstructed_drive = rebuild_polar(0.9, align_and_average(d_drive, d_driven, k=k))
        list_reconstructed_drive = list(reconstructed_drive)
        max_phi = max(differentiate_function(pre_process(compute_dual_gear(list_reconstructed_drive)[-1])))
        final_score = distance + torque_weight * max_phi
        m_penalty = None
        if k != 1:
            m_penalty = mismatch_penalty * abs(d_driven[0] - d_driven[-1])
            logger.info(f'{index} gear: mismatching start = {d_driven[0]}, end = {d_driven[-1]}, penalized {m_penalty}')
            final_score += m_penalty
        logging.info(f'{index} gear: plain score={distance}, max of phi\'={max_phi}')
        results.append((final_score, drive_window, driven_window, list_reconstructed_drive, max_phi, m_penalty))
        if subplots is not None:
            update_polygon_subplots(drive_contour, driven_contour, subplots[0])  # clear sample regions
            reconstructed_driven, plt_center_dist, plt_phi = compute_dual_gear(list_reconstructed_drive, k)
            reconstructed_drive_contour = toCartesianCoordAsNp(reconstructed_drive, 0, 0)
            reconstructed_driven_contour = toCartesianCoordAsNp(reconstructed_driven, 0, 0)
            update_polygon_subplots(reconstructed_drive_contour, reconstructed_driven_contour, subplots[1])
            min_x, max_x, min_y, max_y = drive_window
            sample_region = Rectangle((min_x, min_y), max_x - min_x, max_y - min_y, color='red', fill=False)
            subplots[0][0].add_patch(sample_region)
            min_x, max_x, min_y, max_y = driven_window
            sample_region = Rectangle((min_x, min_y), max_x - min_x, max_y - min_y, color='red', fill=False)
            subplots[0][1].add_patch(sample_region)
            subplots[0][0].scatter(*center_drive, 5)
            subplots[0][1].scatter(*center_driven, 5)
            subplots[1][0].scatter(0, 0, 5)
            subplots[1][1].scatter(0, 0, 5)
            plt.savefig(path_prefix + f'{index}.png')
            save_contour(path_prefix + f'{index}_drive.dat', reconstructed_drive_contour)
            save_contour(path_prefix + f'{index}_driven.dat', reconstructed_driven_contour)
            save_information(path_prefix + f'{index}.txt', center_drive, center_driven, final_score,
                             max_dphi_drive=max_phi, actual_distance=distance, mismatch_penalty=m_penalty)

            # get information about thee phi' functions
            original_figure = plt.gcf()
            figure, new_subplots = plt.subplots(2, 2, figsize=(16, 16))
            new_subplots[0][0].plot(np.linspace(0, 2 * math.pi, len(d_drive), endpoint=False), d_drive)
            new_subplots[0][0].axis([0, 2 * math.pi, 0, 2 * math.pi])
            new_subplots[0][1].plot(np.linspace(0, 2 * math.pi, len(d_driven), endpoint=False), d_driven)
            new_subplots[0][1].axis([0, 2 * math.pi, 0, 2 * math.pi])
            new_subplots[1][0].plot(np.linspace(0, 2 * math.pi, len(d_drive), endpoint=False),
                                    align_and_average(d_drive, d_driven, k=k))
            new_subplots[1][0].axis([0, 2 * math.pi, 0, 2 * math.pi])
            debugging_suite.plotter.draw_contours(
                path_prefix + f'drive_contour_{index}.png',
                [('carve_drive', drive_contour)],
                [center_drive])
            debugging_suite.plotter.draw_contours(
                path_prefix + f'driven_contour_{index}.png',
                [('carve_driven', driven_contour)],
                [center_driven])
            final_driven = np.array(
                psf_rotate(toCartesianCoordAsNp(reconstructed_driven, plt_center_dist, 0), plt_phi[0],
                           (plt_center_dist, 0)))
            debugging_suite.plotter.draw_contours(
                path_prefix + f'reconstructed_contour_{index}.png',
                [('carve_drive', reconstructed_drive_contour),
                 ('carve_driven', final_driven)],
                [(0, 0), (plt_center_dist, 0)])
            if k != 1:
                # then offset shall be 0
                new_subplots[1][1].plot(np.linspace(0, 2 * math.pi, len(d_drive), endpoint=False),
                                        extend_part(d_driven, 0, int(len(d_driven) / k), len(d_drive)))
                new_subplots[1][1].axis('equal')
            plt.axis('equal')
            plt.savefig(path_prefix + f'{index}_functions.png')
            plt.close()
            plt.figure(original_figure.number)
    results.sort(key=lambda dist, *_: dist)
    return results[:keep_count]