Jakie techniki pakowania mogą zwiększyć efektywność wykorzystania miejsca na arkuszu?

Jakie techniki pakowania mogą zwiększyć efektywność wykorzystania miejsca na arkuszu?
VisiateAI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 36
0

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ł).

Kopiuj
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}")

M07198.dxf
Obejma DN100 05.08.2024.dxf

DarekRepos
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 57
0

Taka optymalizacja, gdzie chcesz wycinać kształty z arkusza materiału w taki sposób, aby zminimalizować odpady i uczynić proces ekonomicznie efektywnym to trzeba szukać algorytmów pod hasłami :
"2D cutting stock problem" i "2D nesting problem" i "1D Linear Cutting Optimization"

na githubie mozna wpisac "cutting-stock " tam używaja przeróżnych rozwiązań. Z tego co wyszukałem te problemy można rozwiązać na różne sposoby np znalazłem taki co używa algorytmu genetycznego cos jak tu:

2d-cutting-stock-problem

Możesz stworzyć własny wyspecjalizowany program z algorytmem zachłannym (Greedy Algorithm) np. co najpierw umieści większe kształty, użyje kombinacji algorytmów nestingu i pozwala na obracanie oraz odbijanie kształtów.

Ale są chyba nawet do tego nawet narzedzia np:
AutoNest: Oprogramowanie specjalizujące się w nestingu nieregularnych kształtów.
CutLogic: Oprogramowanie do optymalizacji cięcia dla prostokątów.
NestFab: Oprogramowanie do automatycznego nestingu nieregularnych kształtów.
CLOP (Cutting Layout Optimization Program): Open source program dla podstawowych problemów przycinania.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.