Skip to content

Instantly share code, notes, and snippets.

@ruchej
Last active August 18, 2022 10:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ruchej/af6b0a4b9ec5392b59f2a8442bfde621 to your computer and use it in GitHub Desktop.
Save ruchej/af6b0a4b9ec5392b59f2a8442bfde621 to your computer and use it in GitHub Desktop.
#-*- coding: utf-8 -*-
import copy
import json
import monotonic
class MyEncoder(json.JSONEncoder):
def default(self, obj):
return obj.__dict__
class CutLayer(object):
q = 0
cut_length = 0
in_planes = []
out_planes = []
count_items = 0
def __copy__(self):
copy_object = CutLayer()
return copy_object
def __deepcopy__(self, memodict={}):
copy_object = CutLayer()
copy_object.q = self.q
copy_object.cut_length = self.cut_length
copy_object.in_planes = self.in_planes
copy_object.out_planes = self.out_planes
copy_object.count_items = self.count_items
return copy_object
def __init__(self, *args, **kwargs):
super(CutLayer, self).__init__()
self.q = kwargs.get('q', 0)
self.cut_length = kwargs.get('cut_length', 0)
self.in_planes = kwargs.get('in_planes', [])
self.out_planes = kwargs.get('out_planes', [])
self.count_items = 0
for i in self.in_planes:
self.count_items += i.count
def __str__(self):
return '<%s> in_pl %dшт, out_pl %dшт' % (self.__class__.__name__, len(self.in_planes), len(self.out_planes))
def __repr__(self):
return json.dumps(self.__dict__)
class Plane(object):
id = None
width = 0
height = 0
count = 0
count_in = 0
direction = 0 # horizontal
area = 0
x = 0
y = 0
def __copy__(self):
copy_object = Plane()
return copy_object
def __deepcopy__(self, memodict={}):
copy_object = Plane()
copy_object.id = self.id
copy_object.width = self.width
copy_object.height = self.height
copy_object.count = self.count
copy_object.count_in = self.count_in
copy_object.direction = self.direction
copy_object.area = self.area
copy_object.x = self.x
copy_object.y = self.y
return copy_object
def __init__(self, *args, **kwargs):
super(Plane, self).__init__()
self.id = kwargs.get('id', None)
self.width = kwargs.get('width', 0)
self.height = kwargs.get('height', 0)
self.count = kwargs.get('count', 0)
self.count_in = kwargs.get('count_in', 0)
self.direction = kwargs.get('direction', 0)
self.area = self.width * self.height * self.count
self.x = self.x
self.y = self.y
def __str__(self):
return '<%s> %d x %d - %dшт %s' % (self.__class__.__name__, self.width, self.height, self.count,
'вер' if self.direction else
'гор')
def __repr__(self):
return json.dumps(self.__dict__)
def check_inside(width, height, planes):
# check bounding planes
out_planes = []
in_planes = []
for plane in planes:
pl = copy.deepcopy(plane)
if (pl.width > width) or (pl.height > height):
# not inside
out_planes.append(pl)
else:
in_planes.append(pl)
return in_planes, out_planes
def get_min_rest(width, height, cut_width, plane):
if plane.count > 1:
# horizontal
max_width = plane.count * plane.width + (plane.count - 1) * cut_width
if max_width > width:
cnw = (width + cut_width) // (plane.width + cut_width)
width_rest = width - (cnw * plane.width + (cnw - 1) * cut_width)
else:
width_rest = width - max_width
cnw = plane.count
# vertical
max_height = plane.count * plane.height + (plane.count - 1) * cut_width
if max_height > height:
cnh = (height + cut_width) // (plane.height + cut_width)
height_rest = height - (cnh * plane.height + (cnh - 1) * cut_width)
else:
height_rest = height - max_height
cnh = plane.count
if (height_rest < width_rest):
plane.direction = 1 # vertical direction
plane.count_in = cnh
return height_rest
else:
plane.direction = 0 # horizontal direction
plane.count_in = cnw
return width_rest
else:
plane.count_in = plane.count
plane.direction = 0
return min((width - plane.width), (height - plane.height))
def order_planes(width, height, cut_width, planes):
planes.sort(key=lambda plane: get_min_rest(width, height, cut_width, plane))
def make_cut_layer(x, y, width, height, cut_width, planes, layer_type="default"):
in_planes, out_planes = check_inside(width, height, planes)
res = CutLayer()
current_plane = None
q = 0
if len(in_planes) > 0:
order_planes(width, height, cut_width, in_planes)
cut_lenght = 0
current_plane = in_planes.pop(0)
tmp_x = x
tmp_y = y #height - y if layer_type == "tabletop" else y
if (current_plane.count > 1):
for i in range(0, current_plane.count_in, 1):
tmp_plane = Plane(**{'id': current_plane.id,
'width': current_plane.width,
'height': current_plane.height,
'count': 1})
tmp_plane.x = tmp_x
tmp_plane.y = tmp_y - current_plane.height if layer_type == "tabletop" else tmp_y
current_plane.count -= 1
if current_plane.direction == 0: # horizontal
tmp_x = tmp_x + current_plane.width + cut_width
else: # vertical
if layer_type == "tabletop":
tmp_y = tmp_y - current_plane.height - cut_width
else:
tmp_y = tmp_y + current_plane.height + cut_width
res.in_planes.append(tmp_plane)
# вернуть остаток
if current_plane.count > 0:
in_planes.append(copy.deepcopy(current_plane))
# set current plane size as summ of planes
if current_plane.direction == 0: # horizontal
current_plane.width = tmp_x - x - cut_width
else:
if layer_type == "tabletop":
current_plane.height = y - tmp_y + cut_width
else:
current_plane.height = tmp_y - y - cut_width
else:
# SET LAYER POSITION TO PLANE
current_plane.x = tmp_x
current_plane.y = tmp_y - current_plane.height if layer_type == "tabletop" else tmp_y
res.in_planes.append(copy.deepcopy(current_plane))
# make sub layers
# HORIZONTAL CUT
# A Layer
x_A = x
y_A = y - current_plane.height - cut_width if layer_type == "tabletop" else y + current_plane.height + cut_width
width_A = width
height_A = height - current_plane.height - cut_width
# B Layer
x_B = x + current_plane.width + cut_width
y_B = y
width_B = width - current_plane.width - cut_width
height_B = current_plane.height
layer_A_H = make_cut_layer(x_A, y_A, width_A, height_A, cut_width, copy.deepcopy(in_planes), layer_type)
layer_B_H = make_cut_layer(x_B, y_B, width_B, height_B, cut_width, layer_A_H.out_planes, layer_type)
# VERTICAL CUT
# A Layer
x_C = x
y_C = y - current_plane.height - cut_width if layer_type == "tabletop" else y + current_plane.height + cut_width
width_C = current_plane.width
height_C = height - current_plane.height - cut_width
# B Layer
x_D = x + current_plane.width + cut_width
y_D = y
width_D = width - current_plane.width - cut_width
height_D = height
layer_C_V = make_cut_layer(x_C, y_C, width_C, height_C, cut_width, copy.deepcopy(in_planes), layer_type)
layer_D_V = make_cut_layer(x_D, y_D, width_D, height_D, cut_width, layer_C_V.out_planes, layer_type)
res.out_planes.extend(out_planes)
# "COMPARE HORIZONTAL CUT %s" % (layer_A_H.q + layer_B_H.q)
# "COMPARE VERTICAL CUT %s" % (layer_C_V.q + layer_D_V.q)
if (min(layer_A_H.q, layer_B_H.q)) < (min(layer_C_V.q, layer_D_V.q)):
# "horizontal cut better"
res.q = layer_A_H.q + layer_B_H.q
res.in_planes.extend(layer_A_H.in_planes)
res.in_planes.extend(layer_B_H.in_planes)
res.out_planes.extend(layer_B_H.out_planes)
else:
# "vertical result better"
res.q = layer_C_V.q + layer_D_V.q
res.in_planes.extend(layer_C_V.in_planes)
res.in_planes.extend(layer_D_V.in_planes)
res.out_planes.extend(layer_D_V.out_planes)
else:
# End cutting
# check - width - height
if width < 0:
width = width + cut_width
if height < 0:
height = height + cut_width
res.q = width * height
res.out_planes = out_planes
return res
def cut_lay(x, y, width, height, cut_width, in_planes, layer_type="default"):
iterator = 0
cut_layers = []
while in_planes and iterator < 100:
y_pos = height if layer_type == "tabletop" else 0
cut_layer = make_cut_layer(0, y_pos, width, height, cut_width, in_planes, layer_type)
cut_layers.append(cut_layer)
in_planes = cut_layer.out_planes
iterator += 1
return cut_layers
def cutting(x, y, width, height, cut_width, in_planes, layer_type="default"):
cut_layers = []
#order_planes(width, height, cut_width, in_planes)
new_planes = []
new_planes_item = []
area_layer = height * width
area_item = 0
while in_planes:
area_item += in_planes[0].area
if area_item <= area_layer:
new_planes_item.append(in_planes.pop(0))
else:
new_planes.append(new_planes_item)
area_item = 0
new_planes_item = []
if not in_planes and new_planes_item:
new_planes.append(new_planes_item)
# with open('data1.json', 'w') as f:
# json.dump(new_planes[1], f, cls=MyEncoder, ensure_ascii=False, indent=True)
print('Листов %d' % len(new_planes))
for i, planes in enumerate(new_planes):
print('Кроим %d лист с %d деталей' % (i, get_cnt_planes(planes)))
start = monotonic.time.time()
cut_lay_item = cut_lay(x, y, width, height, cut_width, planes, layer_type)
cnt_t = monotonic.time.time() - start
cut_layers.extend(cut_lay_item)
print('\t\t\t %f сек' % cnt_t)
if i == 2:
break
return cut_layers
def get_cnt_planes(planes):
cnt = 0
for i in planes:
cnt += i.count
return cnt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment