コード例 #1
0
def generate_nfp(nfp_paris):
    """
    :param nfp_paris:
    :return:
    """

    nfp_list = list()

    for pair in nfp_paris:
        poly_a = pair['A']

        poly_a['points'] = nfp_utls.rotate_polygon(
            poly_a['points'], pair['key']['A_rotation'])['points']
        poly_b = pair['B']
        poly_b['points'] = nfp_utls.rotate_polygon(
            poly_b['points'], pair['key']['B_rotation'])['points']

        if pair['key']['inside']:
            nfp = nfp_utls.nfp_rectangle(poly_a['points'], poly_b['points'])

            if nfp_utls.polygon_area(nfp) > 0:
                nfp.reverse()
        else:
            # pair['key']['inside'] == False , so compute no fit polygon between two segments
            # nfp = nfp_utls.nfp_polygon(poly_a, poly_b)    # 使用自己写的生成nfp的函数
            nfp = minkowski_difference(
                poly_a, poly_b)  # 使用 Minkowski_difference和求两个零件的nfp, 考虑用lib
            # print("nfp = ", nfp)

            if nfp_utls.polygon_area(nfp) > 0:
                nfp.reverse()

        nfp_list.append({'key': pair['key'], 'value': nfp})

    return nfp_list
コード例 #2
0
    def set_segments(self, segments_lists):
        """
        :param segments_lists: [[point of segment 1], [], ..., [point of segment 314]]
        :return:
        self.shapes: a list of dictionary, with each dictionary contain information of each segment
        self.shapes: [{area: , p_id: , points:[{'x': , 'y': }...]},... {area: , p_id: , points:[{'x': , 'y': }...]}]
        self.total_segments_area : total area of all segments
        """

        self.shapes = []
        p_id = 1
        total_area = 0

        # patch1 = [[200, 0], [400, 0], [400, 200], [200, 200]]
        # patch2 = [[100, 1000], [300, 1000], [300, 2000], [100, 2000]]

        patch1 = [[950, 1150], [1050, 1150], [1050, 1250], [950, 1250]]
        patch2 = [[1920, 320], [2080, 320], [2080, 480], [1920, 480]]

        patches = [patch1, patch2]

        for segment_cord in segments_lists:
            shape = {
                'area': 0,
                'p_id': str(p_id),
                'points': [{
                    'x': p[0],
                    'y': p[1]
                } for p in segment_cord]
            }
            p_id = p_id + 1

            seg_area = nfp_utls.polygon_area(shape['points'])
            if seg_area > 0:  # 因为设置的是顺时针,所以用公式计算的面积应该小于0
                shape['points'].reverse(
                )  # 确定多边形的线段方向, 多边形方向为逆时针时,S < 0 ;多边形方向为顺时针时,S > 0

            shape['area'] = abs(seg_area)
            total_area += shape['area']
            self.shapes.append(shape)

        for patch in patches:
            shape = {
                'area': 0,
                'p_id': str(p_id),
                'points': [{
                    'x': p[0],
                    'y': p[1]
                } for p in patch]
            }
            p_id = p_id + 1
            seg_area = nfp_utls.polygon_area(shape['points'])
            if seg_area > 0:
                shape['points'].reverse()

            shape['area'] = abs(seg_area)
            total_area += shape['area']
            self.shapes.append(shape)

        self.total_segments_area = total_area
コード例 #3
0
def minkowski_difference(A, B):
    """
    两个多边形的相切空间
    http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/MinkowskiDiff.htm
    :param A:
    :param B:
    :return:
    """
    Ac = [[p['x'], p['y']] for p in A['points']]
    Bc = [[p['x'] * -1, p['y'] * -1] for p in B['points']]
    solution = pyclipper.MinkowskiSum(Ac, Bc, True)
    largest_area = None
    clipper_nfp = None
    for p in solution:
        p = [{'x': i[0], 'y': i[1]} for i in p]
        sarea = nfp_utls.polygon_area(p)
        if largest_area is None or largest_area > sarea:
            clipper_nfp = p
            largest_area = sarea

    clipper_nfp = [{
        'x': clipper_nfp[i]['x'] + Bc[0][0] * -1,
        'y': clipper_nfp[i]['y'] + Bc[0][1] * -1,
    } for i in range(0, len(clipper_nfp))]
    return [clipper_nfp]
