You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

1204 lines
46 KiB

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 extend_dist_min_collision
from compute_print_net import arrange_models_on_platform
from compute_print_net import compute_bbox_all
from compute_print_net_out import read_mesh
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']
# print(f"get_dict_pcd {ply_name.split("=")[0]}.obj, {ply_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<edge_x_max-x_top_delta) :
if draw_down:
pcd.translate([-dist_x, -dist_y, 0])
T_transTemp = np.eye(4)
T_transTemp[:3, 3] = [-dist_x, -dist_y, 0]
T_trans1 = T_transTemp @ T_trans1
# print("draw_down",obj_name)
# pcd_processed_curr = pcd_processed_x_top
pcd_processed_curr = pcd_processed
else:
is_x_top = True
if check_collision_all(pcd, pcd_processed_no_x_top, extend_dist_min_collision):
name = ply_file_name
print("fail to place (x=0)", ply_file_name, dx)
dict_unplaced[name]=name
pcd_processed_curr = pcd_processed
x_step_big = x_step * 2
y_step_big = y_step * 2
collision_threshold_big = collision_threshold + 10
y_init_big = 5
if dx > 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