import os import json import requests import shutil import time import random import matplotlib.pyplot as plt import open3d as o3d import numpy as np from plyfile import PlyData, PlyElement from general import mesh_tranform_to_pcd from general import need_upload_result from general import read_mesh from general import extend_dist_min_collision from compute_print_net import arrange_models_on_platform from compute_print_net import compute_bbox_all def make_bbox_for_print(base_original_obj_dir,dict_bad,dict_origin,is_downsample): """获取需要的盒子大小""" start_time = time.time() obj_id_list = [aa.split(".o")[0] for aa in os.listdir(base_original_obj_dir) if aa.endswith(".obj")] obj_id_list = obj_id_list dict_mesh_obj = {} index = 0 for pid_t_y in obj_id_list: start_time1 = time.time() obj_name = pid_t_y+".obj" obj_path = os.path.join(base_original_obj_dir,obj_name) mesh_obj = read_mesh(obj_path) if mesh_obj is None: dict_bad[obj_name]=obj_name # 记录错误文件 error_log = os.path.join(base_original_obj_dir, "error_files.txt") with open(error_log, 'a') as f: f.write(f"{obj_path}\n") print(f"Skipping invalid file: {obj_path}") continue dict_origin[obj_name] = mesh_obj dict_mesh_obj[obj_name] = mesh_obj print(f"make_bbox_for_print {index} {obj_name} time={time.time()-start_time1}") index = index + 1 print(f"make_bbox_for_print total_time={time.time()-start_time}") # dict_total_matrix,all_models = compute_bbox_all_ext(base_original_obj_dir, is_downsample) dict_total_matrix,all_models = compute_bbox_all(dict_mesh_obj, is_downsample) return get_dict_pcd(dict_mesh_obj,dict_total_matrix,all_models) def get_dict_pcd(dict_mesh_obj,dict_total_matrix,all_models): dict_pcd_fix = {} dict_ply_name = {} for model in all_models: ply_name = model['name'] dict_ply_name[f"{ply_name.split("=")[0]}.obj"] = ply_name dict_pcd_fix = get_pcd_by_matrix(dict_mesh_obj,dict_total_matrix,dict_ply_name) return dict_total_matrix,all_models,dict_pcd_fix def get_pcd_by_matrix(dict_mesh_obj,dict_total_matrix,dict_ply_name): dict_pcd_fix= {} for key, value in dict_mesh_obj.items(): obj_name = key mesh_obj = value pcd_fix = mesh_tranform_to_pcd(mesh_obj, dict_total_matrix[obj_name]) dict_pcd_fix[dict_ply_name[obj_name]] = pcd_fix dict_mesh_obj.clear() del dict_mesh_obj return dict_pcd_fix def ply_print_layout_platform(dict_pcd_fix,dict_pcd_fix2,machine_size,dict_total_matrix,all_models): """根据排版结果移动点云到指定位置""" # placed_models,unplaced_models = get_models_box_size(dict_fix,machine_size) # 1. 获取模型bbox尺寸 # all_models = get_models_bbox_net(dict_pcd_fix) print("all_models", all_models) # 2. 模型排版布局 print("开始计算排序...") placed_models, unplaced_models = arrange_models_on_platform(all_models, machine_size) if len(placed_models) ==0: print("放进打印盒的数量为0") return # 3. 根据排版结果移动点云和模型(原有逻辑不变) for model in placed_models: print(f" - {model['name']} at {model['position']} with dimensions {model['dimensions']}") ply_file_name = model['name'] move_position = model['position'] # print("要读取的点云数据路径",ply_origin_path) pcd = dict_pcd_fix[ply_file_name] # print("dict_fix read",ply_file_name,move_position) points = np.asarray(pcd.points) min_bound = np.min(points, axis=0) # 获取点云的最小边界 max_bound = np.max(points, axis=0) min_bound[1] = max(min_bound[1], 0) bbox_center = (min_bound + max_bound) / 2 # 计算包围盒的中心点 bbox_extent = (max_bound - min_bound) new_bbox = o3d.geometry.OrientedBoundingBox(center=bbox_center, R=np.eye(3), # 旋转矩阵,默认没有旋转 extent=bbox_extent) x = move_position[0] y = move_position[1] z = move_position[2] # move_position = np.array([x,y,z])/100 move_position = np.array([x, y, z]) # translation_vector = -move_position translation_vector = move_position pcd.translate(translation_vector) new_bbox.translate(translation_vector) obj_name = ply_file_name.split("=")[0]+".obj" T_trans1 = np.eye(4) T_trans1[:3, 3] = translation_vector dict_total_matrix[obj_name]= T_trans1 @ dict_total_matrix[obj_name] new_bbox_lines = o3d.geometry.LineSet.create_from_oriented_bounding_box(new_bbox) new_bbox_lines.paint_uniform_color([1, 0, 0]) # 红色 dict_pcd_fix2[ply_file_name] = pcd return placed_models def compute_distance(pcd1, pcd2): """ 正确计算两个点云之间距离的函数。 返回两个点云之间最近距离的平均值、最小值以及全部距离数组。 """ # 使用Open3D内置的高效方法计算距离 # 计算pcd1中每个点到pcd2中最近点的距离 distances = pcd1.compute_point_cloud_distance(pcd2) distances = np.asarray(distances) # 计算有意义的统计量 min_dist = np.min(distances) # 所有点中的最小距离 mean_dist = np.mean(distances) # 距离的平均值 # return min_dist, mean_dist, distances return min_dist import numpy as np def compute_aabb(pcd): """计算点云的AABB包围盒""" points = np.asarray(pcd.points) return { 'min': np.min(points, axis=0), 'max': np.max(points, axis=0) } def aabb_intersect(a, b, collision_threshold): """判断两个AABB包围盒是否相交[2,8](@ref)""" return (a['max'][0] > b['min'][0] - collision_threshold and a['min'][0] < b['max'][0] + collision_threshold) and \ (a['max'][1] > b['min'][1] - collision_threshold and a['min'][1] < b['max'][1] + collision_threshold) and \ (a['max'][2] > b['min'][2] - collision_threshold and a['min'][2] < b['max'][2] + collision_threshold) def check_collision_all(pcd_moving, static_pcds, collision_threshold): # 预计算移动点云AABB moving_aabb = compute_aabb(pcd_moving) for static_pcd in static_pcds: if static_pcd == pcd_moving: continue # 第一阶段:AABB快速排除[1,6](@ref) static_aabb = compute_aabb(static_pcd) # print("len(static_pcd.points)=",len(static_pcd.points),"len(moving_aabb.points)=",len(pcd_moving.points)) if not aabb_intersect(moving_aabb, static_aabb, collision_threshold): continue # 包围盒无交集,直接跳过 if not aabb_intersect(moving_aabb, static_aabb, collision_threshold): return False # 第二阶段:精确点距离计算 min_distance = compute_distance(pcd_moving, static_pcd) # print("check_collision_all",min_distance) if min_distance < collision_threshold: return True return False def compute_centroid(pcd): # 获取点云的所有点 points = np.asarray(pcd.points) # 计算质心(只考虑 X 和 Y 坐标) centroid = np.mean(points[:, :2], axis=0) # 只考虑前两个维度(X 和 Y) return centroid def compute_distance_to_origin(centroid): # 计算质心距离原点的距离(只考虑 X 和 Y 坐标) return np.linalg.norm(centroid) # 计算 X 和 Y 的欧几里得距离 def compute_closest_distance_to_origin(pcd): # 获取点云的所有点坐标 points = np.asarray(pcd.points) # 计算每个点到原点的距离 distances = np.linalg.norm(points, axis=1) # 返回最小距离 return np.min(distances) def sort_ply_files_by_closest_distance(folder_path): ply_files = [f for f in os.listdir(folder_path) if f.endswith('.ply')] distances = [] for ply_file in ply_files: # 读取点云数据 pcd = o3d.io.read_point_cloud(os.path.join(folder_path, ply_file)) # 计算离原点最近的点的距离 closest_distance = compute_closest_distance_to_origin(pcd) distances.append((ply_file, closest_distance)) # 按照最近点的距离排序(由近到远) distances.sort(key=lambda x: x[1]) # 返回排序后的文件列表 sorted_files = [item[0] for item in distances] print("Sorted files:", sorted_files) return sorted_files def compact_mode_for_min_dis_json(placed_models,dict_unplaced,dict_bounds_fix,machine_size,dict_total_matrix): y_step=1 x_step=1 delta = 10 edge_x_min=0 + 4 edge_y_min=0 + 4 edge_x_max=machine_size[0] edge_y_max=machine_size[1] collision_threshold=2 move_last = True pcd_all = [] pcd_processed = [] pcd_processed_x_top = [] pcd_processed_no_x_top = [] # name_list = [] dict_name = {} # model_list = [] dict_model = {} last_pcd_list = [] # last_name_list = [] dic_last_name = {} last_pcd_processed = [] max_x = machine_size[0] min_x = 0 max_delta_x = 0 x_top_delta = 1 border_delta = 4 pcd_first= [] pcd_second= [] index = 0 for model in placed_models: pcd = dict_bounds_fix[model['name']] pcd_all.append(pcd) if (get_axis_aligned_bbox(pcd)['y_min']>edge_y_max*0.3 or True): pcd_first.append(pcd) else: pcd_second.append(pcd) dict_name[pcd] = model['name'] dict_model[pcd] = model dx = model['dimensions'][0] x = model['position'][0] if (x>=edge_x_max-x_top_delta) : pcd_processed_x_top.append(pcd) print("pcd_processed_x_top", model['name']) if dx > max_x: max_x = dx if dx < min_x: min_x = dx max_delta_x = max_x - min_x index += 1 # print("compact_mode_for_min_dis1_json", model, max_delta_x) draw_down = False if max_delta_x < 10: draw_down = False for idx, pcd in enumerate(pcd_first): if dict_model[pcd]['first_line']: pcd_processed.append(pcd) last_pcd_processed.append(pcd) continue x = dict_model[pcd]['position'][0] y = dict_model[pcd]['position'][1] dx = dict_model[pcd]['dimensions'][0] print("compact_mode", dict_name[pcd], dx, x) ply_file_name = dict_name[pcd] obj_name = ply_file_name.split("=")[0]+".obj" T_trans1 = np.eye(4) dist_x = 50 dist_y = 20 is_x_top = False if x - 10 < edge_x_min: dist_x = x - edge_x_min if y - 10 < edge_y_min: dist_y = y - edge_y_min if (x 80: y_init_big = 10 x_init_big = y_init_big - 1 if check_collision_all(pcd, pcd_processed_curr, extend_dist_min_collision): while True: step = 25 pcd.translate([0, -step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -step, 0] T_trans1 = T_transTemp @ T_trans1 # pcd.translate([-step, 0, 0]) # T_transTemp[:3, 3] = [-step, 0, 0] # T_trans1 = T_transTemp @ T_trans1 if not check_collision_all(pcd, pcd_processed_curr, collision_threshold_big): break """ while True: bbox = get_axis_aligned_bbox(pcd) if bbox['y_max'] >= edge_y_max - collision_threshold: pcd.translate([-x_step_big, -y_step_big, 0]) print("compact_mode y_max", idx, bbox['y_max'], edge_y_max - collision_threshold_big) break if bbox['x_max'] >= edge_x_max - collision_threshold: pcd.translate([-x_step_big, -y_step_big, 0]) print("compact_mode x_max", idx, bbox['x_max'], edge_x_max - collision_threshold_big) break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_big): pcd.translate([-x_step_big, -y_step_big, 0]) break pcd.translate([x_step_big, y_step_big, 0]) #""" #""" while True: bbox = get_axis_aligned_bbox(pcd) # print("x_max",bbox['x_max'],bbox['x_min'],bbox['y_max'],bbox['y_min']) if bbox['y_min'] <= edge_y_min + collision_threshold_big and False: pcd.translate([0, y_step_big, 0]) break if bbox['y_max'] >= edge_y_max - collision_threshold_big: pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_big+y_init_big): #5 pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([0, y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 while True: bbox = get_axis_aligned_bbox(pcd) if bbox['x_min'] <= edge_x_min + collision_threshold_big and False: pcd.translate([x_step_big, 0, 0]) break if bbox['x_max'] >= edge_x_max - collision_threshold_big: pcd.translate([-x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_big+x_init_big): pcd.translate([-x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 #""" #""" collision_threshold_init = collision_threshold+6 while True: bbox = get_axis_aligned_bbox(pcd) if bbox['y_min'] <= edge_y_min + collision_threshold_init and False: pcd.translate([0, y_step, 0]) break # if bbox['y_max'] >= edge_y_max - collision_threshold_init: if bbox['y_max'] >= edge_y_max - border_delta: pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_init+1): #5 pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([0, y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step, 0] T_trans1 = T_transTemp @ T_trans1 #""" #""" while True: bbox = get_axis_aligned_bbox(pcd) if bbox['x_min'] <= edge_x_min + collision_threshold_init and False: pcd.translate([x_step, 0, 0]) break # if bbox['x_max'] >= edge_x_max - collision_threshold_init: if bbox['x_max'] >= edge_x_max - border_delta: # print("1pcd.translate([-x_step, 0, 0])",name_list[idx]) pcd.translate([-x_step, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step, 0, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_init): # print("2pcd.translate([-x_step, 0, 0])",name_list[idx]) pcd.translate([-x_step, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step, 0, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([x_step, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [x_step, 0, 0] T_trans1 = T_transTemp @ T_trans1 #""" #""" collision_threshold_init = collision_threshold+2 while True: bbox = get_axis_aligned_bbox(pcd) if bbox['y_min'] <= edge_y_min + collision_threshold_init and False: pcd.translate([0, y_step, 0]) break if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_init+1): #5 pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([0, y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step, 0] T_trans1 = T_transTemp @ T_trans1 while True: bbox = get_axis_aligned_bbox(pcd) if bbox['x_min'] <= edge_x_min + collision_threshold_init and False: pcd.translate([x_step, 0, 0]) break if bbox['x_max'] >= edge_x_max - collision_threshold_init: pcd.translate([-x_step, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step, 0, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_init): pcd.translate([-x_step, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step, 0, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([x_step, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [x_step, 0, 0] T_trans1 = T_transTemp @ T_trans1 # place again collision_threshold_init = collision_threshold+1 while True: bbox = get_axis_aligned_bbox(pcd) if bbox['y_min'] <= edge_y_min + collision_threshold_init and False: pcd.translate([0, y_step, 0]) break if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, pcd_processed_curr,collision_threshold_init): #5 pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([0, y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step, 0] T_trans1 = T_transTemp @ T_trans1 #""" pcd_processed.append(pcd) pcd_processed_x_top.append(pcd) if not is_x_top: pcd_processed_no_x_top.append(pcd) cross_border = False bbox = get_axis_aligned_bbox(pcd) if bbox['x_min'] <= edge_x_min + 1 or bbox['y_min'] <= edge_y_min + 1: cross_border = True print("coross_border",ply_file_name) if cross_border: pcd_second.append(pcd) dic_last_name[pcd] = ply_file_name else: last_pcd_processed.append(pcd) dict_total_matrix[obj_name]= T_trans1 @ dict_total_matrix[obj_name] volumes = [] # for idx, pcd in enumerate(last_pcd_list): for idx, pcd in enumerate(pcd_second): bbox = get_axis_aligned_bbox(pcd) x_length = bbox['x_max'] - bbox['x_min'] y_length = bbox['y_max'] - bbox['y_min'] z_length = bbox['z_max'] - bbox['z_min'] volume = x_length * y_length * z_length volumes.append(volume) # print("last_pcd_list", len(last_pcd_list), len(last_pcd_list), len(last_pcd_processed), len(pcd_all)) # sorted_indices = np.argsort(volumes)[::-1] # last_pcd_list2 = [last_pcd_list[i] for i in sorted_indices] # print("last_pcd_list2", len(last_pcd_list2)) print(f"pcd_second : len(pcd_first)={len(pcd_first)}, len(pcd_second)={len(pcd_second)}, len(last_pcd_processed)={len(last_pcd_processed)}, len(pcd_all)={len(pcd_all)}") sorted_indices = np.argsort(volumes)[::-1] pcd_second2 = [pcd_second[i] for i in sorted_indices] # print("pcd_second2 len", len(pcd_second2)) for idx, pcd in enumerate(pcd_second2): ply_file_name = dict_name[pcd] obj_name = ply_file_name.split("=")[0]+".obj" # print("pcd_second2", obj_name) T_trans1 = np.eye(4) points = np.asarray(pcd.points) min_x = np.min(points[:, 0]) max_y = np.max(points[:, 1]) # 当前最大y值 tx = edge_x_min - min_x ty = -max_y - 0.001 T_transTemp = move_to_top_left(pcd, edge_x_min+2, edge_y_max-2) T_trans1 = T_transTemp @ T_trans1 name = dict_name[pcd] # print("pcd_second2",name,"tx",tx,"ty",ty) succ_move = True y_accum = 0 finish_move2 = False while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+1 if bbox['y_min'] <= edge_y_min + collision_threshold_init: print("succ_move False",name,bbox['y_min'],edge_y_min + collision_threshold_init) succ_move = False finish_move2 = True if (finish_move2): break else: if not check_collision_all(pcd, last_pcd_processed,collision_threshold_init): print("succ_move1",name,bbox['x_max'],bbox['y_max'],len(last_pcd_processed)) break pcd.translate([0, -y_step, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step, 0] T_trans1 = T_transTemp @ T_trans1 y_accum += y_step if succ_move: #print("succ_move2", name) """ while True: bbox = get_axis_aligned_bbox(pcd) if bbox['x_max'] >= edge_x_max - collision_threshold_big: pcd.translate([-x_step_big, 0, 0]) break if check_collision_all(pcd, last_pcd_processed,collision_threshold_big): pcd.translate([-x_step_big, 0, 0]) break pcd.translate([x_step_big, 0, 0]) while True: bbox = get_axis_aligned_bbox(pcd) if bbox['y_max'] >= edge_y_max - collision_threshold: pcd.translate([0, -y_step, 0]) break if check_collision_all(pcd, last_pcd_processed,collision_threshold+1): #5 pcd.translate([0, -y_step, 0]) break pcd.translate([0, y_step, 0]) while True: bbox = get_axis_aligned_bbox(pcd) if bbox['x_max'] >= edge_x_max - collision_threshold: pcd.translate([-x_step, 0, 0]) break if check_collision_all(pcd, last_pcd_processed,collision_threshold): pcd.translate([-x_step, 0, 0]) break pcd.translate([x_step, 0, 0]) #""" #""" x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+2 #print("Move x_step_big", name, bbox['y_max'], bbox['x_max']) if bbox['x_max'] >= edge_x_max - collision_threshold_init: pcd.translate([-x_accu, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_accu, 0, 0] T_trans1 = T_transTemp @ T_trans1 break if not check_collision_all(pcd, last_pcd_processed,collision_threshold_init): x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+1 if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 #print("Move y_max", name, bbox['y_max'], bbox['x_max']) break if check_collision_all(pcd, last_pcd_processed,collision_threshold_init): pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 #print("Move y_max2", name, bbox['y_max'], bbox['x_max']) break pcd.translate([0, y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 #print("Move y_step_big", name, bbox['y_max'], bbox['x_max']) else: n = 1 x_accu += x_step_big pcd.translate([x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 #""" else: # T_transTemp = move_to_bottom_left(pcd, edge_x_min, edge_y_min) T_transTemp = move_to_bottom_right(pcd, edge_x_max, edge_y_min) T_trans1 = T_transTemp @ T_trans1 print("last place", name) """ while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+10 if bbox['x_max'] >= edge_x_max - collision_threshold_init: print("fail to place",name) break if not check_collision_all(pcd, last_pcd_processed,collision_threshold_init): print("last place2",name) break pcd.translate([x_step_big, 0, 0]) while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+3 if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step_big, 0]) print("last place3",name) break if check_collision_all(pcd, last_pcd_processed,collision_threshold_init): #5 pcd.translate([0, -y_step_big, 0]) print("last place4",name,collision_threshold_init,len(last_pcd_processed)) break pcd.translate([0, y_step_big, 0]) print("last place41",y_step_big) while True: bbox = get_axis_aligned_bbox(pcd) if bbox['y_max'] >= edge_y_max - collision_threshold: pcd.translate([0, -y_step, 0]) print("last place5",name) break if check_collision_all(pcd, last_pcd_processed,collision_threshold+1): #5 pcd.translate([0, -y_step, 0]) print("last place6",name) break pcd.translate([0, y_step, 0]) while True: bbox = get_axis_aligned_bbox(pcd) if bbox['x_max'] >= edge_x_max - collision_threshold: pcd.translate([-x_step, 0, 0]) print("last place7",name) break if check_collision_all(pcd, last_pcd_processed,collision_threshold): pcd.translate([-x_step, 0, 0]) print("last place8",name) break pcd.translate([x_step, 0, 0]) #""" """ can_place_last = False x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+2 if bbox['x_max'] >= edge_x_max - collision_threshold_init: if not can_place_last: print("fail to place",name) dict_unplaced[name]=name else: pcd.translate([-x_accu, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_accu, 0, 0] T_trans1 = T_transTemp @ T_trans1 break if not check_collision_all(pcd, last_pcd_processed,collision_threshold_init): can_place_last = True x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+1 if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, last_pcd_processed,collision_threshold_init): pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([0, y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 #print("Move2 y_step_big", name) else: n = 1 x_accu += x_step_big pcd.translate([x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 #""" #""" can_place_last = False x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+2 if bbox['x_max'] >= edge_x_max - collision_threshold_init: if not can_place_last: print("fail to place",name) dict_unplaced[name]=name else: pcd.translate([-x_accu, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_accu, 0, 0] T_trans1 = T_transTemp @ T_trans1 break if not check_collision_all(pcd, last_pcd_processed,collision_threshold_init): can_place_last = True x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+1 if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 break if check_collision_all(pcd, last_pcd_processed,collision_threshold_init): pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 break pcd.translate([0, y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 #print("Move2 y_step_big", name) else: n = 1 x_accu += x_step_big pcd.translate([x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 #""" """ can_place_last = False x_accu = 0 place_first = True while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+2 if bbox['x_min'] <= edge_x_min + collision_threshold_init: if not can_place_last: print("fail to place",name) dict_unplaced[name]=name else: pcd.translate([+x_accu, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [+x_accu, 0, 0] T_trans1 = T_transTemp @ T_trans1 place_first = True print("place_first True") break can_break = False if not check_collision_all(pcd, last_pcd_processed,collision_threshold_init): can_place_last = True x_accu = 0 while True: bbox = get_axis_aligned_bbox(pcd) collision_threshold_init = collision_threshold+1 if bbox['y_max'] >= edge_y_max - collision_threshold_init: pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 can_break = True break if check_collision_all(pcd, last_pcd_processed,collision_threshold_init): pcd.translate([0, -y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, -y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 if place_first: can_break = True break pcd.translate([0, y_step_big, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [0, y_step_big, 0] T_trans1 = T_transTemp @ T_trans1 #print("Move2 y_step_big", name) else: n = 1 if can_break: break x_accu += x_step_big pcd.translate([-x_step_big, 0, 0]) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [-x_step_big, 0, 0] T_trans1 = T_transTemp @ T_trans1 #""" last_pcd_processed.append(pcd) dict_total_matrix[obj_name]= T_trans1 @ dict_total_matrix[obj_name] def move_to_top_left(pcd, edge_x_min, edge_y_max): points = np.asarray(pcd.points) min_x = np.min(points[:, 0]) max_y = np.max(points[:, 1]) # 当前最大y值 tx = edge_x_min - min_x # ty = -max_y - 0.001 ty = edge_y_max - max_y T_transTemp = np.eye(4) T_transTemp[:3, 3] = [tx, ty, 0] pcd.translate((tx, ty, 0), relative=True) return T_transTemp def move_to_bottom_right(pcd, edge_x_max, edge_y_min): points = np.asarray(pcd.points) max_x = np.min(points[:, 0]) min_y = np.min(points[:, 1]) tx = edge_x_max - max_x ty = edge_y_min - min_y pcd.translate((tx, ty, 0), relative=True) T_transTemp = np.eye(4) T_transTemp[:3, 3] = [tx, ty, 0] return T_transTemp def get_axis_aligned_bbox(pcd): points = np.asarray(pcd.points) return { 'x_min': np.min(points[:,0]), 'x_max': np.max(points[:,0]), 'y_min': np.min(points[:,1]), 'y_max': np.max(points[:,1]), 'z_min': np.min(points[:,2]), 'z_max': np.max(points[:,2]) } import json import re def extract_numbers_from_filename(filename): """ 从文件名中提取893333, 338908, 105043和x后面的数字 """ # 提取前两个下划线前的数字 first_part = re.findall(r'^(\d+)_(\d+)', filename) if first_part: num1, num2 = first_part[0] else: num1, num2 = None, None # 提取P后面的数字 p_number = re.findall(r'P(\d+)', filename) num3 = p_number[0] if p_number else None # 提取x后面的数字 x_number = re.findall(r'x(\d+)', filename) num4 = x_number[0] if x_number else None return [num for num in [num1, num2, num3, num4] if num is not None] import requests def move_obj_to_compact_bounds_json(base_original_obj_dir,dict_mesh_obj,dict_unplaced,dict_bad,bad_dir,full_dir,dict_bounds_fix, dict_total_matrix,batch_id, print_start_time,selected_machine,selected_mode,version): """生成3D打印布局的JSON数据并保存为3DPrintLayout.json""" # 创建符合3DPrintLayout规范的JSON数据结构 layout_data = { "summary": { "version": version, "homo_matrix": "Homogeneous Matrix", "precision": 6, "selected_machine": selected_machine, "selected_mode": selected_mode }, "models": [] } send_layout_data={ "data": [], "pre_complate_time": 0.0, "pre_batch_id": batch_id, "type_setting_start_time": print_start_time } print(f"need_upload_result={need_upload_result()}") is_need_upload_result = need_upload_result() obj_file_list = list(dict_mesh_obj.keys()) ply_path_dict = {} original_obj_pid_dir = base_original_obj_dir # 构建PLY文件路径映射 for ply_file_name in dict_bounds_fix: ply_dict_key = ply_file_name.split("=")[0] ply_path_dict[ply_dict_key] = ply_file_name for obj_name in obj_file_list: ply_name_pid = obj_name.replace(".obj", "") ply_name = ply_path_dict.get(ply_name_pid, None) if is_need_upload_result: result = extract_numbers_from_filename(ply_name) if not ply_name or ply_name in dict_unplaced: if is_need_upload_result: print_id = result[2] order_id = result[0] status = 0 pid = result[1] counts = result[3] send_layout_data["data"].append({ "print_id": print_id, "order_id": order_id, "status": status, "pid":pid, "counts":counts}) continue # 跳过未放置的模型 total_matrix = dict_total_matrix[obj_name] flattened = total_matrix.flatten()[:16] matrix_4x4 = [ [round(flattened[i], 6) for i in range(0, 4)], # 第1行 [round(flattened[i], 6) for i in range(4, 8)], # 第2行 [round(flattened[i], 6) for i in range(8, 12)], # 第3行 [round(flattened[i], 6) for i in range(12, 16)] # 第4行 ] layout_data["models"].append({ "file_name": obj_name, "transform": { "homo_matrix": matrix_4x4 } }) if is_need_upload_result: print_id = result[2] order_id = result[0] status = 1 pid = result[1] counts = result[3] send_layout_data["data"].append({ "print_id": print_id, "order_id": order_id, "status": status, "pid":pid, "counts":counts}) # 保存JSON文件 # json_path = os.path.join(base_original_obj_dir, "3DPrintLayout.json") json_path = os.path.join(base_original_obj_dir, f"{batch_id}.json") import re json_str = json.dumps(layout_data, ensure_ascii=False, indent=2) json_str = re.sub( r'\[\s*(-?[\d.]+),\s+(-?[\d.]+),\s+(-?[\d.]+),\s+(-?[\d.]+)\s*\]', r'[\1,\2,\3,\4]', json_str ) with open(json_path, "w", encoding='utf-8') as f: f.write(json_str) print(f"3D打印布局已保存至: {json_path}") print(f"排版错误模型数量::{len(dict_bad)}") for obj_name in dict_bad: print("--错误模型名:", obj_name) process_obj_files(original_obj_pid_dir,bad_dir,obj_name) print(f"排版剩余模型数量::{len(dict_unplaced)}") for ply_file_name in dict_unplaced: obj_name = ply_file_name.split("=")[0]+".obj" print("--剩余模型名:", obj_name) process_obj_files(original_obj_pid_dir,full_dir,obj_name) return send_layout_data def process_obj_files(original_obj_pid_dir,placed_remove_dir,obj_name): """ 处理OBJ文件及其相关资源文件的复制、更新和清理 参数: original_obj_pid_dir: 包含原始OBJ文件的目录 placed_remove_dir: 用于存放移除文件的目录 obj_name: 要处理的OBJ文件名 """ base_origin_obj_path = os.path.join(original_obj_pid_dir,obj_name) # 从obj_name中提取PID(产品ID) obj_pid = obj_name.split("_P")[0] # 查找相关的MTL和纹理文件 mtl_name = None tex_name = None for file_name in os.listdir(original_obj_pid_dir): if file_name.endswith(".mtl") and obj_pid in file_name: mtl_name = file_name if (file_name.endswith(".jpg") or file_name.endswith(".png")) and obj_pid in file_name: tex_name = file_name # 将原始OBJ文件移动到移除目录 placed_remove_obj_path = os.path.join(placed_remove_dir, obj_name) shutil.copy(base_origin_obj_path, placed_remove_obj_path) os.remove(base_origin_obj_path) # 检查目录中是否还有其他OBJ文件 exist_obj_any = False exist_obj = False for file_name in os.listdir(original_obj_pid_dir): if file_name.endswith(".obj"): exist_obj_any = True if obj_pid in file_name: exist_obj = True # 确定是否需要删除MTL和纹理文件 delete_mtl = not exist_obj_any or not exist_obj # 如果确定要删除,移动MTL和纹理文件 if delete_mtl: if mtl_name: base_origin_mtl_path = os.path.join(original_obj_pid_dir, mtl_name) placed_remove_mtl_path = os.path.join(placed_remove_dir, mtl_name) shutil.copy(base_origin_mtl_path, placed_remove_mtl_path) os.remove(base_origin_mtl_path) if tex_name: base_origin_tex_path = os.path.join(original_obj_pid_dir, tex_name) placed_remove_tex_path = os.path.join(placed_remove_dir, tex_name) shutil.copy(base_origin_tex_path, placed_remove_tex_path) os.remove(base_origin_tex_path) def pass_for_min_dis(placed_models, dict_unplaced, dict_bounds_fix): pcd_all = [] name_list = [] model_list = [] for model in placed_models: # pcd = o3d.io.read_point_cloud(ply_origin_path) pcd = dict_bounds_fix[model['name']] pcd_all.append(pcd) name_list.append(model['name']) model_list.append(model) for idx, pcd in enumerate(pcd_all): y = model_list[idx]['position'][1] dx = model_list[idx]['dimensions'][0] dy = model_list[idx]['dimensions'][1] # print("pass_for_min_dis", name_list[idx], y, dy) delta_y = 20 # safe_y = y - delta_y safe_y = y - dy - delta_y min_y = 0 if safe_y < min_y: name = name_list[idx] print("fail to place (x=0)", name_list[idx], y, dy) dict_unplaced[name]=name