コード例 #4
0
    def add_objects(self, objects):
        """add_objects(objects): adds polygon objects to the nester"""
        if not isinstance(objects, list):
            objects = [objects]
        if not self.shapes:
            self.shapes = []

        p_id = 0
        total_area = 0
        for obj in objects:
            points = self.clean_polygon(obj)
            shape = {
                'area': 0,
                'p_id': str(p_id),
                'points': [{
                    'x': p[0],
                    'y': p[1]
                } for p in points],
            }
            # 确定多边形的线段方向
            area = nfp_utls.polygon_area(shape['points'])
            if area > 0:
                shape['points'].reverse()

            shape['area'] = abs(area)
            total_area += shape['area']
            self.shapes.append(shape)

        # 如果是一般布,需要这个尺寸
        self.shapes_max_length = total_area / BIN_HEIGHT * 3
コード例 #5
0
def minkowski_difference(A, B):
    """
    两个多边形的相切空间
    ff.htm.
    :param A:
    :param B:
    :return:
    """
    Ac = [[p['x'], p['y']] for p in A['points']]
    Bc = [[p['x'] * -1, p['y'] * -1] for p in B['points']]
    solution = pyclipper.MinkowskiSum(Ac, Bc, True)
    # print('DDD')
    # print(A)
    # print(B)
    largest_area = None
    clipper_nfp = None
    for p in solution:
        p = [{'x': i[0], 'y': i[1]} for i in p]
        sarea = nfp_utls.polygon_area(p)
        if largest_area is None or largest_area > sarea:
            clipper_nfp = p
            largest_area = sarea

    clipper_nfp = [{
        'x': clipper_nfp[i]['x'] + Bc[0][0] * -1,
        'y': clipper_nfp[i]['y'] + Bc[0][1] * -1
    } for i in range(0, len(clipper_nfp))]
    #print(clipper_nfp)
    return [clipper_nfp]
コード例 #6
0
    def add_objects(self, objects):
        """add_objects(objects): adds polygon objects to the nester"""
        if not isinstance(objects, list):
            objects = [objects]
        if not self.shapes:
            self.shapes = []

        p_id = 0
        total_area = 0
        for obj in objects:
            points = self.clean_polygon(obj)
            shape = {
                'area': 0,
                'p_id': str(p_id),
                'points': [{
                    'x': p[0],
                    'y': p[1]
                } for p in points],
            }
            # Xác định hướng đường thẳng của đa giác
            area = nfp_utls.polygon_area(shape['points'])
            if area > 0:
                shape['points'].reverse()

            shape['area'] = abs(area)
            total_area += shape['area']
            self.shapes.append(shape)

        # Nếu là vải thông thường, kích thước này là bắt buộc
        self.shapes_max_length = total_area / BIN_HEIGHT * 3
コード例 #7
0
def minkow(A, B):
    Ac = [[p['x'], p['y']] for p in A]
    Bc = [[p['x'] * -1, p['y'] * -1] for p in B]

    solution = pyclipper.MinkowskiSum(Ac, Bc, True)
    largest_area = None
    clipper_nfp = None

    for p in solution:
        p = [{'x': i[0], 'y': i[1]} for i in p]
        sarea = nfp_utls.polygon_area(p)
        if largest_area is None or largest_area > sarea:
            clipper_nfp = p
            largest_area = sarea

    clipper_nfp = [{
        'x': clipper_nfp[i]['x'] + Bc[0][0] * -1,
        'y': clipper_nfp[i]['y'] + Bc[0][1] * -1
    } for i in range(0, len(clipper_nfp))]
    return clipper_nfp
