Witam mam kod do pakowania na arkuszu kawałków. na razie zrobiłem tylko z repack i width x height ale w tym sposobie jest słaba oszczędność miejsca. ma ktoś pomysł jak zrobić inteligentne pakowanie które będzie potrafiło wpasować z sobą dwa elementy? Nie proszę o rozwiązanie lecz o sugestie (pomysł).
import ezdxf
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Polygon, Arc, Rectangle
from rectpack import newPacker
class SheetMetal:
def __init__(self, width, height, thickness, ilosc_sztuk):
self.width = int(width)
self.height = int(height)
self.thickness = thickness
self.shapes = []
self.ilosc_sztuk = ilosc_sztuk
def read_dwg_file(self, dwg_filename):
dwg = ezdxf.readfile(dwg_filename)
msp = dwg.modelspace()
shapes = []
for entity in msp:
if entity.dxftype() == 'LWPOLYLINE':
points = entity.get_points(format='xyb')
shapes.append(('LWPOLYLINE', points, entity.closed))
elif entity.dxftype() == 'CIRCLE':
center = entity.dxf.center
radius = entity.dxf.radius
shapes.append(('CIRCLE', (center.x, center.y, radius)))
elif entity.dxftype() == 'ARC':
center = entity.dxf.center
radius = entity.dxf.radius
start_angle = entity.dxf.start_angle
end_angle = entity.dxf.end_angle
shapes.append(('ARC', (center.x, center.y, radius, start_angle, end_angle)))
elif entity.dxftype() == 'LINE':
start_point = (entity.dxf.start.x, entity.dxf.start.y)
end_point = (entity.dxf.end.x, entity.dxf.end.y)
shapes.append(('LINE', start_point, end_point))
elif entity.dxftype() == 'POINT':
location = (entity.dxf.location.x, entity.dxf.location.y)
shapes.append(('POINT', location))
elif entity.dxftype() == 'INSERT':
insertion_point = (entity.dxf.insert.x, entity.dxf.insert.y)
shapes.append(('INSERT', insertion_point))
self.shapes = shapes
def extract_max_points(self):
print("Extracting max points...")
max_x = max_y = float('-inf')
min_x = min_y = float('inf')
for shape in self.shapes:
if shape[0] == 'LWPOLYLINE':
points = shape[1]
max_x = max(max_x, max(point[0] for point in points))
max_y = max(max_y, max(point[1] for point in points))
min_x = min(min_x, min(point[0] for point in points))
min_y = min(min_y, min(point[1] for point in points))
elif shape[0] == 'CIRCLE':
center, radius = shape[1][0:2], shape[1][2]
max_x = max(max_x, center[0] + radius)
max_y = max(max_y, center[1] + radius)
min_x = min(min_x, center[0] - radius)
min_y = min(min_y, center[1] - radius)
elif shape[0] == 'ARC':
center, radius = shape[1][0:2], shape[1][2]
max_x = max(max_x, center[0] + radius)
max_y = max(max_y, center[1] + radius)
min_x = min(min_x, center[0] - radius)
min_y = min(min_y, center[1] - radius)
elif shape[0] == 'LINE':
start_point, end_point = shape[1], shape[2]
max_x = max(max_x, start_point[0], end_point[0])
max_y = max(max_y, start_point[1], end_point[1])
min_x = min(min_x, start_point[0], end_point[0])
min_y = min(min_y, start_point[1], end_point[1])
elif shape[0] == 'POINT':
location = shape[1]
max_x = max(max_x, location[0])
max_y = max(max_y, location[1])
min_x = min(min_x, location[0])
min_y = min(min_y, location[1])
elif shape[0] == 'INSERT':
insertion_point = shape[1]
max_x = max(max_x, insertion_point[0])
max_y = max(max_y, insertion_point[1])
min_x = min(min_x, insertion_point[0])
min_y = min(min_y, insertion_point[1])
width = max_x - min_x
height = max_y - min_y
return max_x, max_y, min_x, min_y, width, height
def calculate_surface_area(self):
surface_area = 0
for shape in self.shapes:
if shape[0] == 'LWPOLYLINE':
points = shape[1]
area = 0.5 * abs(sum(points[i][0] * points[i+1][1] - points[i+1][0] * points[i][1] for i in range(-1, len(points)-1)))
surface_area += area
elif shape[0] == 'CIRCLE':
radius = shape[1][2]
area = np.pi * radius * radius
surface_area += area
elif shape[0] == 'ARC':
_, _, radius, start_angle, end_angle = shape[1]
angle = (end_angle - start_angle) % 360
area = (angle / 360) * np.pi * radius * radius
surface_area += area
surface_area *= self.thickness
return surface_area
def calculate_perimeter(self):
perimeter = 0
for shape in self.shapes:
if shape[0] == 'LWPOLYLINE':
points = shape[1]
for i in range(len(points) - 1):
start = points[i][:2]
end = points[i + 1][:2]
perimeter += np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
if shape[2]:
perimeter += np.sqrt((points[-1][0] - points[0][0]) ** 2 + (points[-1][1] - points[0][1]) ** 2)
elif shape[0] == 'CIRCLE':
radius = shape[1][2]
perimeter += 2 * np.pi * radius
elif shape[0] == 'ARC':
_, _, radius, start_angle, end_angle = shape[1]
angle = (end_angle - start_angle) % 360
perimeter += (angle / 360) * 2 * np.pi * radius
elif shape[0] == 'LINE':
start_point = shape[1]
end_point = shape[2]
perimeter += np.sqrt((end_point[0] - start_point[0]) ** 2 + (end_point[1] - start_point[1]) ** 2)
return perimeter
def scale_and_translate_shapes(self, scale_factor, dx, dy):
translated_shapes = []
for shape in self.shapes:
if shape[0] == 'LWPOLYLINE':
points = [(point[0] + dx, point[1] + dy, point[2]) for point in shape[1]]
translated_shapes.append(('LWPOLYLINE', points, shape[2]))
elif shape[0] == 'CIRCLE':
center = (shape[1][0] + dx, shape[1][1] + dy)
radius = shape[1][2]
translated_shapes.append(('CIRCLE', (center[0], center[1], radius)))
elif shape[0] == 'ARC':
center = (shape[1][0] + dx, shape[1][1] + dy)
radius = shape[1][2]
start_angle = shape[1][3]
end_angle = shape[1][4]
translated_shapes.append(('ARC', (center[0], center[1], radius, start_angle, end_angle)))
elif shape[0] == 'LINE':
start_point = (shape[1][0] + dx, shape[1][1] + dy)
end_point = (shape[2][0] + dx, shape[2][1] + dy)
translated_shapes.append(('LINE', start_point, end_point))
elif shape[0] == 'POINT':
location = (shape[1][0] + dx, shape[1][1] + dy)
translated_shapes.append(('POINT', location))
elif shape[0] == 'INSERT':
insertion_point = (shape[1][0] + dx, shape[1][1] + dy)
translated_shapes.append(('INSERT', insertion_point))
self.shapes = translated_shapes
def pack_shapes(self):
packer = newPacker(rotation=True)
max_x, max_y, min_x, min_y = self.extract_max_points()[:4]
width = max_x - min_x
height = max_y - min_y
current_x_offset = 0
current_y_offset = 0
packed_shapes = []
for i in range(self.ilosc_sztuk):
if current_x_offset + width > self.width and current_y_offset + height > self.height:
break
if current_x_offset + width > self.width:
current_x_offset = 0
current_y_offset += height
if current_y_offset + height > self.height:
break
packer.add_bin(self.width, self.height)
packer.add_rect(width, height)
packer.pack()
packed = False
for abin in packer:
for rect in abin:
if rect.width <= self.width and rect.height <= self.height:
x, y, w, h = rect.x, rect.y, rect.width, rect.height
packed_shapes.append((x + current_x_offset, y + current_y_offset, w, h))
packed = True
break
if packed:
break
if packed:
current_x_offset += width
else:
break
return packed_shapes
def draw_shapes(self, ax):
packed_shapes = self.pack_shapes()
for x, y, w, h in packed_shapes:
for shape in self.shapes:
if shape[0] == 'LWPOLYLINE':
points = [(point[0] + x, point[1] + y) for point in shape[1]]
is_closed = shape[2]
self.draw_lwpolyline(ax, points, is_closed)
elif shape[0] == 'CIRCLE':
center = (shape[1][0] + x, shape[1][1] + y)
radius = shape[1][2]
self.draw_circle(ax, center, radius)
elif shape[0] == 'ARC':
center = (shape[1][0] + x, shape[1][1] + y)
radius = shape[1][2]
start_angle = shape[1][3]
end_angle = shape[1][4]
self.draw_arc(ax, center, radius, start_angle, end_angle)
elif shape[0] == 'LINE':
start_point = (shape[1][0] + x, shape[1][1] + y)
end_point = (shape[2][0] + x, shape[2][1] + y)
self.draw_line(ax, start_point, end_point)
elif shape[0] == 'POINT':
location = (shape[1][0] + x, shape[1][1] + y)
self.draw_point(ax, location)
elif shape[0] == 'INSERT':
insertion_point = (shape[1][0] + x, shape[1][1] + y)
self.draw_insert(ax, insertion_point)
def draw_circle(self, ax, center, radius):
circle = Circle(center, radius, fill=False, edgecolor='black')
ax.add_patch(circle)
def draw_arc(self, ax, center, radius, start_angle, end_angle):
arc = Arc(center, radius * 2, radius * 2, angle=0, theta1=start_angle, theta2=end_angle, edgecolor='black')
ax.add_patch(arc)
def draw_line(self, ax, start, end):
ax.plot([start[0], end[0]], [start[1], end[1]], 'k-')
mid_x, mid_y = (start[0] + end[0]) / 2, (start[1] + end[1]) / 2
def draw_point(self, ax, location):
ax.plot(location[0], location[1], 'ko', markersize=10)
def draw_insert(self, ax, insertion_point):
ax.plot(insertion_point[0], insertion_point[1], 'ro', markersize=10)
def draw_lwpolyline(self, ax, points, is_closed):
vertices = np.array(points)[:, :2]
if is_closed:
polygon = Polygon(vertices, closed=True, fill=False, edgecolor='black')
ax.add_patch(polygon)
else:
ax.plot(vertices[:, 0], vertices[:, 1], 'k-')
for i in range(len(points) - 1):
start, end = points[i][:2], points[i + 1][:2]
bulge = points[i][2]
if bulge != 0:
self.add_arc(ax, start, end, bulge)
def add_arc(self, ax, start, end, bulge):
dx, dy = end[0] - start[0], end[1] - start[1]
dist = np.sqrt(dx**2 + dy**2)
radius = dist * (1 + bulge**2) / (2 * bulge)
mid = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2]
sagitta = radius - dist / 2 * abs(bulge)
angle = np.arctan2(dy, dx)
if bulge > 0:
center = [mid[0] + sagitta * np.sin(angle), mid[1] - sagitta * np.cos(angle)]
else:
center = [mid[0] - sagitta * np.sin(angle), mid[1] + sagitta * np.cos(angle)]
start_angle = np.degrees(np.arctan2(start[1] - center[1], start[0] - center[0]))
end_angle = np.degrees(np.arctan2(end[1] - center[1], end[0] - center[0]))
if bulge < 0:
if start_angle < end_angle:
start_angle += 360
else:
if end_angle < start_angle:
end_angle += 360
arc = Arc(center, 2*radius, 2*radius, angle=0, theta1=start_angle, theta2=end_angle, color='black')
ax.add_patch(arc)
def draw_sheet(self, dwg_filename, quantity, file_name=None, fill_background=False):
self.read_dwg_file(dwg_filename)
max_x, max_y, min_x, min_y, width, height = self.extract_max_points()
dx = -min_x
dy = -min_y
self.scale_and_translate_shapes(1, dx, dy) # Translate without scaling
surface_area = self.calculate_surface_area()
perimeter = self.calculate_perimeter()
fig, ax = plt.subplots(1, figsize=(12, 8))
ax.set_xlim(0, self.width)
ax.set_ylim(0, self.height)
if fill_background:
self.paint_background(ax)
self.draw_shapes(ax)
plt.gca().set_aspect('equal', adjustable='box')
plt.gca().invert_yaxis()
if file_name:
plt.savefig(file_name)
plt.show()
plt.close(fig)
return max_x, max_y, surface_area, perimeter, width, height
def paint_background(self, ax):
rect = Rectangle((0, 0), self.width, self.height, linewidth=1, edgecolor='none', facecolor='w', alpha=0.3)
ax.add_patch(rect)
ilosc_sztuk = 20
sheet_metal = SheetMetal(2000, 1400, 10, ilosc_sztuk)
dwg_filename = "M07198.dxf"
max_x, max_y, surface_area, perimeter, width, height = sheet_metal.draw_sheet(dwg_filename, ilosc_sztuk, "sheet_metal_cutting.png", fill_background=True)
print(f"Maksymalny punkt na osi X: {max_x}")
print(f"Maksymalny punkt na osi Y: {max_y}")
print(f"Pole powierzchni bocznej: {surface_area}")