コード例 #8
0
def set_target_loop(best, nest):
    """
    Đặt tất cả đồ họa xuống và thoát
    :param best: Một kết quả đang chạy
    :param nest: Nester class
    :return:
    """
    res = best
    total_area = 0
    rate = None
    num_placed = 0
    step = 0
    while 1:
        print("Loop ", step + 1)
        nest.run()
        best = nest.best
        if best['fitness'] <= res['fitness']:
            res = best
            for s_data in res['placements']:
                tmp_total_area = 0.0
                tmp_num_placed = 0

                for move_step in s_data:
                    tmp_total_area += nest.shapes[int(
                        move_step['p_id'])]['area']
                    tmp_num_placed += 1

                tmp_rates = tmp_total_area / abs(
                    nfp_utls.polygon_area(nest.container['points']))

                if (num_placed < tmp_num_placed or total_area < tmp_total_area
                        or rate < tmp_rates):
                    num_placed = tmp_num_placed
                    total_area = tmp_total_area
                    rate = tmp_rates
        # Tất cả đồ họa bị lỗi trước khi thoát
        if num_placed == len(nest.shapes):
            break
    # Đang vẽ
    draw_result(res['placements'], nest.shapes, nest.container,
                nest.container_bounds)
コード例 #9
0
def set_target_loop(best, nest):
    """
    把所有图形全部放下就退出
    :param best: 一个运行结果
    :param nest: Nester class
    :return:
    """
    res = best
    total_area = 0
    rate = None
    num_placed = 0
    while 1:
        nest.run()
        best = nest.best
        if best['fitness'] <= res['fitness']:
            res = best
            for s_data in res['placements']:
                tmp_total_area = 0.0
                tmp_num_placed = 0

                for move_step in s_data:
                    tmp_total_area += nest.shapes[int(
                        move_step['p_id'])]['area']
                    tmp_num_placed += 1

                tmp_rates = tmp_total_area / abs(
                    nfp_utls.polygon_area(nest.container['points']))

                if (num_placed < tmp_num_placed or total_area < tmp_total_area
                        or rate < tmp_rates):
                    num_placed = tmp_num_placed
                    total_area = tmp_total_area
                    rate = tmp_rates
        # 全部图形放下才退出
        if num_placed == len(nest.shapes):
            break
    # 画图
    draw_result(res['placements'], nest.shapes, nest.container,
                nest.container_bounds)
コード例 #10
0
    def add_objects(self, objects, objects_str):
        """加载形状

        Args:
            objects (list): 形状信息
            objects_str (str): 因为打分程序要求输入点的顺序不能改变,使用它来存储这一信息
        """
        if not isinstance(objects, list):
            objects = [objects]
        if not self.shapes:
            self.shapes = []

        self.originalshapes = objects_str
        p_id = 0
        total_area = 0
        for obj in objects:
            points = self.clean_polygon(obj)
            shape = {
                'area': 0,
                'p_id': str(p_id),
                'points': [{
                    'x': p[0],
                    'y': p[1]
                } for p in points]
            }
            # 确定多边形的线段方向
            area = nfp_utls.polygon_area(shape['points'])
            if area > 0:
                shape['points'].reverse()

            shape['area'] = abs(area)
            total_area += shape['area']
            self.shapes.append(shape)

        #积木形状总面积
        self.shapes_total_area = total_area
コード例 #11
0
    def place_paths(self):
        # 排列图形
        if self.bin_polygon is None:
            return None

        # rotate paths by given rotation
        rotated = list()
        for i in range(0, len(self.paths)):
            r = rotate_polygon(self.paths[i][1]['points'], self.paths[i][2])
            r['rotation'] = self.paths[i][2]
            r['source'] = self.paths[i][1]['p_id']
            r['p_id'] = self.paths[i][0]
            rotated.append(r)

        paths = rotated
        # 保存所有转移数据
        all_placements = list()
        # 基因组的适应值
        fitness = 0
        bin_area = abs(polygon_area(self.bin_polygon['points']))
        min_width = None
        while len(paths) > 0:
            placed = list()
            placements = list()
            # add 1 for each new bin opened (lower fitness is better)
            fitness += 1
            for i in range(0, len(paths)):
                path = paths[i]
                # 图形的坐标
                key = json.dumps({
                    'A': '-1',
                    'B': path['p_id'],
                    'inside': True,
                    'A_rotation': 0,
                    'B_rotation': path['rotation'],
                })

                binNfp = self.nfpCache.get(key)
                if binNfp is None or len(binNfp) == 0:
                    continue

                # part unplaceable, skip
                error = False

                # ensure all necessary NFPs exist
                for p in placed:
                    key = json.dumps({
                        'A': p['p_id'],
                        'B': path['p_id'],
                        'inside': False,
                        'A_rotation': p['rotation'],
                        'B_rotation': path['rotation'],
                    })
                    nfp = self.nfpCache.get(key)
                    if nfp is None:
                        error = True
                        break

                # part unplaceable, skip
                if error:
                    continue

                position = None
                if len(placed) == 0:
                    for j in range(0, len(binNfp)):
                        for k in range(0, len(binNfp[j])):
                            if position is None or (
                                    binNfp[j][k]['x'] - path['points'][0]['x']
                                    < position['x']):
                                position = {
                                    'x':
                                    binNfp[j][k]['x'] - path['points'][0]['x'],
                                    'y':
                                    binNfp[j][k]['y'] - path['points'][0]['y'],
                                    'p_id': path['p_id'],
                                    'rotation': path['rotation'],
                                }

                    placements.append(position)
                    placed.append(path)
                    continue

                clipper_bin_nfp = list()
                for j in range(0, len(binNfp)):
                    clipper_bin_nfp.append([[p['x'], p['y']]
                                            for p in binNfp[j]])

                clipper = pyclipper.Pyclipper()

                for j in range(0, len(placed)):
                    p = placed[j]
                    key = json.dumps({
                        'A': p['p_id'],
                        'B': path['p_id'],
                        'inside': False,
                        'A_rotation': p['rotation'],
                        'B_rotation': path['rotation'],
                    })
                    nfp = self.nfpCache.get(key)

                    if nfp is None:
                        continue
                    for k in range(0, len(nfp)):
                        clone = [[
                            np['x'] + placements[j]['x'],
                            np['y'] + placements[j]['y']
                        ] for np in nfp[k]]
                        clone = pyclipper.CleanPolygon(clone)
                        if len(clone) > 2:
                            clipper.AddPath(clone, pyclipper.PT_SUBJECT, True)
                combine_nfp = clipper.Execute(pyclipper.CT_UNION,
                                              pyclipper.PFT_NONZERO,
                                              pyclipper.PFT_NONZERO)
                if len(combine_nfp) == 0:
                    continue

                clipper = pyclipper.Pyclipper()
                clipper.AddPaths(combine_nfp, pyclipper.PT_CLIP, True)
                try:
                    clipper.AddPaths(clipper_bin_nfp, pyclipper.PT_SUBJECT,
                                     True)
                except:
                    print('图形坐标出错', clipper_bin_nfp)

                # choose placement that results in the smallest bounding box
                finalNfp = clipper.Execute(
                    pyclipper.CT_DIFFERENCE,
                    pyclipper.PFT_NONZERO,
                    pyclipper.PFT_NONZERO,
                )
                if len(finalNfp) == 0:
                    continue
                finalNfp = pyclipper.CleanPolygons(finalNfp)

                for j in range(len(finalNfp) - 1, -1, -1):
                    if len(finalNfp[j]) < 3:
                        finalNfp.pop(j)
                if len(finalNfp) == 0:
                    continue

                finalNfp = [[{
                    'x': p[0],
                    'y': p[1]
                } for p in polygon] for polygon in finalNfp]

                min_width = None
                min_area = None
                min_x = None

                for nf in finalNfp:

                    if abs(polygon_area(nf)) < 2:
                        continue

                    for p_nf in nf:
                        # 生成nfp多边形
                        all_points = list()
                        for m in range(0, len(placed)):
                            for p in placed[m]['points']:
                                all_points.append({
                                    'x':
                                    p['x'] + placements[m]['x'],
                                    'y':
                                    p['y'] + placements[m]['y'],
                                })
                        # path 坐标
                        shift_vector = {
                            'x': p_nf['x'] - path['points'][0]['x'],
                            'y': p_nf['y'] - path['points'][0]['y'],
                            'p_id': path['p_id'],
                            'rotation': path['rotation'],
                        }

                        # 找新坐标后的最小矩形
                        for m in range(0, len(path['points'])):
                            all_points.append({
                                'x':
                                path['points'][m]['x'] + shift_vector['x'],
                                'y':
                                path['points'][m]['y'] + shift_vector['y'],
                            })

                        rect_bounds = get_polygon_bounds(all_points)
                        # weigh width more, to help compress in direction of gravity
                        area = rect_bounds['width'] * 2 + rect_bounds['height']

                        if (min_area is None or area < min_area
                                or almost_equal(min_area, area)) and (
                                    min_x is None
                                    or shift_vector['x'] <= min_x):
                            min_area = area
                            min_width = rect_bounds['width']
                            position = shift_vector
                            min_x = shift_vector['x']

                if position:
                    placed.append(path)
                    placements.append(position)

            if min_width:
                fitness += min_width / bin_area

            for p in placed:
                p_id = paths.index(p)
                if p_id >= 0:
                    paths.pop(p_id)

            if placements and len(placements) > 0:
                all_placements.append(placements)

            else:
                # something went wrong
                break

        fitness += 2 * len(paths)

        return {
            'placements': all_placements,
            'fitness': fitness,
            'paths': paths,
            'area': bin_area,
        }
コード例 #12
0
    def process_nfp(self, pair):
        """
        计算所有图形两两组合的相切多边形(NFP)
        :param pair: 两个组合图形的参数
        :return:
        """
        if pair is None or len(pair) == 0:
            return None

        # 考虑有没有洞和凹面
        search_edges = self.config['exploreConcave']
        use_holes = self.config['useHoles']

        # 图形参数
        A = copy.deepcopy(pair['A'])
        A['points'] = nfp_utls.rotate_polygon(
            A['points'], pair['key']['A_rotation'])['points']
        B = copy.deepcopy(pair['B'])
        B['points'] = nfp_utls.rotate_polygon(
            B['points'], pair['key']['B_rotation'])['points']

        if pair['key']['inside']:
            # 内切或者外切
            if nfp_utls.is_rectangle(A['points'], 0.0001):
                nfp = nfp_utls.nfp_rectangle(A['points'], B['points'])
            else:
                nfp = nfp_utls.nfp_polygon(A, B, True, search_edges)

            # ensure all interior NFPs have the same winding direction
            if nfp and len(nfp) > 0:
                for i in range(0, len(nfp)):
                    if nfp_utls.polygon_area(nfp[i]) > 0:
                        nfp[i].reverse()
            else:
                pass
                # print('NFP Warning:', pair['key'])

        else:
            if search_edges:
                nfp = nfp_utls.nfp_polygon(A, B, False, search_edges)
            else:
                nfp = minkowski_difference(A, B)

            # 检查NFP多边形是否合理
            if nfp is None or len(nfp) == 0:
                pass
                # print('error in NFP 260')
                # print('NFP Error:', pair['key'])
                # print('A;', A)
                # print('B:', B)
                return None

            for i in range(0, len(nfp)):
                # if search edges is active, only the first NFP is guaranteed to pass sanity check
                if not search_edges or i == 0:
                    if abs(nfp_utls.polygon_area(nfp[i])) < abs(
                            nfp_utls.polygon_area(A['points'])):
                        pass
                        # print('error in NFP area 269')
                        # print('NFP Area Error: ', abs(nfp_utls.polygon_area(nfp[i])), pair['key'])
                        # print('NFP:', json.dumps(nfp[i]))
                        # print('A: ', A)
                        # print('B: ', B)
                        nfp.pop(i)
                        return None

            if len(nfp) == 0:
                return None
            # for outer NFPs, the first is guaranteed to be the largest.
            # Any subsequent NFPs that lie inside the first are hole
            for i in range(0, len(nfp)):
                if nfp_utls.polygon_area(nfp[i]) > 0:
                    nfp[i].reverse()

                if i > 0:
                    if nfp_utls.point_in_polygon(nfp[i][0], nfp[0]):
                        if nfp_utls.polygon_area(nfp[i]) < 0:
                            nfp[i].reverse()

            # generate nfps for children (holes of parts) if any exist
            # 有洞的暂时不管
            if use_holes and len(A) > 0:
                pass
        return {'key': pair['key'], 'value': nfp}
コード例 #13
0
    def process_nfp(self, pair):
        """
        Tính đa giác tiếp tuyến của tất cả các cặp hình
        :param pair: Tham số kết hợp của hai hình
        :return:
        """
        if pair is None or len(pair) == 0:
            return None

        # Tham số cài đặt, có lỗ hay mặt lõm hay không
        search_edges = self.config['exploreConcave']
        use_holes = self.config['useHoles']

        # Thông số đồ họa, quay các hình theo góc quay
        # A nếu là bin thì không chứa thông tin offset
        A = copy.deepcopy(pair['A'])
        A['points'] = nfp_utls.rotate_polygon(
            A['points'], pair['key']['A_rotation'])['points']

        # Là path, chứa thông tin offset
        B = copy.deepcopy(pair['B'])
        # Rotate B theo key pair đã tạo với A
        B['points'] = nfp_utls.rotate_polygon(
            B['points'], pair['key']['B_rotation'])['points']

        if pair['key']['inside']:
            # Inside or outside
            # Thường thì đây là cặp bin và path, tính NPF của part với bin hoặc giữa các path với nhau
            if nfp_utls.is_rectangle(A['points'], 0.0001):
                # Tính npf của hình với bin
                nfp = nfp_utls.nfp_rectangle(A['points'], B['points'])
            else:
                nfp = nfp_utls.nfp_polygon(A, B, True, search_edges)

            # ensure all interior NFPs have the same winding direction

            # Test_Di chuyển về sát lề
            # for nf in nfp:
            #     for p in nf:
            #         p['x'] = p['x'] - 10
            #         p['y'] = p['y'] - 10

            if nfp and len(nfp) > 0:
                for i in range(0, len(nfp)):
                    if nfp_utls.polygon_area(nfp[i]) > 0:
                        nfp[i].reverse()
            else:
                pass
                # print('NFP Warning:', pair['key'])

        else:
            if search_edges:
                nfp = nfp_utls.nfp_polygon(A, B, False, search_edges)
            else:
                nfp = minkowski_difference(A, B)

            # Kiểm tra xem đa giác NFP có hợp lý không
            if nfp is None or len(nfp) == 0:
                pass
                # print('error in NFP 260')
                # print('NFP Error:', pair['key'])
                # print('A;', A)
                # print('B:', B)
                return None

            for i in range(0, len(nfp)):
                # if search edges is active, only the first NFP is guaranteed to pass sanity check
                if not search_edges or i == 0:
                    if abs(nfp_utls.polygon_area(nfp[i])) < abs(
                            nfp_utls.polygon_area(A['points'])):
                        pass
                        # print('error in NFP area 269')
                        # print('NFP Area Error: ', abs(nfp_utls.polygon_area(nfp[i])), pair['key'])
                        # print('NFP:', json.dumps(nfp[i]))
                        # print('A: ', A)
                        # print('B: ', B)
                        nfp.pop(i)
                        return None

            if len(nfp) == 0:
                return None
            # for outer NFPs, the first is guaranteed to be the largest.
            # Any subsequent NFPs that lie inside the first are hole
            for i in range(0, len(nfp)):
                if nfp_utls.polygon_area(nfp[i]) > 0:
                    nfp[i].reverse()

                if i > 0:
                    if nfp_utls.point_in_polygon(nfp[i][0], nfp[0]):
                        if nfp_utls.polygon_area(nfp[i]) < 0:
                            nfp[i].reverse()

            # generate nfps for children (holes of parts) if any exist
            # Leave the hole in
            if use_holes and len(A) > 0:
                pass
        return {'key': pair['key'], 'value': nfp}